add
This commit is contained in:
37
README.md
37
README.md
@@ -0,0 +1,37 @@
|
||||
# Nursing Home Management
|
||||
|
||||
This repository contains a Spring Boot backend and a Vue 2 + ElementUI frontend for a nursing home management system.
|
||||
|
||||
## Tech Stack
|
||||
- Backend: Spring Boot 3, Sa-Token, MyBatis, MySQL
|
||||
- Frontend: Vue 2, Axios, ElementUI
|
||||
|
||||
## Setup
|
||||
|
||||
### 1) Database
|
||||
1. Create a MySQL database and import the schema:
|
||||
- File: backend/sql/schema.sql
|
||||
2. Update database connection in backend/src/main/resources/application.yml.
|
||||
|
||||
Default admin account:
|
||||
- username: admin
|
||||
- password: admin123
|
||||
|
||||
### 2) Backend
|
||||
```bash
|
||||
cd backend
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
### 3) Frontend
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
npm run serve
|
||||
```
|
||||
|
||||
Frontend will run at http://localhost:5173 and proxy to the backend at http://localhost:8080.
|
||||
|
||||
## Notes
|
||||
- Family users can register via the Register page (needs matching elder ID card in the database).
|
||||
- File uploads are stored in the backend `uploads` folder and served at `/files/*`.
|
||||
|
||||
74
backend/pom.xml
Normal file
74
backend/pom.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.nursinghome</groupId>
|
||||
<artifactId>nursing-home-backend</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>nursing-home-backend</name>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<spring.boot.version>3.2.5</spring.boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||
<version>1.37.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</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>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
143
backend/sql/schema.sql
Normal file
143
backend/sql/schema.sql
Normal file
@@ -0,0 +1,143 @@
|
||||
CREATE DATABASE IF NOT EXISTS nursing_home DEFAULT CHARACTER SET utf8mb4;
|
||||
USE nursing_home;
|
||||
|
||||
DROP TABLE IF EXISTS payment_record;
|
||||
DROP TABLE IF EXISTS bill;
|
||||
DROP TABLE IF EXISTS feedback;
|
||||
DROP TABLE IF EXISTS notice;
|
||||
DROP TABLE IF EXISTS handover;
|
||||
DROP TABLE IF EXISTS health_record;
|
||||
DROP TABLE IF EXISTS care_record;
|
||||
DROP TABLE IF EXISTS schedule;
|
||||
DROP TABLE IF EXISTS family_elder;
|
||||
DROP TABLE IF EXISTS elder;
|
||||
DROP TABLE IF EXISTS sys_user;
|
||||
|
||||
CREATE TABLE sys_user (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
password VARCHAR(100) NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
phone VARCHAR(30),
|
||||
role VARCHAR(20) NOT NULL,
|
||||
status TINYINT NOT NULL DEFAULT 1,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE elder (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
gender VARCHAR(10),
|
||||
id_card VARCHAR(30) NOT NULL UNIQUE,
|
||||
birthday DATE,
|
||||
room_no VARCHAR(20),
|
||||
check_in_date DATE,
|
||||
care_level VARCHAR(20),
|
||||
status VARCHAR(20),
|
||||
remark VARCHAR(200)
|
||||
);
|
||||
|
||||
CREATE TABLE family_elder (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
family_id BIGINT NOT NULL,
|
||||
elder_id BIGINT NOT NULL,
|
||||
relationship VARCHAR(20),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_family (family_id),
|
||||
INDEX idx_elder (elder_id)
|
||||
);
|
||||
|
||||
CREATE TABLE schedule (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
nurse_id BIGINT NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
shift VARCHAR(20),
|
||||
task VARCHAR(200),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_nurse_date (nurse_id, date)
|
||||
);
|
||||
|
||||
CREATE TABLE care_record (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
elder_id BIGINT NOT NULL,
|
||||
nurse_id BIGINT NOT NULL,
|
||||
content VARCHAR(500),
|
||||
attachment_url VARCHAR(200),
|
||||
record_time DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_elder (elder_id)
|
||||
);
|
||||
|
||||
CREATE TABLE health_record (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
elder_id BIGINT NOT NULL,
|
||||
nurse_id BIGINT NOT NULL,
|
||||
temperature DECIMAL(4,1),
|
||||
bp_systolic INT,
|
||||
bp_diastolic INT,
|
||||
heart_rate INT,
|
||||
note VARCHAR(200),
|
||||
record_time DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_elder (elder_id)
|
||||
);
|
||||
|
||||
CREATE TABLE handover (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
nurse_id BIGINT NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
content VARCHAR(500),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_nurse (nurse_id)
|
||||
);
|
||||
|
||||
CREATE TABLE notice (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
title VARCHAR(100) NOT NULL,
|
||||
content VARCHAR(1000),
|
||||
target_role VARCHAR(20),
|
||||
target_user_id BIGINT,
|
||||
created_by BIGINT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE feedback (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
family_id BIGINT NOT NULL,
|
||||
elder_id BIGINT NOT NULL,
|
||||
type VARCHAR(20),
|
||||
content VARCHAR(500),
|
||||
rating INT,
|
||||
status VARCHAR(20),
|
||||
reply VARCHAR(500),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE bill (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
elder_id BIGINT NOT NULL,
|
||||
month VARCHAR(7) NOT NULL,
|
||||
bed_fee DECIMAL(10,2),
|
||||
care_fee DECIMAL(10,2),
|
||||
meal_fee DECIMAL(10,2),
|
||||
other_fee DECIMAL(10,2),
|
||||
total DECIMAL(10,2),
|
||||
status VARCHAR(20),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
paid_at DATETIME
|
||||
);
|
||||
|
||||
CREATE TABLE payment_record (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
bill_id BIGINT NOT NULL,
|
||||
family_id BIGINT NOT NULL,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
method VARCHAR(20),
|
||||
paid_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO sys_user(username, password, name, phone, role, status)
|
||||
VALUES ('admin', 'admin123', 'Administrator', '13800000000', 'ADMIN', 1);
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.nursinghome;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.nursinghome.mapper")
|
||||
public class NursingHomeApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(NursingHomeApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.nursinghome.common;
|
||||
|
||||
public class ApiException extends RuntimeException {
|
||||
public ApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.nursinghome.common;
|
||||
|
||||
public class ApiResponse<T> {
|
||||
private int code;
|
||||
private String message;
|
||||
private T data;
|
||||
|
||||
public ApiResponse() {}
|
||||
|
||||
public ApiResponse(int code, String message, T data) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
return new ApiResponse<>(0, "ok", data);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> success() {
|
||||
return new ApiResponse<>(0, "ok", null);
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> error(String message) {
|
||||
return new ApiResponse<>(1, message, null);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.nursinghome.common;
|
||||
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(ApiException.class)
|
||||
public ApiResponse<Void> handleApi(ApiException ex) {
|
||||
return ApiResponse.error(ex.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ApiResponse<Void> handleValid(MethodArgumentNotValidException ex) {
|
||||
String msg = ex.getBindingResult().getAllErrors().isEmpty()
|
||||
? "validation error" : ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
|
||||
return ApiResponse.error(msg);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ApiResponse<Void> handle(Exception ex) {
|
||||
return ApiResponse.error(ex.getMessage());
|
||||
}
|
||||
}
|
||||
17
backend/src/main/java/com/nursinghome/config/CorsConfig.java
Normal file
17
backend/src/main/java/com/nursinghome/config/CorsConfig.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.nursinghome.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.nursinghome.config;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import com.nursinghome.entity.User;
|
||||
import com.nursinghome.mapper.UserMapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class SaTokenStpInterfaceImpl implements StpInterface {
|
||||
private final UserMapper userMapper;
|
||||
|
||||
public SaTokenStpInterfaceImpl(UserMapper userMapper) {
|
||||
this.userMapper = userMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRoleList(Object loginId, String loginType) {
|
||||
User user = userMapper.findById(Long.valueOf(loginId.toString()));
|
||||
if (user == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.singletonList(user.getRole());
|
||||
}
|
||||
}
|
||||
23
backend/src/main/java/com/nursinghome/config/WebConfig.java
Normal file
23
backend/src/main/java/com/nursinghome/config/WebConfig.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.nursinghome.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
@Value("${app.upload-dir}")
|
||||
private String uploadDir;
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
Path uploadPath = Paths.get(uploadDir).toAbsolutePath().normalize();
|
||||
String location = uploadPath.toUri().toString();
|
||||
registry.addResourceHandler("/files/**")
|
||||
.addResourceLocations(location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package com.nursinghome.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.nursinghome.common.ApiException;
|
||||
import com.nursinghome.common.ApiResponse;
|
||||
import com.nursinghome.dto.*;
|
||||
import com.nursinghome.entity.*;
|
||||
import com.nursinghome.mapper.*;
|
||||
import com.nursinghome.service.BillService;
|
||||
import com.nursinghome.service.ElderService;
|
||||
import com.nursinghome.service.UserService;
|
||||
import com.nursinghome.util.PasswordUtil;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/admin")
|
||||
@SaCheckRole("ADMIN")
|
||||
public class AdminController {
|
||||
private final UserService userService;
|
||||
private final ElderService elderService;
|
||||
private final ScheduleMapper scheduleMapper;
|
||||
private final FeedbackMapper feedbackMapper;
|
||||
private final NoticeMapper noticeMapper;
|
||||
private final BillService billService;
|
||||
private final BillMapper billMapper;
|
||||
private final UserMapper userMapper;
|
||||
|
||||
public AdminController(UserService userService,
|
||||
ElderService elderService,
|
||||
ScheduleMapper scheduleMapper,
|
||||
FeedbackMapper feedbackMapper,
|
||||
NoticeMapper noticeMapper,
|
||||
BillService billService,
|
||||
BillMapper billMapper,
|
||||
UserMapper userMapper) {
|
||||
this.userService = userService;
|
||||
this.elderService = elderService;
|
||||
this.scheduleMapper = scheduleMapper;
|
||||
this.feedbackMapper = feedbackMapper;
|
||||
this.noticeMapper = noticeMapper;
|
||||
this.billService = billService;
|
||||
this.billMapper = billMapper;
|
||||
this.userMapper = userMapper;
|
||||
}
|
||||
|
||||
@GetMapping("/stats")
|
||||
public ApiResponse<Map<String, Object>> stats() {
|
||||
int elders = elderService.listAll().size();
|
||||
int nurses = userService.listByRole("NURSE").size();
|
||||
int families = userService.listByRole("FAMILY").size();
|
||||
BigDecimal income = BigDecimal.ZERO;
|
||||
for (Bill bill : billMapper.listAll()) {
|
||||
if ("PAID".equals(bill.getStatus()) && bill.getTotal() != null) {
|
||||
income = income.add(bill.getTotal());
|
||||
}
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("elders", elders);
|
||||
data.put("nurses", nurses);
|
||||
data.put("families", families);
|
||||
data.put("income", income);
|
||||
return ApiResponse.success(data);
|
||||
}
|
||||
|
||||
@GetMapping("/users")
|
||||
public ApiResponse<List<User>> listUsers(@RequestParam(required = false) String role) {
|
||||
List<User> users = userService.listByRole(role);
|
||||
for (User user : users) {
|
||||
user.setPassword(null);
|
||||
}
|
||||
return ApiResponse.success(users);
|
||||
}
|
||||
|
||||
@PostMapping("/users")
|
||||
public ApiResponse<User> createUser(@RequestBody UserRequest request) {
|
||||
User user = new User();
|
||||
user.setUsername(request.getUsername());
|
||||
user.setPassword(request.getPassword());
|
||||
user.setName(request.getName());
|
||||
user.setPhone(request.getPhone());
|
||||
user.setRole(request.getRole());
|
||||
user.setStatus(request.getStatus() == null ? 1 : request.getStatus());
|
||||
userService.createUser(user, true);
|
||||
user.setPassword(null);
|
||||
return ApiResponse.success(user);
|
||||
}
|
||||
|
||||
@PutMapping("/users")
|
||||
public ApiResponse<Void> updateUser(@RequestBody UserUpdateRequest request) {
|
||||
User user = new User();
|
||||
user.setId(request.getId());
|
||||
user.setName(request.getName());
|
||||
user.setPhone(request.getPhone());
|
||||
user.setStatus(request.getStatus());
|
||||
userService.updateUser(user);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PutMapping("/users/{id}/status")
|
||||
public ApiResponse<Void> updateUserStatus(@PathVariable Long id, @RequestParam Integer status) {
|
||||
userService.updateStatus(id, status);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/users/{id}/reset-password")
|
||||
public ApiResponse<Void> resetPassword(@PathVariable Long id, @RequestParam String password) {
|
||||
userService.updatePassword(id, PasswordUtil.hash(password));
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@GetMapping("/elders")
|
||||
public ApiResponse<List<Elder>> listElders() {
|
||||
return ApiResponse.success(elderService.listAll());
|
||||
}
|
||||
|
||||
@PostMapping("/elders")
|
||||
public ApiResponse<Elder> createElder(@RequestBody ElderRequest request) {
|
||||
Elder elder = new Elder();
|
||||
elder.setName(request.getName());
|
||||
elder.setGender(request.getGender());
|
||||
elder.setIdCard(request.getIdCard());
|
||||
elder.setBirthday(request.getBirthday());
|
||||
elder.setRoomNo(request.getRoomNo());
|
||||
elder.setCheckInDate(request.getCheckInDate());
|
||||
elder.setCareLevel(request.getCareLevel());
|
||||
elder.setStatus(request.getStatus());
|
||||
elder.setRemark(request.getRemark());
|
||||
return ApiResponse.success(elderService.create(elder));
|
||||
}
|
||||
|
||||
@PutMapping("/elders")
|
||||
public ApiResponse<Void> updateElder(@RequestBody ElderRequest request) {
|
||||
Elder elder = new Elder();
|
||||
elder.setId(request.getId());
|
||||
elder.setName(request.getName());
|
||||
elder.setGender(request.getGender());
|
||||
elder.setIdCard(request.getIdCard());
|
||||
elder.setBirthday(request.getBirthday());
|
||||
elder.setRoomNo(request.getRoomNo());
|
||||
elder.setCheckInDate(request.getCheckInDate());
|
||||
elder.setCareLevel(request.getCareLevel());
|
||||
elder.setStatus(request.getStatus());
|
||||
elder.setRemark(request.getRemark());
|
||||
elderService.update(elder);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/elders/{id}")
|
||||
public ApiResponse<Void> deleteElder(@PathVariable Long id) {
|
||||
elderService.delete(id);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@GetMapping("/schedules")
|
||||
public ApiResponse<List<Schedule>> listSchedules(@RequestParam String date) {
|
||||
return ApiResponse.success(scheduleMapper.listByDate(java.time.LocalDate.parse(date)));
|
||||
}
|
||||
|
||||
@PostMapping("/schedules")
|
||||
public ApiResponse<Schedule> createSchedule(@RequestBody ScheduleRequest request) {
|
||||
Schedule schedule = new Schedule();
|
||||
schedule.setNurseId(request.getNurseId());
|
||||
schedule.setDate(request.getDate());
|
||||
schedule.setShift(request.getShift());
|
||||
schedule.setTask(request.getTask());
|
||||
scheduleMapper.insert(schedule);
|
||||
return ApiResponse.success(schedule);
|
||||
}
|
||||
|
||||
@PutMapping("/schedules")
|
||||
public ApiResponse<Void> updateSchedule(@RequestBody ScheduleRequest request) {
|
||||
Schedule schedule = new Schedule();
|
||||
schedule.setId(request.getId());
|
||||
schedule.setNurseId(request.getNurseId());
|
||||
schedule.setDate(request.getDate());
|
||||
schedule.setShift(request.getShift());
|
||||
schedule.setTask(request.getTask());
|
||||
scheduleMapper.update(schedule);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@DeleteMapping("/schedules/{id}")
|
||||
public ApiResponse<Void> deleteSchedule(@PathVariable Long id) {
|
||||
scheduleMapper.delete(id);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/bills")
|
||||
public ApiResponse<Bill> createBill(@RequestBody BillRequest request) {
|
||||
Bill bill = new Bill();
|
||||
bill.setElderId(request.getElderId());
|
||||
bill.setMonth(request.getMonth());
|
||||
bill.setBedFee(request.getBedFee());
|
||||
bill.setCareFee(request.getCareFee());
|
||||
bill.setMealFee(request.getMealFee());
|
||||
bill.setOtherFee(request.getOtherFee());
|
||||
return ApiResponse.success(billService.createBill(bill));
|
||||
}
|
||||
|
||||
@GetMapping("/bills")
|
||||
public ApiResponse<List<Bill>> listBills(@RequestParam(required = false) Long elderId) {
|
||||
if (elderId == null) {
|
||||
return ApiResponse.success(billMapper.listAll());
|
||||
}
|
||||
return ApiResponse.success(billService.listByElderId(elderId));
|
||||
}
|
||||
|
||||
@GetMapping("/feedback")
|
||||
public ApiResponse<List<Feedback>> listFeedback() {
|
||||
return ApiResponse.success(feedbackMapper.listAll());
|
||||
}
|
||||
|
||||
@PutMapping("/feedback")
|
||||
public ApiResponse<Void> updateFeedback(@RequestBody FeedbackUpdateRequest request) {
|
||||
Feedback feedback = new Feedback();
|
||||
feedback.setId(request.getId());
|
||||
feedback.setStatus(request.getStatus());
|
||||
feedback.setReply(request.getReply());
|
||||
feedbackMapper.updateStatus(feedback);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/notices")
|
||||
public ApiResponse<Notice> createNotice(@RequestBody NoticeRequest request) {
|
||||
Notice notice = new Notice();
|
||||
notice.setTitle(request.getTitle());
|
||||
notice.setContent(request.getContent());
|
||||
notice.setTargetRole(request.getTargetRole());
|
||||
notice.setTargetUserId(request.getTargetUserId());
|
||||
notice.setCreatedBy(Long.valueOf(StpUtil.getLoginId().toString()));
|
||||
noticeMapper.insert(notice);
|
||||
return ApiResponse.success(notice);
|
||||
}
|
||||
|
||||
@GetMapping("/notices")
|
||||
public ApiResponse<List<Notice>> listNotices(@RequestParam(required = false) String role,
|
||||
@RequestParam(required = false) Long userId) {
|
||||
return ApiResponse.success(noticeMapper.listByTarget(role, userId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.nursinghome.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.nursinghome.common.ApiException;
|
||||
import com.nursinghome.common.ApiResponse;
|
||||
import com.nursinghome.dto.LoginRequest;
|
||||
import com.nursinghome.dto.RegisterRequest;
|
||||
import com.nursinghome.entity.User;
|
||||
import com.nursinghome.service.FamilyService;
|
||||
import com.nursinghome.service.UserService;
|
||||
import com.nursinghome.util.PasswordUtil;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
private final UserService userService;
|
||||
private final FamilyService familyService;
|
||||
|
||||
public AuthController(UserService userService, FamilyService familyService) {
|
||||
this.userService = userService;
|
||||
this.familyService = familyService;
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public ApiResponse<Map<String, Object>> login(@Valid @RequestBody LoginRequest request) {
|
||||
User user = userService.findByUsername(request.getUsername());
|
||||
if (user == null) {
|
||||
throw new ApiException("user not found");
|
||||
}
|
||||
if (user.getStatus() != null && user.getStatus() == 0) {
|
||||
throw new ApiException("user disabled");
|
||||
}
|
||||
if (!PasswordUtil.matches(request.getPassword(), user.getPassword())) {
|
||||
throw new ApiException("invalid password");
|
||||
}
|
||||
if (!PasswordUtil.isBcrypt(user.getPassword())) {
|
||||
userService.updatePassword(user.getId(), PasswordUtil.hash(request.getPassword()));
|
||||
}
|
||||
StpUtil.login(user.getId());
|
||||
|
||||
user.setPassword(null);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("token", StpUtil.getTokenValue());
|
||||
data.put("role", user.getRole());
|
||||
data.put("user", user);
|
||||
return ApiResponse.success(data);
|
||||
}
|
||||
|
||||
@PostMapping("/register")
|
||||
public ApiResponse<User> register(@Valid @RequestBody RegisterRequest request) {
|
||||
User user = familyService.registerFamily(
|
||||
request.getUsername(),
|
||||
request.getPassword(),
|
||||
request.getName(),
|
||||
request.getPhone(),
|
||||
request.getElderIdCard(),
|
||||
request.getRelationship()
|
||||
);
|
||||
return ApiResponse.success(user);
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public ApiResponse<Void> logout() {
|
||||
StpUtil.logout();
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@GetMapping("/me")
|
||||
@SaCheckLogin
|
||||
public ApiResponse<User> me() {
|
||||
Long userId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
User user = userService.findById(userId);
|
||||
if (user != null) {
|
||||
user.setPassword(null);
|
||||
}
|
||||
return ApiResponse.success(user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.nursinghome.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.nursinghome.common.ApiException;
|
||||
import com.nursinghome.common.ApiResponse;
|
||||
import com.nursinghome.dto.FeedbackRequest;
|
||||
import com.nursinghome.dto.PayRequest;
|
||||
import com.nursinghome.entity.*;
|
||||
import com.nursinghome.mapper.*;
|
||||
import com.nursinghome.service.BillService;
|
||||
import com.nursinghome.service.FamilyService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/family")
|
||||
@SaCheckRole("FAMILY")
|
||||
public class FamilyController {
|
||||
private final FamilyService familyService;
|
||||
private final ElderMapper elderMapper;
|
||||
private final CareRecordMapper careRecordMapper;
|
||||
private final HealthRecordMapper healthRecordMapper;
|
||||
private final BillService billService;
|
||||
private final BillMapper billMapper;
|
||||
private final NoticeMapper noticeMapper;
|
||||
private final FeedbackMapper feedbackMapper;
|
||||
|
||||
public FamilyController(FamilyService familyService,
|
||||
ElderMapper elderMapper,
|
||||
CareRecordMapper careRecordMapper,
|
||||
HealthRecordMapper healthRecordMapper,
|
||||
BillService billService,
|
||||
BillMapper billMapper,
|
||||
NoticeMapper noticeMapper,
|
||||
FeedbackMapper feedbackMapper) {
|
||||
this.familyService = familyService;
|
||||
this.elderMapper = elderMapper;
|
||||
this.careRecordMapper = careRecordMapper;
|
||||
this.healthRecordMapper = healthRecordMapper;
|
||||
this.billService = billService;
|
||||
this.billMapper = billMapper;
|
||||
this.noticeMapper = noticeMapper;
|
||||
this.feedbackMapper = feedbackMapper;
|
||||
}
|
||||
|
||||
@GetMapping("/elders")
|
||||
public ApiResponse<List<Elder>> listElders() {
|
||||
Long familyId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
List<FamilyElder> relations = familyService.listRelations(familyId);
|
||||
List<Elder> elders = new ArrayList<>();
|
||||
for (FamilyElder relation : relations) {
|
||||
Elder elder = elderMapper.findById(relation.getElderId());
|
||||
if (elder != null) {
|
||||
elders.add(elder);
|
||||
}
|
||||
}
|
||||
return ApiResponse.success(elders);
|
||||
}
|
||||
|
||||
@GetMapping("/care-records")
|
||||
public ApiResponse<List<CareRecord>> listCare(@RequestParam Long elderId) {
|
||||
Long familyId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
boolean allowed = false;
|
||||
for (FamilyElder relation : familyService.listRelations(familyId)) {
|
||||
if (relation.getElderId().equals(elderId)) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allowed) {
|
||||
throw new ApiException("no access to this elder");
|
||||
}
|
||||
return ApiResponse.success(careRecordMapper.listByElderId(elderId));
|
||||
}
|
||||
|
||||
@GetMapping("/health-records")
|
||||
public ApiResponse<List<HealthRecord>> listHealth(@RequestParam Long elderId) {
|
||||
Long familyId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
boolean allowed = false;
|
||||
for (FamilyElder relation : familyService.listRelations(familyId)) {
|
||||
if (relation.getElderId().equals(elderId)) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allowed) {
|
||||
throw new ApiException("no access to this elder");
|
||||
}
|
||||
return ApiResponse.success(healthRecordMapper.listByElderId(elderId));
|
||||
}
|
||||
|
||||
@GetMapping("/bills")
|
||||
public ApiResponse<List<Bill>> listBills(@RequestParam Long elderId) {
|
||||
Long familyId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
boolean allowed = false;
|
||||
for (FamilyElder relation : familyService.listRelations(familyId)) {
|
||||
if (relation.getElderId().equals(elderId)) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allowed) {
|
||||
throw new ApiException("no access to this elder");
|
||||
}
|
||||
return ApiResponse.success(billService.listByElderId(elderId));
|
||||
}
|
||||
|
||||
@PostMapping("/bills/{id}/pay")
|
||||
public ApiResponse<Void> pay(@PathVariable Long id, @RequestBody PayRequest request) {
|
||||
Long familyId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
Bill bill = billMapper.findById(id);
|
||||
if (bill == null) {
|
||||
throw new ApiException("bill not found");
|
||||
}
|
||||
boolean allowed = false;
|
||||
for (FamilyElder relation : familyService.listRelations(familyId)) {
|
||||
if (relation.getElderId().equals(bill.getElderId())) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allowed) {
|
||||
throw new ApiException("no access to this bill");
|
||||
}
|
||||
if ("PAID".equals(bill.getStatus())) {
|
||||
throw new ApiException("bill already paid");
|
||||
}
|
||||
BigDecimal amount = bill.getTotal() == null ? BigDecimal.ZERO : bill.getTotal();
|
||||
billService.payBill(id, familyId, request.getMethod(), amount);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/feedback")
|
||||
public ApiResponse<Feedback> createFeedback(@RequestBody FeedbackRequest request) {
|
||||
Feedback feedback = new Feedback();
|
||||
feedback.setFamilyId(Long.valueOf(StpUtil.getLoginId().toString()));
|
||||
feedback.setElderId(request.getElderId());
|
||||
feedback.setType(request.getType());
|
||||
feedback.setContent(request.getContent());
|
||||
feedback.setRating(request.getRating());
|
||||
feedback.setStatus("NEW");
|
||||
feedback.setCreatedAt(LocalDateTime.now());
|
||||
feedbackMapper.insert(feedback);
|
||||
return ApiResponse.success(feedback);
|
||||
}
|
||||
|
||||
@GetMapping("/notices")
|
||||
public ApiResponse<List<Notice>> listNotices() {
|
||||
Long familyId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
return ApiResponse.success(noticeMapper.listByTarget("FAMILY", familyId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.nursinghome.controller;
|
||||
|
||||
import com.nursinghome.common.ApiResponse;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/files")
|
||||
public class FileController {
|
||||
@Value("${app.upload-dir}")
|
||||
private String uploadDir;
|
||||
|
||||
@PostMapping("/upload")
|
||||
public ApiResponse<Map<String, Object>> upload(@RequestParam("file") MultipartFile file) throws IOException {
|
||||
if (file.isEmpty()) {
|
||||
return ApiResponse.error("empty file");
|
||||
}
|
||||
String original = file.getOriginalFilename();
|
||||
String ext = StringUtils.getFilenameExtension(original);
|
||||
String name = UUID.randomUUID().toString().replace("-", "");
|
||||
if (ext != null && !ext.isEmpty()) {
|
||||
name = name + "." + ext;
|
||||
}
|
||||
File dir = new File(uploadDir);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
File dest = new File(dir, name);
|
||||
file.transferTo(dest);
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("url", "/files/" + name);
|
||||
return ApiResponse.success(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.nursinghome.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckRole;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.nursinghome.common.ApiResponse;
|
||||
import com.nursinghome.dto.CareRecordRequest;
|
||||
import com.nursinghome.dto.HandoverRequest;
|
||||
import com.nursinghome.dto.HealthRecordRequest;
|
||||
import com.nursinghome.entity.*;
|
||||
import com.nursinghome.mapper.*;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/nurse")
|
||||
@SaCheckRole("NURSE")
|
||||
public class NurseController {
|
||||
private final ScheduleMapper scheduleMapper;
|
||||
private final CareRecordMapper careRecordMapper;
|
||||
private final HealthRecordMapper healthRecordMapper;
|
||||
private final HandoverMapper handoverMapper;
|
||||
private final NoticeMapper noticeMapper;
|
||||
|
||||
public NurseController(ScheduleMapper scheduleMapper,
|
||||
CareRecordMapper careRecordMapper,
|
||||
HealthRecordMapper healthRecordMapper,
|
||||
HandoverMapper handoverMapper,
|
||||
NoticeMapper noticeMapper) {
|
||||
this.scheduleMapper = scheduleMapper;
|
||||
this.careRecordMapper = careRecordMapper;
|
||||
this.healthRecordMapper = healthRecordMapper;
|
||||
this.handoverMapper = handoverMapper;
|
||||
this.noticeMapper = noticeMapper;
|
||||
}
|
||||
|
||||
@GetMapping("/schedules")
|
||||
public ApiResponse<List<Schedule>> listSchedules(@RequestParam String date) {
|
||||
Long nurseId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
return ApiResponse.success(scheduleMapper.listByNurseAndDate(nurseId, java.time.LocalDate.parse(date)));
|
||||
}
|
||||
|
||||
@PostMapping("/care-records")
|
||||
public ApiResponse<CareRecord> createCare(@RequestBody CareRecordRequest request) {
|
||||
CareRecord record = new CareRecord();
|
||||
record.setElderId(request.getElderId());
|
||||
record.setNurseId(Long.valueOf(StpUtil.getLoginId().toString()));
|
||||
record.setContent(request.getContent());
|
||||
record.setAttachmentUrl(request.getAttachmentUrl());
|
||||
record.setRecordTime(request.getRecordTime() == null ? java.time.LocalDateTime.now() : request.getRecordTime());
|
||||
careRecordMapper.insert(record);
|
||||
return ApiResponse.success(record);
|
||||
}
|
||||
|
||||
@GetMapping("/care-records")
|
||||
public ApiResponse<List<CareRecord>> listCare(@RequestParam Long elderId) {
|
||||
return ApiResponse.success(careRecordMapper.listByElderId(elderId));
|
||||
}
|
||||
|
||||
@PostMapping("/health-records")
|
||||
public ApiResponse<HealthRecord> createHealth(@RequestBody HealthRecordRequest request) {
|
||||
HealthRecord record = new HealthRecord();
|
||||
record.setElderId(request.getElderId());
|
||||
record.setNurseId(Long.valueOf(StpUtil.getLoginId().toString()));
|
||||
record.setTemperature(request.getTemperature());
|
||||
record.setBpSystolic(request.getBpSystolic());
|
||||
record.setBpDiastolic(request.getBpDiastolic());
|
||||
record.setHeartRate(request.getHeartRate());
|
||||
record.setNote(request.getNote());
|
||||
record.setRecordTime(request.getRecordTime() == null ? java.time.LocalDateTime.now() : request.getRecordTime());
|
||||
healthRecordMapper.insert(record);
|
||||
return ApiResponse.success(record);
|
||||
}
|
||||
|
||||
@GetMapping("/health-records")
|
||||
public ApiResponse<List<HealthRecord>> listHealth(@RequestParam Long elderId) {
|
||||
return ApiResponse.success(healthRecordMapper.listByElderId(elderId));
|
||||
}
|
||||
|
||||
@PostMapping("/handovers")
|
||||
public ApiResponse<Handover> createHandover(@RequestBody HandoverRequest request) {
|
||||
Handover handover = new Handover();
|
||||
handover.setNurseId(Long.valueOf(StpUtil.getLoginId().toString()));
|
||||
handover.setDate(request.getDate());
|
||||
handover.setContent(request.getContent());
|
||||
handoverMapper.insert(handover);
|
||||
return ApiResponse.success(handover);
|
||||
}
|
||||
|
||||
@GetMapping("/handovers")
|
||||
public ApiResponse<List<Handover>> listHandovers() {
|
||||
Long nurseId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
return ApiResponse.success(handoverMapper.listByNurseId(nurseId));
|
||||
}
|
||||
|
||||
@GetMapping("/notices")
|
||||
public ApiResponse<List<Notice>> listNotices() {
|
||||
Long nurseId = Long.valueOf(StpUtil.getLoginId().toString());
|
||||
return ApiResponse.success(noticeMapper.listByTarget("NURSE", nurseId));
|
||||
}
|
||||
}
|
||||
60
backend/src/main/java/com/nursinghome/dto/BillRequest.java
Normal file
60
backend/src/main/java/com/nursinghome/dto/BillRequest.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class BillRequest {
|
||||
private Long elderId;
|
||||
private String month;
|
||||
private BigDecimal bedFee;
|
||||
private BigDecimal careFee;
|
||||
private BigDecimal mealFee;
|
||||
private BigDecimal otherFee;
|
||||
|
||||
public Long getElderId() {
|
||||
return elderId;
|
||||
}
|
||||
|
||||
public void setElderId(Long elderId) {
|
||||
this.elderId = elderId;
|
||||
}
|
||||
|
||||
public String getMonth() {
|
||||
return month;
|
||||
}
|
||||
|
||||
public void setMonth(String month) {
|
||||
this.month = month;
|
||||
}
|
||||
|
||||
public BigDecimal getBedFee() {
|
||||
return bedFee;
|
||||
}
|
||||
|
||||
public void setBedFee(BigDecimal bedFee) {
|
||||
this.bedFee = bedFee;
|
||||
}
|
||||
|
||||
public BigDecimal getCareFee() {
|
||||
return careFee;
|
||||
}
|
||||
|
||||
public void setCareFee(BigDecimal careFee) {
|
||||
this.careFee = careFee;
|
||||
}
|
||||
|
||||
public BigDecimal getMealFee() {
|
||||
return mealFee;
|
||||
}
|
||||
|
||||
public void setMealFee(BigDecimal mealFee) {
|
||||
this.mealFee = mealFee;
|
||||
}
|
||||
|
||||
public BigDecimal getOtherFee() {
|
||||
return otherFee;
|
||||
}
|
||||
|
||||
public void setOtherFee(BigDecimal otherFee) {
|
||||
this.otherFee = otherFee;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class CareRecordRequest {
|
||||
private Long elderId;
|
||||
private String content;
|
||||
private String attachmentUrl;
|
||||
private LocalDateTime recordTime;
|
||||
|
||||
public Long getElderId() {
|
||||
return elderId;
|
||||
}
|
||||
|
||||
public void setElderId(Long elderId) {
|
||||
this.elderId = elderId;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getAttachmentUrl() {
|
||||
return attachmentUrl;
|
||||
}
|
||||
|
||||
public void setAttachmentUrl(String attachmentUrl) {
|
||||
this.attachmentUrl = attachmentUrl;
|
||||
}
|
||||
|
||||
public LocalDateTime getRecordTime() {
|
||||
return recordTime;
|
||||
}
|
||||
|
||||
public void setRecordTime(LocalDateTime recordTime) {
|
||||
this.recordTime = recordTime;
|
||||
}
|
||||
}
|
||||
96
backend/src/main/java/com/nursinghome/dto/ElderRequest.java
Normal file
96
backend/src/main/java/com/nursinghome/dto/ElderRequest.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class ElderRequest {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String gender;
|
||||
private String idCard;
|
||||
private LocalDate birthday;
|
||||
private String roomNo;
|
||||
private LocalDate checkInDate;
|
||||
private String careLevel;
|
||||
private String status;
|
||||
private String remark;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(String gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public String getIdCard() {
|
||||
return idCard;
|
||||
}
|
||||
|
||||
public void setIdCard(String idCard) {
|
||||
this.idCard = idCard;
|
||||
}
|
||||
|
||||
public java.time.LocalDate getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
|
||||
public void setBirthday(java.time.LocalDate birthday) {
|
||||
this.birthday = birthday;
|
||||
}
|
||||
|
||||
public String getRoomNo() {
|
||||
return roomNo;
|
||||
}
|
||||
|
||||
public void setRoomNo(String roomNo) {
|
||||
this.roomNo = roomNo;
|
||||
}
|
||||
|
||||
public java.time.LocalDate getCheckInDate() {
|
||||
return checkInDate;
|
||||
}
|
||||
|
||||
public void setCheckInDate(java.time.LocalDate checkInDate) {
|
||||
this.checkInDate = checkInDate;
|
||||
}
|
||||
|
||||
public String getCareLevel() {
|
||||
return careLevel;
|
||||
}
|
||||
|
||||
public void setCareLevel(String careLevel) {
|
||||
this.careLevel = careLevel;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
public class FeedbackRequest {
|
||||
private Long elderId;
|
||||
private String type;
|
||||
private String content;
|
||||
private Integer rating;
|
||||
|
||||
public Long getElderId() {
|
||||
return elderId;
|
||||
}
|
||||
|
||||
public void setElderId(Long elderId) {
|
||||
this.elderId = elderId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Integer getRating() {
|
||||
return rating;
|
||||
}
|
||||
|
||||
public void setRating(Integer rating) {
|
||||
this.rating = rating;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
public class FeedbackUpdateRequest {
|
||||
private Long id;
|
||||
private String status;
|
||||
private String reply;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getReply() {
|
||||
return reply;
|
||||
}
|
||||
|
||||
public void setReply(String reply) {
|
||||
this.reply = reply;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class HandoverRequest {
|
||||
private LocalDate date;
|
||||
private String content;
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class HealthRecordRequest {
|
||||
private Long elderId;
|
||||
private BigDecimal temperature;
|
||||
private Integer bpSystolic;
|
||||
private Integer bpDiastolic;
|
||||
private Integer heartRate;
|
||||
private String note;
|
||||
private LocalDateTime recordTime;
|
||||
|
||||
public Long getElderId() {
|
||||
return elderId;
|
||||
}
|
||||
|
||||
public void setElderId(Long elderId) {
|
||||
this.elderId = elderId;
|
||||
}
|
||||
|
||||
public BigDecimal getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(BigDecimal temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public Integer getBpSystolic() {
|
||||
return bpSystolic;
|
||||
}
|
||||
|
||||
public void setBpSystolic(Integer bpSystolic) {
|
||||
this.bpSystolic = bpSystolic;
|
||||
}
|
||||
|
||||
public Integer getBpDiastolic() {
|
||||
return bpDiastolic;
|
||||
}
|
||||
|
||||
public void setBpDiastolic(Integer bpDiastolic) {
|
||||
this.bpDiastolic = bpDiastolic;
|
||||
}
|
||||
|
||||
public Integer getHeartRate() {
|
||||
return heartRate;
|
||||
}
|
||||
|
||||
public void setHeartRate(Integer heartRate) {
|
||||
this.heartRate = heartRate;
|
||||
}
|
||||
|
||||
public String getNote() {
|
||||
return note;
|
||||
}
|
||||
|
||||
public void setNote(String note) {
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
public LocalDateTime getRecordTime() {
|
||||
return recordTime;
|
||||
}
|
||||
|
||||
public void setRecordTime(LocalDateTime recordTime) {
|
||||
this.recordTime = recordTime;
|
||||
}
|
||||
}
|
||||
26
backend/src/main/java/com/nursinghome/dto/LoginRequest.java
Normal file
26
backend/src/main/java/com/nursinghome/dto/LoginRequest.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class LoginRequest {
|
||||
@NotBlank(message = "username required")
|
||||
private String username;
|
||||
@NotBlank(message = "password required")
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
40
backend/src/main/java/com/nursinghome/dto/NoticeRequest.java
Normal file
40
backend/src/main/java/com/nursinghome/dto/NoticeRequest.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
public class NoticeRequest {
|
||||
private String title;
|
||||
private String content;
|
||||
private String targetRole;
|
||||
private Long targetUserId;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getTargetRole() {
|
||||
return targetRole;
|
||||
}
|
||||
|
||||
public void setTargetRole(String targetRole) {
|
||||
this.targetRole = targetRole;
|
||||
}
|
||||
|
||||
public Long getTargetUserId() {
|
||||
return targetUserId;
|
||||
}
|
||||
|
||||
public void setTargetUserId(Long targetUserId) {
|
||||
this.targetUserId = targetUserId;
|
||||
}
|
||||
}
|
||||
13
backend/src/main/java/com/nursinghome/dto/PayRequest.java
Normal file
13
backend/src/main/java/com/nursinghome/dto/PayRequest.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
public class PayRequest {
|
||||
private String method;
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class RegisterRequest {
|
||||
@NotBlank(message = "username required")
|
||||
private String username;
|
||||
@NotBlank(message = "password required")
|
||||
private String password;
|
||||
@NotBlank(message = "name required")
|
||||
private String name;
|
||||
private String phone;
|
||||
@NotBlank(message = "elderIdCard required")
|
||||
private String elderIdCard;
|
||||
private String relationship;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getElderIdCard() {
|
||||
return elderIdCard;
|
||||
}
|
||||
|
||||
public void setElderIdCard(String elderIdCard) {
|
||||
this.elderIdCard = elderIdCard;
|
||||
}
|
||||
|
||||
public String getRelationship() {
|
||||
return relationship;
|
||||
}
|
||||
|
||||
public void setRelationship(String relationship) {
|
||||
this.relationship = relationship;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class ScheduleRequest {
|
||||
private Long id;
|
||||
private Long nurseId;
|
||||
private LocalDate date;
|
||||
private String shift;
|
||||
private String task;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getNurseId() {
|
||||
return nurseId;
|
||||
}
|
||||
|
||||
public void setNurseId(Long nurseId) {
|
||||
this.nurseId = nurseId;
|
||||
}
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getShift() {
|
||||
return shift;
|
||||
}
|
||||
|
||||
public void setShift(String shift) {
|
||||
this.shift = shift;
|
||||
}
|
||||
|
||||
public String getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
public void setTask(String task) {
|
||||
this.task = task;
|
||||
}
|
||||
}
|
||||
58
backend/src/main/java/com/nursinghome/dto/UserRequest.java
Normal file
58
backend/src/main/java/com/nursinghome/dto/UserRequest.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
public class UserRequest {
|
||||
private String username;
|
||||
private String password;
|
||||
private String name;
|
||||
private String phone;
|
||||
private String role;
|
||||
private Integer status;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public Integer getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Integer status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.nursinghome.dto;
|
||||
|
||||
public class UserUpdateRequest {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String phone;
|
||||
private Integer status;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public Integer getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Integer status) {
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
106
backend/src/main/java/com/nursinghome/entity/Bill.java
Normal file
106
backend/src/main/java/com/nursinghome/entity/Bill.java
Normal file
@@ -0,0 +1,106 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class Bill {
|
||||
private Long id;
|
||||
private Long elderId;
|
||||
private String month;
|
||||
private BigDecimal bedFee;
|
||||
private BigDecimal careFee;
|
||||
private BigDecimal mealFee;
|
||||
private BigDecimal otherFee;
|
||||
private BigDecimal total;
|
||||
private String status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime paidAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getElderId() {
|
||||
return elderId;
|
||||
}
|
||||
|
||||
public void setElderId(Long elderId) {
|
||||
this.elderId = elderId;
|
||||
}
|
||||
|
||||
public String getMonth() {
|
||||
return month;
|
||||
}
|
||||
|
||||
public void setMonth(String month) {
|
||||
this.month = month;
|
||||
}
|
||||
|
||||
public BigDecimal getBedFee() {
|
||||
return bedFee;
|
||||
}
|
||||
|
||||
public void setBedFee(BigDecimal bedFee) {
|
||||
this.bedFee = bedFee;
|
||||
}
|
||||
|
||||
public BigDecimal getCareFee() {
|
||||
return careFee;
|
||||
}
|
||||
|
||||
public void setCareFee(BigDecimal careFee) {
|
||||
this.careFee = careFee;
|
||||
}
|
||||
|
||||
public BigDecimal getMealFee() {
|
||||
return mealFee;
|
||||
}
|
||||
|
||||
public void setMealFee(BigDecimal mealFee) {
|
||||
this.mealFee = mealFee;
|
||||
}
|
||||
|
||||
public BigDecimal getOtherFee() {
|
||||
return otherFee;
|
||||
}
|
||||
|
||||
public void setOtherFee(BigDecimal otherFee) {
|
||||
this.otherFee = otherFee;
|
||||
}
|
||||
|
||||
public BigDecimal getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(BigDecimal total) {
|
||||
this.total = total;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getPaidAt() {
|
||||
return paidAt;
|
||||
}
|
||||
|
||||
public void setPaidAt(LocalDateTime paidAt) {
|
||||
this.paidAt = paidAt;
|
||||
}
|
||||
}
|
||||
69
backend/src/main/java/com/nursinghome/entity/CareRecord.java
Normal file
69
backend/src/main/java/com/nursinghome/entity/CareRecord.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class CareRecord {
|
||||
private Long id;
|
||||
private Long elderId;
|
||||
private Long nurseId;
|
||||
private String content;
|
||||
private String attachmentUrl;
|
||||
private LocalDateTime recordTime;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getElderId() {
|
||||
return elderId;
|
||||
}
|
||||
|
||||
public void setElderId(Long elderId) {
|
||||
this.elderId = elderId;
|
||||
}
|
||||
|
||||
public Long getNurseId() {
|
||||
return nurseId;
|
||||
}
|
||||
|
||||
public void setNurseId(Long nurseId) {
|
||||
this.nurseId = nurseId;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getAttachmentUrl() {
|
||||
return attachmentUrl;
|
||||
}
|
||||
|
||||
public void setAttachmentUrl(String attachmentUrl) {
|
||||
this.attachmentUrl = attachmentUrl;
|
||||
}
|
||||
|
||||
public LocalDateTime getRecordTime() {
|
||||
return recordTime;
|
||||
}
|
||||
|
||||
public void setRecordTime(LocalDateTime recordTime) {
|
||||
this.recordTime = recordTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
96
backend/src/main/java/com/nursinghome/entity/Elder.java
Normal file
96
backend/src/main/java/com/nursinghome/entity/Elder.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class Elder {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String gender;
|
||||
private String idCard;
|
||||
private LocalDate birthday;
|
||||
private String roomNo;
|
||||
private LocalDate checkInDate;
|
||||
private String careLevel;
|
||||
private String status;
|
||||
private String remark;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(String gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public String getIdCard() {
|
||||
return idCard;
|
||||
}
|
||||
|
||||
public void setIdCard(String idCard) {
|
||||
this.idCard = idCard;
|
||||
}
|
||||
|
||||
public LocalDate getBirthday() {
|
||||
return birthday;
|
||||
}
|
||||
|
||||
public void setBirthday(LocalDate birthday) {
|
||||
this.birthday = birthday;
|
||||
}
|
||||
|
||||
public String getRoomNo() {
|
||||
return roomNo;
|
||||
}
|
||||
|
||||
public void setRoomNo(String roomNo) {
|
||||
this.roomNo = roomNo;
|
||||
}
|
||||
|
||||
public LocalDate getCheckInDate() {
|
||||
return checkInDate;
|
||||
}
|
||||
|
||||
public void setCheckInDate(LocalDate checkInDate) {
|
||||
this.checkInDate = checkInDate;
|
||||
}
|
||||
|
||||
public String getCareLevel() {
|
||||
return careLevel;
|
||||
}
|
||||
|
||||
public void setCareLevel(String careLevel) {
|
||||
this.careLevel = careLevel;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class FamilyElder {
|
||||
private Long id;
|
||||
private Long familyId;
|
||||
private Long elderId;
|
||||
private String relationship;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getFamilyId() {
|
||||
return familyId;
|
||||
}
|
||||
|
||||
public void setFamilyId(Long familyId) {
|
||||
this.familyId = familyId;
|
||||
}
|
||||
|
||||
public Long getElderId() {
|
||||
return elderId;
|
||||
}
|
||||
|
||||
public void setElderId(Long elderId) {
|
||||
this.elderId = elderId;
|
||||
}
|
||||
|
||||
public String getRelationship() {
|
||||
return relationship;
|
||||
}
|
||||
|
||||
public void setRelationship(String relationship) {
|
||||
this.relationship = relationship;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
96
backend/src/main/java/com/nursinghome/entity/Feedback.java
Normal file
96
backend/src/main/java/com/nursinghome/entity/Feedback.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class Feedback {
|
||||
private Long id;
|
||||
private Long familyId;
|
||||
private Long elderId;
|
||||
private String type;
|
||||
private String content;
|
||||
private Integer rating;
|
||||
private String status;
|
||||
private String reply;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getFamilyId() {
|
||||
return familyId;
|
||||
}
|
||||
|
||||
public void setFamilyId(Long familyId) {
|
||||
this.familyId = familyId;
|
||||
}
|
||||
|
||||
public Long getElderId() {
|
||||
return elderId;
|
||||
}
|
||||
|
||||
public void setElderId(Long elderId) {
|
||||
this.elderId = elderId;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Integer getRating() {
|
||||
return rating;
|
||||
}
|
||||
|
||||
public void setRating(Integer rating) {
|
||||
this.rating = rating;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getReply() {
|
||||
return reply;
|
||||
}
|
||||
|
||||
public void setReply(String reply) {
|
||||
this.reply = reply;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
52
backend/src/main/java/com/nursinghome/entity/Handover.java
Normal file
52
backend/src/main/java/com/nursinghome/entity/Handover.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class Handover {
|
||||
private Long id;
|
||||
private Long nurseId;
|
||||
private LocalDate date;
|
||||
private String content;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getNurseId() {
|
||||
return nurseId;
|
||||
}
|
||||
|
||||
public void setNurseId(Long nurseId) {
|
||||
this.nurseId = nurseId;
|
||||
}
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class HealthRecord {
|
||||
private Long id;
|
||||
private Long elderId;
|
||||
private Long nurseId;
|
||||
private BigDecimal temperature;
|
||||
private Integer bpSystolic;
|
||||
private Integer bpDiastolic;
|
||||
private Integer heartRate;
|
||||
private String note;
|
||||
private LocalDateTime recordTime;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getElderId() {
|
||||
return elderId;
|
||||
}
|
||||
|
||||
public void setElderId(Long elderId) {
|
||||
this.elderId = elderId;
|
||||
}
|
||||
|
||||
public Long getNurseId() {
|
||||
return nurseId;
|
||||
}
|
||||
|
||||
public void setNurseId(Long nurseId) {
|
||||
this.nurseId = nurseId;
|
||||
}
|
||||
|
||||
public BigDecimal getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(BigDecimal temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
public Integer getBpSystolic() {
|
||||
return bpSystolic;
|
||||
}
|
||||
|
||||
public void setBpSystolic(Integer bpSystolic) {
|
||||
this.bpSystolic = bpSystolic;
|
||||
}
|
||||
|
||||
public Integer getBpDiastolic() {
|
||||
return bpDiastolic;
|
||||
}
|
||||
|
||||
public void setBpDiastolic(Integer bpDiastolic) {
|
||||
this.bpDiastolic = bpDiastolic;
|
||||
}
|
||||
|
||||
public Integer getHeartRate() {
|
||||
return heartRate;
|
||||
}
|
||||
|
||||
public void setHeartRate(Integer heartRate) {
|
||||
this.heartRate = heartRate;
|
||||
}
|
||||
|
||||
public String getNote() {
|
||||
return note;
|
||||
}
|
||||
|
||||
public void setNote(String note) {
|
||||
this.note = note;
|
||||
}
|
||||
|
||||
public LocalDateTime getRecordTime() {
|
||||
return recordTime;
|
||||
}
|
||||
|
||||
public void setRecordTime(LocalDateTime recordTime) {
|
||||
this.recordTime = recordTime;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
69
backend/src/main/java/com/nursinghome/entity/Notice.java
Normal file
69
backend/src/main/java/com/nursinghome/entity/Notice.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class Notice {
|
||||
private Long id;
|
||||
private String title;
|
||||
private String content;
|
||||
private String targetRole;
|
||||
private Long targetUserId;
|
||||
private Long createdBy;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getTargetRole() {
|
||||
return targetRole;
|
||||
}
|
||||
|
||||
public void setTargetRole(String targetRole) {
|
||||
this.targetRole = targetRole;
|
||||
}
|
||||
|
||||
public Long getTargetUserId() {
|
||||
return targetUserId;
|
||||
}
|
||||
|
||||
public void setTargetUserId(Long targetUserId) {
|
||||
this.targetUserId = targetUserId;
|
||||
}
|
||||
|
||||
public Long getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(Long createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class PaymentRecord {
|
||||
private Long id;
|
||||
private Long billId;
|
||||
private Long familyId;
|
||||
private BigDecimal amount;
|
||||
private String method;
|
||||
private LocalDateTime paidAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getBillId() {
|
||||
return billId;
|
||||
}
|
||||
|
||||
public void setBillId(Long billId) {
|
||||
this.billId = billId;
|
||||
}
|
||||
|
||||
public Long getFamilyId() {
|
||||
return familyId;
|
||||
}
|
||||
|
||||
public void setFamilyId(Long familyId) {
|
||||
this.familyId = familyId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public LocalDateTime getPaidAt() {
|
||||
return paidAt;
|
||||
}
|
||||
|
||||
public void setPaidAt(LocalDateTime paidAt) {
|
||||
this.paidAt = paidAt;
|
||||
}
|
||||
}
|
||||
70
backend/src/main/java/com/nursinghome/entity/Schedule.java
Normal file
70
backend/src/main/java/com/nursinghome/entity/Schedule.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class Schedule {
|
||||
private Long id;
|
||||
private Long nurseId;
|
||||
private LocalDate date;
|
||||
private String shift;
|
||||
private String task;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getNurseId() {
|
||||
return nurseId;
|
||||
}
|
||||
|
||||
public void setNurseId(Long nurseId) {
|
||||
this.nurseId = nurseId;
|
||||
}
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getShift() {
|
||||
return shift;
|
||||
}
|
||||
|
||||
public void setShift(String shift) {
|
||||
this.shift = shift;
|
||||
}
|
||||
|
||||
public String getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
public void setTask(String task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
87
backend/src/main/java/com/nursinghome/entity/User.java
Normal file
87
backend/src/main/java/com/nursinghome/entity/User.java
Normal file
@@ -0,0 +1,87 @@
|
||||
package com.nursinghome.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class User {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String password;
|
||||
private String name;
|
||||
private String phone;
|
||||
private String role;
|
||||
private Integer status;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public Integer getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Integer status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
28
backend/src/main/java/com/nursinghome/mapper/BillMapper.java
Normal file
28
backend/src/main/java/com/nursinghome/mapper/BillMapper.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.Bill;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface BillMapper {
|
||||
@Insert("INSERT INTO bill(elder_id, month, bed_fee, care_fee, meal_fee, other_fee, total, status, created_at) VALUES(#{elderId}, #{month}, #{bedFee}, #{careFee}, #{mealFee}, #{otherFee}, #{total}, #{status}, NOW())")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(Bill bill);
|
||||
|
||||
@Select("SELECT * FROM bill ORDER BY month DESC")
|
||||
List<Bill> listAll();
|
||||
|
||||
@Select("SELECT * FROM bill WHERE id = #{id}")
|
||||
Bill findById(Long id);
|
||||
|
||||
@Select("SELECT * FROM bill WHERE elder_id = #{elderId} ORDER BY month DESC")
|
||||
List<Bill> listByElderId(Long elderId);
|
||||
|
||||
@Select("<script>SELECT * FROM bill WHERE elder_id IN <foreach collection='elderIds' item='id' open='(' separator=',' close=')'>#{id}</foreach> ORDER BY month DESC</script>")
|
||||
List<Bill> listByElderIds(@Param("elderIds") List<Long> elderIds);
|
||||
|
||||
@Update("UPDATE bill SET status=#{status}, paid_at=#{paidAt} WHERE id=#{id}")
|
||||
int updateStatus(Bill bill);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.CareRecord;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface CareRecordMapper {
|
||||
@Insert("INSERT INTO care_record(elder_id, nurse_id, content, attachment_url, record_time, created_at) VALUES(#{elderId}, #{nurseId}, #{content}, #{attachmentUrl}, #{recordTime}, NOW())")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(CareRecord record);
|
||||
|
||||
@Select("SELECT * FROM care_record WHERE elder_id = #{elderId} ORDER BY record_time DESC")
|
||||
List<CareRecord> listByElderId(Long elderId);
|
||||
|
||||
@Select("<script>SELECT * FROM care_record WHERE elder_id IN <foreach collection='elderIds' item='id' open='(' separator=',' close=')'>#{id}</foreach> ORDER BY record_time DESC</script>")
|
||||
List<CareRecord> listByElderIds(@Param("elderIds") List<Long> elderIds);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.Elder;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ElderMapper {
|
||||
@Select("SELECT * FROM elder ORDER BY id DESC")
|
||||
List<Elder> listAll();
|
||||
|
||||
@Select("SELECT * FROM elder WHERE id = #{id}")
|
||||
Elder findById(Long id);
|
||||
|
||||
@Select("SELECT * FROM elder WHERE id_card = #{idCard} LIMIT 1")
|
||||
Elder findByIdCard(String idCard);
|
||||
|
||||
@Insert("INSERT INTO elder(name, gender, id_card, birthday, room_no, check_in_date, care_level, status, remark) VALUES(#{name}, #{gender}, #{idCard}, #{birthday}, #{roomNo}, #{checkInDate}, #{careLevel}, #{status}, #{remark})")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(Elder elder);
|
||||
|
||||
@Update("UPDATE elder SET name=#{name}, gender=#{gender}, id_card=#{idCard}, birthday=#{birthday}, room_no=#{roomNo}, check_in_date=#{checkInDate}, care_level=#{careLevel}, status=#{status}, remark=#{remark} WHERE id=#{id}")
|
||||
int update(Elder elder);
|
||||
|
||||
@Delete("DELETE FROM elder WHERE id=#{id}")
|
||||
int delete(Long id);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.FamilyElder;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface FamilyElderMapper {
|
||||
@Insert("INSERT INTO family_elder(family_id, elder_id, relationship, created_at) VALUES(#{familyId}, #{elderId}, #{relationship}, NOW())")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(FamilyElder relation);
|
||||
|
||||
@Select("SELECT * FROM family_elder WHERE family_id = #{familyId}")
|
||||
List<FamilyElder> listByFamilyId(Long familyId);
|
||||
|
||||
@Select("SELECT * FROM family_elder WHERE family_id = #{familyId} AND elder_id = #{elderId} LIMIT 1")
|
||||
FamilyElder findByFamilyAndElder(@Param("familyId") Long familyId, @Param("elderId") Long elderId);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.Feedback;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface FeedbackMapper {
|
||||
@Insert("INSERT INTO feedback(family_id, elder_id, type, content, rating, status, created_at, updated_at) VALUES(#{familyId}, #{elderId}, #{type}, #{content}, #{rating}, #{status}, NOW(), NOW())")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(Feedback feedback);
|
||||
|
||||
@Select("SELECT * FROM feedback ORDER BY created_at DESC")
|
||||
List<Feedback> listAll();
|
||||
|
||||
@Update("UPDATE feedback SET status=#{status}, reply=#{reply}, updated_at=NOW() WHERE id=#{id}")
|
||||
int updateStatus(Feedback feedback);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.Handover;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface HandoverMapper {
|
||||
@Insert("INSERT INTO handover(nurse_id, date, content, created_at) VALUES(#{nurseId}, #{date}, #{content}, NOW())")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(Handover handover);
|
||||
|
||||
@Select("SELECT * FROM handover WHERE nurse_id = #{nurseId} ORDER BY date DESC")
|
||||
List<Handover> listByNurseId(Long nurseId);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.HealthRecord;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface HealthRecordMapper {
|
||||
@Insert("INSERT INTO health_record(elder_id, nurse_id, temperature, bp_systolic, bp_diastolic, heart_rate, note, record_time, created_at) VALUES(#{elderId}, #{nurseId}, #{temperature}, #{bpSystolic}, #{bpDiastolic}, #{heartRate}, #{note}, #{recordTime}, NOW())")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(HealthRecord record);
|
||||
|
||||
@Select("SELECT * FROM health_record WHERE elder_id = #{elderId} ORDER BY record_time DESC")
|
||||
List<HealthRecord> listByElderId(Long elderId);
|
||||
|
||||
@Select("<script>SELECT * FROM health_record WHERE elder_id IN <foreach collection='elderIds' item='id' open='(' separator=',' close=')'>#{id}</foreach> ORDER BY record_time DESC</script>")
|
||||
List<HealthRecord> listByElderIds(@Param("elderIds") List<Long> elderIds);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.Notice;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface NoticeMapper {
|
||||
@Insert("INSERT INTO notice(title, content, target_role, target_user_id, created_by, created_at) VALUES(#{title}, #{content}, #{targetRole}, #{targetUserId}, #{createdBy}, NOW())")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(Notice notice);
|
||||
|
||||
@Select("<script>SELECT * FROM notice WHERE 1=1 <if test='targetUserId != null'> AND target_user_id = #{targetUserId}</if> <if test='targetRole != null'> AND (target_role = #{targetRole} OR target_role = 'ALL')</if> ORDER BY created_at DESC</script>")
|
||||
List<Notice> listByTarget(@Param("targetRole") String targetRole, @Param("targetUserId") Long targetUserId);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.PaymentRecord;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface PaymentRecordMapper {
|
||||
@Insert("INSERT INTO payment_record(bill_id, family_id, amount, method, paid_at) VALUES(#{billId}, #{familyId}, #{amount}, #{method}, #{paidAt})")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(PaymentRecord record);
|
||||
|
||||
@Select("SELECT * FROM payment_record WHERE bill_id = #{billId} ORDER BY paid_at DESC")
|
||||
List<PaymentRecord> listByBillId(Long billId);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.Schedule;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ScheduleMapper {
|
||||
@Select("SELECT * FROM schedule WHERE nurse_id = #{nurseId} AND date = #{date} ORDER BY id DESC")
|
||||
List<Schedule> listByNurseAndDate(@Param("nurseId") Long nurseId, @Param("date") LocalDate date);
|
||||
|
||||
@Select("SELECT * FROM schedule WHERE date = #{date} ORDER BY id DESC")
|
||||
List<Schedule> listByDate(LocalDate date);
|
||||
|
||||
@Insert("INSERT INTO schedule(nurse_id, date, shift, task, created_at, updated_at) VALUES(#{nurseId}, #{date}, #{shift}, #{task}, NOW(), NOW())")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(Schedule schedule);
|
||||
|
||||
@Update("UPDATE schedule SET nurse_id=#{nurseId}, date=#{date}, shift=#{shift}, task=#{task}, updated_at=NOW() WHERE id=#{id}")
|
||||
int update(Schedule schedule);
|
||||
|
||||
@Delete("DELETE FROM schedule WHERE id=#{id}")
|
||||
int delete(Long id);
|
||||
}
|
||||
31
backend/src/main/java/com/nursinghome/mapper/UserMapper.java
Normal file
31
backend/src/main/java/com/nursinghome/mapper/UserMapper.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.nursinghome.mapper;
|
||||
|
||||
import com.nursinghome.entity.User;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface UserMapper {
|
||||
@Select("SELECT * FROM sys_user WHERE username = #{username} LIMIT 1")
|
||||
User findByUsername(String username);
|
||||
|
||||
@Select("SELECT * FROM sys_user WHERE id = #{id}")
|
||||
User findById(Long id);
|
||||
|
||||
@Select("<script>SELECT * FROM sys_user <where><if test='role != null'>role = #{role}</if></where> ORDER BY id DESC</script>")
|
||||
List<User> listByRole(@Param("role") String role);
|
||||
|
||||
@Insert("INSERT INTO sys_user(username, password, name, phone, role, status, created_at, updated_at) VALUES(#{username}, #{password}, #{name}, #{phone}, #{role}, #{status}, NOW(), NOW())")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id")
|
||||
int insert(User user);
|
||||
|
||||
@Update("UPDATE sys_user SET name=#{name}, phone=#{phone}, status=#{status}, updated_at=NOW() WHERE id=#{id}")
|
||||
int update(User user);
|
||||
|
||||
@Update("UPDATE sys_user SET password=#{password}, updated_at=NOW() WHERE id=#{id}")
|
||||
int updatePassword(@Param("id") Long id, @Param("password") String password);
|
||||
|
||||
@Update("UPDATE sys_user SET status=#{status}, updated_at=NOW() WHERE id=#{id}")
|
||||
int updateStatus(@Param("id") Long id, @Param("status") Integer status);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.nursinghome.service;
|
||||
|
||||
import com.nursinghome.entity.Bill;
|
||||
import com.nursinghome.entity.PaymentRecord;
|
||||
import com.nursinghome.mapper.BillMapper;
|
||||
import com.nursinghome.mapper.PaymentRecordMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class BillService {
|
||||
private final BillMapper billMapper;
|
||||
private final PaymentRecordMapper paymentRecordMapper;
|
||||
|
||||
public BillService(BillMapper billMapper, PaymentRecordMapper paymentRecordMapper) {
|
||||
this.billMapper = billMapper;
|
||||
this.paymentRecordMapper = paymentRecordMapper;
|
||||
}
|
||||
|
||||
public Bill createBill(Bill bill) {
|
||||
if (bill.getTotal() == null) {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
if (bill.getBedFee() != null) total = total.add(bill.getBedFee());
|
||||
if (bill.getCareFee() != null) total = total.add(bill.getCareFee());
|
||||
if (bill.getMealFee() != null) total = total.add(bill.getMealFee());
|
||||
if (bill.getOtherFee() != null) total = total.add(bill.getOtherFee());
|
||||
bill.setTotal(total);
|
||||
}
|
||||
if (bill.getStatus() == null) {
|
||||
bill.setStatus("UNPAID");
|
||||
}
|
||||
billMapper.insert(bill);
|
||||
return bill;
|
||||
}
|
||||
|
||||
public List<Bill> listByElderId(Long elderId) {
|
||||
return billMapper.listByElderId(elderId);
|
||||
}
|
||||
|
||||
public List<Bill> listByElderIds(List<Long> elderIds) {
|
||||
return billMapper.listByElderIds(elderIds);
|
||||
}
|
||||
|
||||
public void payBill(Long billId, Long familyId, String method, BigDecimal amount) {
|
||||
Bill bill = new Bill();
|
||||
bill.setId(billId);
|
||||
bill.setStatus("PAID");
|
||||
bill.setPaidAt(LocalDateTime.now());
|
||||
billMapper.updateStatus(bill);
|
||||
|
||||
PaymentRecord record = new PaymentRecord();
|
||||
record.setBillId(billId);
|
||||
record.setFamilyId(familyId);
|
||||
record.setAmount(amount);
|
||||
record.setMethod(method);
|
||||
record.setPaidAt(LocalDateTime.now());
|
||||
paymentRecordMapper.insert(record);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.nursinghome.service;
|
||||
|
||||
import com.nursinghome.common.ApiException;
|
||||
import com.nursinghome.entity.Elder;
|
||||
import com.nursinghome.mapper.ElderMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class ElderService {
|
||||
private final ElderMapper elderMapper;
|
||||
|
||||
public ElderService(ElderMapper elderMapper) {
|
||||
this.elderMapper = elderMapper;
|
||||
}
|
||||
|
||||
public List<Elder> listAll() {
|
||||
return elderMapper.listAll();
|
||||
}
|
||||
|
||||
public Elder findById(Long id) {
|
||||
return elderMapper.findById(id);
|
||||
}
|
||||
|
||||
public Elder findByIdCard(String idCard) {
|
||||
return elderMapper.findByIdCard(idCard);
|
||||
}
|
||||
|
||||
public Elder create(Elder elder) {
|
||||
if (elderMapper.findByIdCard(elder.getIdCard()) != null) {
|
||||
throw new ApiException("id card already exists");
|
||||
}
|
||||
elderMapper.insert(elder);
|
||||
return elder;
|
||||
}
|
||||
|
||||
public void update(Elder elder) {
|
||||
elderMapper.update(elder);
|
||||
}
|
||||
|
||||
public void delete(Long id) {
|
||||
elderMapper.delete(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.nursinghome.service;
|
||||
|
||||
import com.nursinghome.common.ApiException;
|
||||
import com.nursinghome.entity.Elder;
|
||||
import com.nursinghome.entity.FamilyElder;
|
||||
import com.nursinghome.entity.User;
|
||||
import com.nursinghome.mapper.FamilyElderMapper;
|
||||
import com.nursinghome.util.PasswordUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class FamilyService {
|
||||
private final UserService userService;
|
||||
private final ElderService elderService;
|
||||
private final FamilyElderMapper familyElderMapper;
|
||||
|
||||
public FamilyService(UserService userService, ElderService elderService, FamilyElderMapper familyElderMapper) {
|
||||
this.userService = userService;
|
||||
this.elderService = elderService;
|
||||
this.familyElderMapper = familyElderMapper;
|
||||
}
|
||||
|
||||
public User registerFamily(String username, String password, String name, String phone, String elderIdCard, String relationship) {
|
||||
Elder elder = elderService.findByIdCard(elderIdCard);
|
||||
if (elder == null) {
|
||||
throw new ApiException("elder not found");
|
||||
}
|
||||
User user = new User();
|
||||
user.setUsername(username);
|
||||
user.setPassword(password);
|
||||
user.setName(name);
|
||||
user.setPhone(phone);
|
||||
user.setRole("FAMILY");
|
||||
user.setStatus(1);
|
||||
userService.createUser(user, true);
|
||||
|
||||
FamilyElder relation = new FamilyElder();
|
||||
relation.setFamilyId(user.getId());
|
||||
relation.setElderId(elder.getId());
|
||||
relation.setRelationship(relationship);
|
||||
familyElderMapper.insert(relation);
|
||||
return user;
|
||||
}
|
||||
|
||||
public List<FamilyElder> listRelations(Long familyId) {
|
||||
return familyElderMapper.listByFamilyId(familyId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.nursinghome.service;
|
||||
|
||||
import com.nursinghome.common.ApiException;
|
||||
import com.nursinghome.entity.User;
|
||||
import com.nursinghome.mapper.UserMapper;
|
||||
import com.nursinghome.util.PasswordUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class UserService {
|
||||
private final UserMapper userMapper;
|
||||
|
||||
public UserService(UserMapper userMapper) {
|
||||
this.userMapper = userMapper;
|
||||
}
|
||||
|
||||
public User findByUsername(String username) {
|
||||
return userMapper.findByUsername(username);
|
||||
}
|
||||
|
||||
public User findById(Long id) {
|
||||
return userMapper.findById(id);
|
||||
}
|
||||
|
||||
public List<User> listByRole(String role) {
|
||||
return userMapper.listByRole(role);
|
||||
}
|
||||
|
||||
public User createUser(User user, boolean hashPassword) {
|
||||
if (userMapper.findByUsername(user.getUsername()) != null) {
|
||||
throw new ApiException("username already exists");
|
||||
}
|
||||
if (hashPassword) {
|
||||
user.setPassword(PasswordUtil.hash(user.getPassword()));
|
||||
}
|
||||
if (user.getStatus() == null) {
|
||||
user.setStatus(1);
|
||||
}
|
||||
userMapper.insert(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
public void updateUser(User user) {
|
||||
userMapper.update(user);
|
||||
}
|
||||
|
||||
public void updateStatus(Long id, Integer status) {
|
||||
userMapper.updateStatus(id, status);
|
||||
}
|
||||
|
||||
public void updatePassword(Long id, String password) {
|
||||
userMapper.updatePassword(id, password);
|
||||
}
|
||||
}
|
||||
25
backend/src/main/java/com/nursinghome/util/PasswordUtil.java
Normal file
25
backend/src/main/java/com/nursinghome/util/PasswordUtil.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.nursinghome.util;
|
||||
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
public class PasswordUtil {
|
||||
private static final BCryptPasswordEncoder ENCODER = new BCryptPasswordEncoder();
|
||||
|
||||
public static String hash(String raw) {
|
||||
return ENCODER.encode(raw);
|
||||
}
|
||||
|
||||
public static boolean matches(String raw, String stored) {
|
||||
if (stored == null) {
|
||||
return false;
|
||||
}
|
||||
if (isBcrypt(stored)) {
|
||||
return ENCODER.matches(raw, stored);
|
||||
}
|
||||
return stored.equals(raw);
|
||||
}
|
||||
|
||||
public static boolean isBcrypt(String stored) {
|
||||
return stored != null && stored.startsWith("$2");
|
||||
}
|
||||
}
|
||||
28
backend/src/main/resources/application.yml
Normal file
28
backend/src/main/resources/application.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:mysql://localhost:3306/nursing_home?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: root
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 10MB
|
||||
max-request-size: 20MB
|
||||
|
||||
mybatis:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
|
||||
sa-token:
|
||||
token-name: satoken
|
||||
timeout: 86400
|
||||
activity-timeout: -1
|
||||
is-concurrent: true
|
||||
is-share: true
|
||||
token-style: uuid
|
||||
|
||||
app:
|
||||
upload-dir: uploads
|
||||
3
frontend/babel.config.js
Normal file
3
frontend/babel.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"]
|
||||
};
|
||||
22
frontend/package.json
Normal file
22
frontend/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "nursing-home-frontend",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"element-ui": "^2.15.14",
|
||||
"vue": "^2.7.16",
|
||||
"vue-router": "^3.6.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^5.0.8",
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-loader": "^8.3.0",
|
||||
"vue-template-compiler": "^2.7.16"
|
||||
}
|
||||
}
|
||||
14
frontend/public/index.html
Normal file
14
frontend/public/index.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Nursing Home</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>JavaScript is required.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
13
frontend/src/App.vue
Normal file
13
frontend/src/App.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background: #f3f8f3;
|
||||
font-family: "Source Sans 3", "Helvetica Neue", Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
26
frontend/src/api/http.js
Normal file
26
frontend/src/api/http.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import axios from "axios";
|
||||
|
||||
const http = axios.create({
|
||||
baseURL: "/api"
|
||||
});
|
||||
|
||||
http.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem("token");
|
||||
if (token) {
|
||||
config.headers["satoken"] = token;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
http.interceptors.response.use(
|
||||
(response) => {
|
||||
const data = response.data;
|
||||
if (data && data.code !== 0) {
|
||||
return Promise.reject(new Error(data.message || "request error"));
|
||||
}
|
||||
return response;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
|
||||
export default http;
|
||||
54
frontend/src/api/index.js
Normal file
54
frontend/src/api/index.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import http from "./http";
|
||||
|
||||
export const login = (payload) => http.post("/auth/login", payload);
|
||||
export const register = (payload) => http.post("/auth/register", payload);
|
||||
export const me = () => http.get("/auth/me");
|
||||
export const logout = () => http.post("/auth/logout");
|
||||
|
||||
export const adminStats = () => http.get("/admin/stats");
|
||||
export const adminUsers = (role) => http.get("/admin/users", { params: { role } });
|
||||
export const adminCreateUser = (payload) => http.post("/admin/users", payload);
|
||||
export const adminUpdateUser = (payload) => http.put("/admin/users", payload);
|
||||
export const adminResetPassword = (id, password) => http.post(`/admin/users/${id}/reset-password`, null, { params: { password } });
|
||||
|
||||
export const eldersList = () => http.get("/admin/elders");
|
||||
export const eldersCreate = (payload) => http.post("/admin/elders", payload);
|
||||
export const eldersUpdate = (payload) => http.put("/admin/elders", payload);
|
||||
export const eldersDelete = (id) => http.delete(`/admin/elders/${id}`);
|
||||
|
||||
export const schedulesByDate = (date) => http.get("/admin/schedules", { params: { date } });
|
||||
export const scheduleCreate = (payload) => http.post("/admin/schedules", payload);
|
||||
export const scheduleUpdate = (payload) => http.put("/admin/schedules", payload);
|
||||
export const scheduleDelete = (id) => http.delete(`/admin/schedules/${id}`);
|
||||
|
||||
export const billsCreate = (payload) => http.post("/admin/bills", payload);
|
||||
export const billsList = (elderId) => http.get("/admin/bills", { params: { elderId } });
|
||||
|
||||
export const feedbackList = () => http.get("/admin/feedback");
|
||||
export const feedbackUpdate = (payload) => http.put("/admin/feedback", payload);
|
||||
|
||||
export const noticeCreate = (payload) => http.post("/admin/notices", payload);
|
||||
export const noticeList = (role, userId) => http.get("/admin/notices", { params: { role, userId } });
|
||||
|
||||
export const nurseSchedules = (date) => http.get("/nurse/schedules", { params: { date } });
|
||||
export const nurseCareCreate = (payload) => http.post("/nurse/care-records", payload);
|
||||
export const nurseCareList = (elderId) => http.get("/nurse/care-records", { params: { elderId } });
|
||||
export const nurseHealthCreate = (payload) => http.post("/nurse/health-records", payload);
|
||||
export const nurseHealthList = (elderId) => http.get("/nurse/health-records", { params: { elderId } });
|
||||
export const nurseHandoverCreate = (payload) => http.post("/nurse/handovers", payload);
|
||||
export const nurseHandoverList = () => http.get("/nurse/handovers");
|
||||
export const nurseNoticeList = () => http.get("/nurse/notices");
|
||||
|
||||
export const familyElders = () => http.get("/family/elders");
|
||||
export const familyCareList = (elderId) => http.get("/family/care-records", { params: { elderId } });
|
||||
export const familyHealthList = (elderId) => http.get("/family/health-records", { params: { elderId } });
|
||||
export const familyBills = (elderId) => http.get("/family/bills", { params: { elderId } });
|
||||
export const familyPay = (id, payload) => http.post(`/family/bills/${id}/pay`, payload);
|
||||
export const familyFeedback = (payload) => http.post("/family/feedback", payload);
|
||||
export const familyNoticeList = () => http.get("/family/notices");
|
||||
|
||||
export const uploadFile = (file) => {
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
return http.post("/files/upload", form, { headers: { "Content-Type": "multipart/form-data" } });
|
||||
};
|
||||
34
frontend/src/assets/theme.css
Normal file
34
frontend/src/assets/theme.css
Normal file
@@ -0,0 +1,34 @@
|
||||
:root {
|
||||
--primary-green: #2f7d59;
|
||||
--primary-green-dark: #225c43;
|
||||
--primary-green-light: #d9efe3;
|
||||
}
|
||||
|
||||
.el-button--primary,
|
||||
.el-menu-item.is-active,
|
||||
.el-submenu__title:hover,
|
||||
.el-menu-item:hover {
|
||||
background-color: var(--primary-green) !important;
|
||||
border-color: var(--primary-green) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.page-card {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 8px 24px rgba(47, 125, 89, 0.12);
|
||||
}
|
||||
|
||||
.header-bar {
|
||||
background: linear-gradient(135deg, var(--primary-green) 0%, #5fbf90 100%);
|
||||
color: #fff;
|
||||
padding: 14px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
14
frontend/src/main.js
Normal file
14
frontend/src/main.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import Vue from "vue";
|
||||
import ElementUI from "element-ui";
|
||||
import "element-ui/lib/theme-chalk/index.css";
|
||||
import "./assets/theme.css";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
|
||||
Vue.use(ElementUI);
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
render: (h) => h(App)
|
||||
}).$mount("#app");
|
||||
102
frontend/src/router/index.js
Normal file
102
frontend/src/router/index.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import Vue from "vue";
|
||||
import Router from "vue-router";
|
||||
import Login from "../views/Login.vue";
|
||||
import Register from "../views/Register.vue";
|
||||
import Layout from "../views/Layout.vue";
|
||||
|
||||
import AdminDashboard from "../views/admin/Dashboard.vue";
|
||||
import AdminUsers from "../views/admin/Users.vue";
|
||||
import AdminElders from "../views/admin/Elders.vue";
|
||||
import AdminSchedules from "../views/admin/Schedules.vue";
|
||||
import AdminBills from "../views/admin/Bills.vue";
|
||||
import AdminFeedback from "../views/admin/Feedback.vue";
|
||||
import AdminNotices from "../views/admin/Notices.vue";
|
||||
|
||||
import NurseDashboard from "../views/nurse/Dashboard.vue";
|
||||
import NurseSchedules from "../views/nurse/Schedules.vue";
|
||||
import NurseCare from "../views/nurse/CareRecords.vue";
|
||||
import NurseHealth from "../views/nurse/HealthRecords.vue";
|
||||
import NurseHandovers from "../views/nurse/Handovers.vue";
|
||||
import NurseNotices from "../views/nurse/Notices.vue";
|
||||
|
||||
import FamilyDashboard from "../views/family/Dashboard.vue";
|
||||
import FamilyElders from "../views/family/Elders.vue";
|
||||
import FamilyCare from "../views/family/CareRecords.vue";
|
||||
import FamilyHealth from "../views/family/HealthRecords.vue";
|
||||
import FamilyBills from "../views/family/Bills.vue";
|
||||
import FamilyFeedback from "../views/family/Feedback.vue";
|
||||
import FamilyNotices from "../views/family/Notices.vue";
|
||||
|
||||
Vue.use(Router);
|
||||
|
||||
const router = new Router({
|
||||
mode: "history",
|
||||
routes: [
|
||||
{ path: "/", redirect: "/login" },
|
||||
{ path: "/login", component: Login },
|
||||
{ path: "/register", component: Register },
|
||||
{
|
||||
path: "/admin",
|
||||
component: Layout,
|
||||
meta: { role: "ADMIN" },
|
||||
children: [
|
||||
{ path: "", redirect: "dashboard" },
|
||||
{ path: "dashboard", component: AdminDashboard },
|
||||
{ path: "users", component: AdminUsers },
|
||||
{ path: "elders", component: AdminElders },
|
||||
{ path: "schedules", component: AdminSchedules },
|
||||
{ path: "bills", component: AdminBills },
|
||||
{ path: "feedback", component: AdminFeedback },
|
||||
{ path: "notices", component: AdminNotices }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/nurse",
|
||||
component: Layout,
|
||||
meta: { role: "NURSE" },
|
||||
children: [
|
||||
{ path: "", redirect: "dashboard" },
|
||||
{ path: "dashboard", component: NurseDashboard },
|
||||
{ path: "schedules", component: NurseSchedules },
|
||||
{ path: "care", component: NurseCare },
|
||||
{ path: "health", component: NurseHealth },
|
||||
{ path: "handovers", component: NurseHandovers },
|
||||
{ path: "notices", component: NurseNotices }
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/family",
|
||||
component: Layout,
|
||||
meta: { role: "FAMILY" },
|
||||
children: [
|
||||
{ path: "", redirect: "dashboard" },
|
||||
{ path: "dashboard", component: FamilyDashboard },
|
||||
{ path: "elders", component: FamilyElders },
|
||||
{ path: "care", component: FamilyCare },
|
||||
{ path: "health", component: FamilyHealth },
|
||||
{ path: "bills", component: FamilyBills },
|
||||
{ path: "feedback", component: FamilyFeedback },
|
||||
{ path: "notices", component: FamilyNotices }
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const token = localStorage.getItem("token");
|
||||
const role = localStorage.getItem("role");
|
||||
if (to.path === "/login" || to.path === "/register") {
|
||||
return next();
|
||||
}
|
||||
if (!token) {
|
||||
return next("/login");
|
||||
}
|
||||
if (to.meta && to.meta.role && to.meta.role !== role) {
|
||||
if (role === "ADMIN") return next("/admin");
|
||||
if (role === "NURSE") return next("/nurse");
|
||||
if (role === "FAMILY") return next("/family");
|
||||
}
|
||||
return next();
|
||||
});
|
||||
|
||||
export default router;
|
||||
20
frontend/src/utils/date.js
Normal file
20
frontend/src/utils/date.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export function formatDate(value) {
|
||||
if (!value) return "";
|
||||
const d = new Date(value);
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
return `${y}-${m}-${day}`;
|
||||
}
|
||||
|
||||
export function formatDateTime(value) {
|
||||
if (!value) return "";
|
||||
const d = new Date(value);
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
const hh = String(d.getHours()).padStart(2, "0");
|
||||
const mm = String(d.getMinutes()).padStart(2, "0");
|
||||
const ss = String(d.getSeconds()).padStart(2, "0");
|
||||
return `${y}-${m}-${day}T${hh}:${mm}:${ss}`;
|
||||
}
|
||||
96
frontend/src/views/Layout.vue
Normal file
96
frontend/src/views/Layout.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<el-container style="min-height: 100vh;">
|
||||
<el-aside width="220px" style="background: #0f2f23; color: #fff;">
|
||||
<div style="padding: 18px 16px; font-size: 18px; font-weight: 600;">
|
||||
Nursing Home
|
||||
</div>
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
router
|
||||
background-color="#0f2f23"
|
||||
text-color="#c7e7d5"
|
||||
active-text-color="#ffffff">
|
||||
<el-menu-item v-for="item in menu" :key="item.path" :index="item.path">
|
||||
{{ item.label }}
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-container>
|
||||
<div class="header-bar">
|
||||
<div>{{ roleLabel }}</div>
|
||||
<div>
|
||||
<el-button size="mini" type="primary" @click="handleLogout">Logout</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-main>
|
||||
<router-view />
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { logout } from "../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
role: localStorage.getItem("role")
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
menu() {
|
||||
if (this.role === "ADMIN") {
|
||||
return [
|
||||
{ path: "/admin/dashboard", label: "Dashboard" },
|
||||
{ path: "/admin/users", label: "Users" },
|
||||
{ path: "/admin/elders", label: "Elders" },
|
||||
{ path: "/admin/schedules", label: "Schedules" },
|
||||
{ path: "/admin/bills", label: "Bills" },
|
||||
{ path: "/admin/feedback", label: "Feedback" },
|
||||
{ path: "/admin/notices", label: "Notices" }
|
||||
];
|
||||
}
|
||||
if (this.role === "NURSE") {
|
||||
return [
|
||||
{ path: "/nurse/dashboard", label: "Dashboard" },
|
||||
{ path: "/nurse/schedules", label: "My Schedule" },
|
||||
{ path: "/nurse/care", label: "Care Records" },
|
||||
{ path: "/nurse/health", label: "Health Records" },
|
||||
{ path: "/nurse/handovers", label: "Handovers" },
|
||||
{ path: "/nurse/notices", label: "Notices" }
|
||||
];
|
||||
}
|
||||
return [
|
||||
{ path: "/family/dashboard", label: "Dashboard" },
|
||||
{ path: "/family/elders", label: "Elders" },
|
||||
{ path: "/family/care", label: "Daily Care" },
|
||||
{ path: "/family/health", label: "Health" },
|
||||
{ path: "/family/bills", label: "Bills" },
|
||||
{ path: "/family/feedback", label: "Feedback" },
|
||||
{ path: "/family/notices", label: "Notices" }
|
||||
];
|
||||
},
|
||||
activeMenu() {
|
||||
return this.$route.path;
|
||||
},
|
||||
roleLabel() {
|
||||
if (this.role === "ADMIN") return "Administrator";
|
||||
if (this.role === "NURSE") return "Nurse";
|
||||
return "Family";
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async handleLogout() {
|
||||
try {
|
||||
await logout();
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("role");
|
||||
this.$router.push("/login");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
75
frontend/src/views/Login.vue
Normal file
75
frontend/src/views/Login.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="login-page">
|
||||
<div class="login-card">
|
||||
<h2>Welcome Back</h2>
|
||||
<el-form :model="form" label-position="top">
|
||||
<el-form-item label="Username">
|
||||
<el-input v-model="form.username" placeholder="username"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Password">
|
||||
<el-input v-model="form.password" type="password" placeholder="password"></el-input>
|
||||
</el-form-item>
|
||||
<el-button type="primary" style="width: 100%;" @click="handleLogin">Login</el-button>
|
||||
</el-form>
|
||||
<div class="login-footer">
|
||||
<span>No account?</span>
|
||||
<el-button type="text" @click="$router.push('/register')">Register</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { login } from "../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
username: "",
|
||||
password: ""
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async handleLogin() {
|
||||
try {
|
||||
const res = await login(this.form);
|
||||
const data = res.data.data;
|
||||
localStorage.setItem("token", data.token);
|
||||
localStorage.setItem("role", data.role);
|
||||
if (data.role === "ADMIN") {
|
||||
this.$router.push("/admin");
|
||||
} else if (data.role === "NURSE") {
|
||||
this.$router.push("/nurse");
|
||||
} else {
|
||||
this.$router.push("/family");
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "login failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(circle at 20% 20%, #dff3e7, #f3f8f3);
|
||||
}
|
||||
.login-card {
|
||||
width: 360px;
|
||||
padding: 28px;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 14px 30px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.login-footer {
|
||||
margin-top: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
82
frontend/src/views/Register.vue
Normal file
82
frontend/src/views/Register.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="login-page">
|
||||
<div class="login-card">
|
||||
<h2>Family Register</h2>
|
||||
<el-form :model="form" label-position="top">
|
||||
<el-form-item label="Username">
|
||||
<el-input v-model="form.username"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Password">
|
||||
<el-input v-model="form.password" type="password"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Name">
|
||||
<el-input v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Phone">
|
||||
<el-input v-model="form.phone"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Elder ID Card">
|
||||
<el-input v-model="form.elderIdCard"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="Relationship">
|
||||
<el-input v-model="form.relationship" placeholder="son/daughter/spouse"></el-input>
|
||||
</el-form-item>
|
||||
<el-button type="primary" style="width: 100%;" @click="handleRegister">Register</el-button>
|
||||
</el-form>
|
||||
<div class="login-footer">
|
||||
<el-button type="text" @click="$router.push('/login')">Back to Login</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { register } from "../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
username: "",
|
||||
password: "",
|
||||
name: "",
|
||||
phone: "",
|
||||
elderIdCard: "",
|
||||
relationship: ""
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async handleRegister() {
|
||||
try {
|
||||
await register(this.form);
|
||||
this.$message.success("registered");
|
||||
this.$router.push("/login");
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "register failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(circle at 20% 20%, #dff3e7, #f3f8f3);
|
||||
}
|
||||
.login-card {
|
||||
width: 420px;
|
||||
padding: 28px;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 14px 30px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.login-footer {
|
||||
margin-top: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
69
frontend/src/views/admin/Bills.vue
Normal file
69
frontend/src/views/admin/Bills.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Billing</h3>
|
||||
<div style="display:flex; gap: 12px; margin-bottom: 12px;">
|
||||
<el-input v-model="filterElderId" placeholder="Elder ID" style="width: 200px;" />
|
||||
<el-button type="primary" @click="load">Search</el-button>
|
||||
<el-button @click="showCreate = true">New Bill</el-button>
|
||||
</div>
|
||||
<el-table :data="bills" stripe>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="elderId" label="Elder ID" width="100" />
|
||||
<el-table-column prop="month" label="Month" />
|
||||
<el-table-column prop="total" label="Total" />
|
||||
<el-table-column prop="status" label="Status" />
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="New Bill" :visible.sync="showCreate">
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="Elder ID"><el-input v-model="form.elderId"/></el-form-item>
|
||||
<el-form-item label="Month"><el-input v-model="form.month" placeholder="YYYY-MM"/></el-form-item>
|
||||
<el-form-item label="Bed Fee"><el-input v-model="form.bedFee"/></el-form-item>
|
||||
<el-form-item label="Care Fee"><el-input v-model="form.careFee"/></el-form-item>
|
||||
<el-form-item label="Meal Fee"><el-input v-model="form.mealFee"/></el-form-item>
|
||||
<el-form-item label="Other Fee"><el-input v-model="form.otherFee"/></el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showCreate = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="create">Create</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { billsList, billsCreate } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
bills: [],
|
||||
filterElderId: "",
|
||||
showCreate: false,
|
||||
form: { elderId: "", month: "", bedFee: "", careFee: "", mealFee: "", otherFee: "" }
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.load();
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
try {
|
||||
const res = await billsList(this.filterElderId || null);
|
||||
this.bills = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
async create() {
|
||||
try {
|
||||
await billsCreate(this.form);
|
||||
this.showCreate = false;
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "create failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
49
frontend/src/views/admin/Dashboard.vue
Normal file
49
frontend/src/views/admin/Dashboard.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Admin Dashboard</h3>
|
||||
<el-row :gutter="16" style="margin-top: 12px;">
|
||||
<el-col :span="6">
|
||||
<el-card>
|
||||
<div>Elders</div>
|
||||
<h2>{{ stats.elders }}</h2>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card>
|
||||
<div>Nurses</div>
|
||||
<h2>{{ stats.nurses }}</h2>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card>
|
||||
<div>Families</div>
|
||||
<h2>{{ stats.families }}</h2>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card>
|
||||
<div>Income</div>
|
||||
<h2>{{ stats.income }}</h2>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { adminStats } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { stats: { elders: 0, nurses: 0, families: 0, income: 0 } };
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
const res = await adminStats();
|
||||
this.stats = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
128
frontend/src/views/admin/Elders.vue
Normal file
128
frontend/src/views/admin/Elders.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<div style="display:flex; justify-content: space-between; align-items:center;">
|
||||
<h3>Elders</h3>
|
||||
<el-button type="primary" @click="showCreate = true">Add Elder</el-button>
|
||||
</div>
|
||||
<el-table :data="elders" stripe>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="name" label="Name" />
|
||||
<el-table-column prop="gender" label="Gender" width="80" />
|
||||
<el-table-column prop="idCard" label="ID Card" />
|
||||
<el-table-column prop="roomNo" label="Room" width="100" />
|
||||
<el-table-column prop="careLevel" label="Care Level" />
|
||||
<el-table-column label="Actions" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" @click="editElder(scope.row)">Edit</el-button>
|
||||
<el-button size="mini" type="danger" @click="deleteElder(scope.row)">Delete</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="Add Elder" :visible.sync="showCreate">
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="Name"><el-input v-model="form.name"/></el-form-item>
|
||||
<el-form-item label="Gender"><el-input v-model="form.gender"/></el-form-item>
|
||||
<el-form-item label="ID Card"><el-input v-model="form.idCard"/></el-form-item>
|
||||
<el-form-item label="Birthday"><el-date-picker v-model="form.birthday" type="date"/></el-form-item>
|
||||
<el-form-item label="Room No"><el-input v-model="form.roomNo"/></el-form-item>
|
||||
<el-form-item label="Check In"><el-date-picker v-model="form.checkInDate" type="date"/></el-form-item>
|
||||
<el-form-item label="Care Level"><el-input v-model="form.careLevel"/></el-form-item>
|
||||
<el-form-item label="Status"><el-input v-model="form.status"/></el-form-item>
|
||||
<el-form-item label="Remark"><el-input v-model="form.remark"/></el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showCreate = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="createElder">Create</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="Edit Elder" :visible.sync="showEdit">
|
||||
<el-form :model="editForm" label-width="120px">
|
||||
<el-form-item label="Name"><el-input v-model="editForm.name"/></el-form-item>
|
||||
<el-form-item label="Gender"><el-input v-model="editForm.gender"/></el-form-item>
|
||||
<el-form-item label="ID Card"><el-input v-model="editForm.idCard"/></el-form-item>
|
||||
<el-form-item label="Birthday"><el-date-picker v-model="editForm.birthday" type="date"/></el-form-item>
|
||||
<el-form-item label="Room No"><el-input v-model="editForm.roomNo"/></el-form-item>
|
||||
<el-form-item label="Check In"><el-date-picker v-model="editForm.checkInDate" type="date"/></el-form-item>
|
||||
<el-form-item label="Care Level"><el-input v-model="editForm.careLevel"/></el-form-item>
|
||||
<el-form-item label="Status"><el-input v-model="editForm.status"/></el-form-item>
|
||||
<el-form-item label="Remark"><el-input v-model="editForm.remark"/></el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showEdit = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="updateElder">Save</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { eldersList, eldersCreate, eldersUpdate, eldersDelete } from "../../api";
|
||||
import { formatDate } from "../../utils/date";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
elders: [],
|
||||
showCreate: false,
|
||||
showEdit: false,
|
||||
form: { name: "", gender: "", idCard: "", birthday: "", roomNo: "", checkInDate: "", careLevel: "", status: "", remark: "" },
|
||||
editForm: {}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.load();
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
try {
|
||||
const res = await eldersList();
|
||||
this.elders = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
async createElder() {
|
||||
try {
|
||||
const payload = {
|
||||
...this.form,
|
||||
birthday: formatDate(this.form.birthday),
|
||||
checkInDate: formatDate(this.form.checkInDate)
|
||||
};
|
||||
await eldersCreate(payload);
|
||||
this.showCreate = false;
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "create failed");
|
||||
}
|
||||
},
|
||||
editElder(row) {
|
||||
this.editForm = { ...row };
|
||||
this.showEdit = true;
|
||||
},
|
||||
async updateElder() {
|
||||
try {
|
||||
const payload = {
|
||||
...this.editForm,
|
||||
birthday: formatDate(this.editForm.birthday),
|
||||
checkInDate: formatDate(this.editForm.checkInDate)
|
||||
};
|
||||
await eldersUpdate(payload);
|
||||
this.showEdit = false;
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "update failed");
|
||||
}
|
||||
},
|
||||
async deleteElder(row) {
|
||||
try {
|
||||
await eldersDelete(row.id);
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "delete failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
76
frontend/src/views/admin/Feedback.vue
Normal file
76
frontend/src/views/admin/Feedback.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Feedback</h3>
|
||||
<el-table :data="items" stripe>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="elderId" label="Elder ID" />
|
||||
<el-table-column prop="type" label="Type" />
|
||||
<el-table-column prop="content" label="Content" />
|
||||
<el-table-column prop="status" label="Status" width="120" />
|
||||
<el-table-column label="Actions" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" @click="openReply(scope.row)">Reply</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="Reply" :visible.sync="showReply">
|
||||
<el-form :model="replyForm" label-width="120px">
|
||||
<el-form-item label="Status">
|
||||
<el-select v-model="replyForm.status">
|
||||
<el-option label="New" value="NEW" />
|
||||
<el-option label="Processing" value="PROCESSING" />
|
||||
<el-option label="Closed" value="CLOSED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="Reply">
|
||||
<el-input v-model="replyForm.reply" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showReply = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="saveReply">Save</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { feedbackList, feedbackUpdate } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
showReply: false,
|
||||
replyForm: { id: null, status: "PROCESSING", reply: "" }
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.load();
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
try {
|
||||
const res = await feedbackList();
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
openReply(row) {
|
||||
this.replyForm = { id: row.id, status: row.status || "PROCESSING", reply: row.reply || "" };
|
||||
this.showReply = true;
|
||||
},
|
||||
async saveReply() {
|
||||
try {
|
||||
await feedbackUpdate(this.replyForm);
|
||||
this.showReply = false;
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "update failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
70
frontend/src/views/admin/Notices.vue
Normal file
70
frontend/src/views/admin/Notices.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Notices</h3>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<el-button type="primary" @click="showCreate = true">New Notice</el-button>
|
||||
</div>
|
||||
<el-table :data="items" stripe>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="title" label="Title" />
|
||||
<el-table-column prop="targetRole" label="Target" width="120" />
|
||||
<el-table-column prop="createdAt" label="Created" />
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="New Notice" :visible.sync="showCreate">
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="Title"><el-input v-model="form.title"/></el-form-item>
|
||||
<el-form-item label="Content"><el-input v-model="form.content" type="textarea"/></el-form-item>
|
||||
<el-form-item label="Target Role">
|
||||
<el-select v-model="form.targetRole">
|
||||
<el-option label="All" value="ALL" />
|
||||
<el-option label="Nurse" value="NURSE" />
|
||||
<el-option label="Family" value="FAMILY" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="Target User ID"><el-input v-model="form.targetUserId"/></el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showCreate = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="create">Create</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { noticeCreate, noticeList } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
items: [],
|
||||
showCreate: false,
|
||||
form: { title: "", content: "", targetRole: "ALL", targetUserId: "" }
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.load();
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
try {
|
||||
const res = await noticeList();
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
async create() {
|
||||
try {
|
||||
const payload = { ...this.form, targetUserId: this.form.targetUserId || null };
|
||||
await noticeCreate(payload);
|
||||
this.showCreate = false;
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "create failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
108
frontend/src/views/admin/Schedules.vue
Normal file
108
frontend/src/views/admin/Schedules.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Schedule Management</h3>
|
||||
<div style="margin: 12px 0; display:flex; gap: 12px;">
|
||||
<el-date-picker v-model="date" type="date" placeholder="Pick date" />
|
||||
<el-button type="primary" @click="load">Search</el-button>
|
||||
<el-button @click="showCreate = true">Add Schedule</el-button>
|
||||
</div>
|
||||
<el-table :data="schedules" stripe>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="nurseId" label="Nurse ID" width="100" />
|
||||
<el-table-column prop="date" label="Date" />
|
||||
<el-table-column prop="shift" label="Shift" />
|
||||
<el-table-column prop="task" label="Task" />
|
||||
<el-table-column label="Actions" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" @click="edit(scope.row)">Edit</el-button>
|
||||
<el-button size="mini" type="danger" @click="remove(scope.row)">Delete</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="Schedule" :visible.sync="showCreate">
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="Nurse ID"><el-input v-model="form.nurseId"/></el-form-item>
|
||||
<el-form-item label="Date"><el-date-picker v-model="form.date" type="date"/></el-form-item>
|
||||
<el-form-item label="Shift"><el-input v-model="form.shift"/></el-form-item>
|
||||
<el-form-item label="Task"><el-input v-model="form.task"/></el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showCreate = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="create">Save</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="Edit Schedule" :visible.sync="showEdit">
|
||||
<el-form :model="editForm" label-width="120px">
|
||||
<el-form-item label="Nurse ID"><el-input v-model="editForm.nurseId"/></el-form-item>
|
||||
<el-form-item label="Date"><el-date-picker v-model="editForm.date" type="date"/></el-form-item>
|
||||
<el-form-item label="Shift"><el-input v-model="editForm.shift"/></el-form-item>
|
||||
<el-form-item label="Task"><el-input v-model="editForm.task"/></el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showEdit = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="update">Save</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { schedulesByDate, scheduleCreate, scheduleUpdate, scheduleDelete } from "../../api";
|
||||
import { formatDate } from "../../utils/date";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
date: "",
|
||||
schedules: [],
|
||||
showCreate: false,
|
||||
showEdit: false,
|
||||
form: { nurseId: "", date: "", shift: "", task: "" },
|
||||
editForm: {}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
if (!this.date) return;
|
||||
try {
|
||||
const res = await schedulesByDate(formatDate(this.date));
|
||||
this.schedules = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
async create() {
|
||||
try {
|
||||
await scheduleCreate({ ...this.form, date: formatDate(this.form.date) });
|
||||
this.showCreate = false;
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "create failed");
|
||||
}
|
||||
},
|
||||
edit(row) {
|
||||
this.editForm = { ...row };
|
||||
this.showEdit = true;
|
||||
},
|
||||
async update() {
|
||||
try {
|
||||
await scheduleUpdate({ ...this.editForm, date: formatDate(this.editForm.date) });
|
||||
this.showEdit = false;
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "update failed");
|
||||
}
|
||||
},
|
||||
async remove(row) {
|
||||
try {
|
||||
await scheduleDelete(row.id);
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "delete failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
132
frontend/src/views/admin/Users.vue
Normal file
132
frontend/src/views/admin/Users.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<div style="display:flex; justify-content: space-between; align-items:center;">
|
||||
<h3>User Management</h3>
|
||||
<el-button type="primary" @click="showCreate = true">New User</el-button>
|
||||
</div>
|
||||
<div style="margin: 12px 0;">
|
||||
<el-select v-model="role" placeholder="role" @change="loadUsers">
|
||||
<el-option label="All" value=""></el-option>
|
||||
<el-option label="Admin" value="ADMIN"></el-option>
|
||||
<el-option label="Nurse" value="NURSE"></el-option>
|
||||
<el-option label="Family" value="FAMILY"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-table :data="users" stripe>
|
||||
<el-table-column prop="id" label="ID" width="80" />
|
||||
<el-table-column prop="username" label="Username" />
|
||||
<el-table-column prop="name" label="Name" />
|
||||
<el-table-column prop="role" label="Role" width="100" />
|
||||
<el-table-column prop="status" label="Status" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'">
|
||||
{{ scope.row.status === 1 ? 'Active' : 'Disabled' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Actions" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" @click="editUser(scope.row)">Edit</el-button>
|
||||
<el-button size="mini" @click="resetPwd(scope.row)">Reset</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog title="Create User" :visible.sync="showCreate">
|
||||
<el-form :model="createForm" label-width="120px">
|
||||
<el-form-item label="Username"><el-input v-model="createForm.username"/></el-form-item>
|
||||
<el-form-item label="Password"><el-input v-model="createForm.password" type="password"/></el-form-item>
|
||||
<el-form-item label="Name"><el-input v-model="createForm.name"/></el-form-item>
|
||||
<el-form-item label="Phone"><el-input v-model="createForm.phone"/></el-form-item>
|
||||
<el-form-item label="Role">
|
||||
<el-select v-model="createForm.role">
|
||||
<el-option label="Admin" value="ADMIN"/>
|
||||
<el-option label="Nurse" value="NURSE"/>
|
||||
<el-option label="Family" value="FAMILY"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showCreate = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="createUser">Create</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="Edit User" :visible.sync="showEdit">
|
||||
<el-form :model="editForm" label-width="120px">
|
||||
<el-form-item label="Name"><el-input v-model="editForm.name"/></el-form-item>
|
||||
<el-form-item label="Phone"><el-input v-model="editForm.phone"/></el-form-item>
|
||||
<el-form-item label="Status">
|
||||
<el-select v-model="editForm.status">
|
||||
<el-option label="Active" :value="1"/>
|
||||
<el-option label="Disabled" :value="0"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="showEdit = false">Cancel</el-button>
|
||||
<el-button type="primary" @click="updateUser">Save</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { adminUsers, adminCreateUser, adminUpdateUser, adminResetPassword } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
role: "",
|
||||
users: [],
|
||||
showCreate: false,
|
||||
showEdit: false,
|
||||
createForm: { username: "", password: "", name: "", phone: "", role: "NURSE" },
|
||||
editForm: { id: null, name: "", phone: "", status: 1 }
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.loadUsers();
|
||||
},
|
||||
methods: {
|
||||
async loadUsers() {
|
||||
try {
|
||||
const res = await adminUsers(this.role || null);
|
||||
this.users = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
async createUser() {
|
||||
try {
|
||||
await adminCreateUser(this.createForm);
|
||||
this.showCreate = false;
|
||||
this.loadUsers();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "create failed");
|
||||
}
|
||||
},
|
||||
editUser(row) {
|
||||
this.editForm = { id: row.id, name: row.name, phone: row.phone, status: row.status };
|
||||
this.showEdit = true;
|
||||
},
|
||||
async updateUser() {
|
||||
try {
|
||||
await adminUpdateUser(this.editForm);
|
||||
this.showEdit = false;
|
||||
this.loadUsers();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "update failed");
|
||||
}
|
||||
},
|
||||
async resetPwd(row) {
|
||||
try {
|
||||
await adminResetPassword(row.id, "123456");
|
||||
this.$message.success("reset to 123456");
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "reset failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
60
frontend/src/views/family/Bills.vue
Normal file
60
frontend/src/views/family/Bills.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Bills</h3>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<el-select v-model="elderId" placeholder="Select elder" @change="load">
|
||||
<el-option v-for="elder in elders" :key="elder.id" :label="elder.name" :value="elder.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-table :data="items" stripe>
|
||||
<el-table-column prop="month" label="Month" />
|
||||
<el-table-column prop="total" label="Total" />
|
||||
<el-table-column prop="status" label="Status" />
|
||||
<el-table-column label="Actions" width="160">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="primary" @click="pay(scope.row)" :disabled="scope.row.status === 'PAID'">
|
||||
Pay
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { familyElders, familyBills, familyPay } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { elders: [], elderId: "", items: [] };
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
const res = await familyElders();
|
||||
this.elders = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
if (!this.elderId) return;
|
||||
try {
|
||||
const res = await familyBills(this.elderId);
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
async pay(row) {
|
||||
try {
|
||||
await familyPay(row.id, { method: "ONLINE" });
|
||||
this.$message.success("paid");
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "pay failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
44
frontend/src/views/family/CareRecords.vue
Normal file
44
frontend/src/views/family/CareRecords.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Daily Care</h3>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<el-select v-model="elderId" placeholder="Select elder" @change="load">
|
||||
<el-option v-for="elder in elders" :key="elder.id" :label="elder.name" :value="elder.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-table :data="items" stripe>
|
||||
<el-table-column prop="recordTime" label="Time" />
|
||||
<el-table-column prop="content" label="Content" />
|
||||
<el-table-column prop="attachmentUrl" label="Attachment" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { familyElders, familyCareList } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { elders: [], elderId: "", items: [] };
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
const res = await familyElders();
|
||||
this.elders = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
if (!this.elderId) return;
|
||||
try {
|
||||
const res = await familyCareList(this.elderId);
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
6
frontend/src/views/family/Dashboard.vue
Normal file
6
frontend/src/views/family/Dashboard.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Family Dashboard</h3>
|
||||
<p>View elder status, bills, and send feedback.</p>
|
||||
</div>
|
||||
</template>
|
||||
29
frontend/src/views/family/Elders.vue
Normal file
29
frontend/src/views/family/Elders.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Elders</h3>
|
||||
<el-table :data="elders" stripe>
|
||||
<el-table-column prop="name" label="Name" />
|
||||
<el-table-column prop="gender" label="Gender" width="80" />
|
||||
<el-table-column prop="roomNo" label="Room" width="100" />
|
||||
<el-table-column prop="careLevel" label="Care Level" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { familyElders } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { elders: [] };
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
const res = await familyElders();
|
||||
this.elders = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
57
frontend/src/views/family/Feedback.vue
Normal file
57
frontend/src/views/family/Feedback.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Feedback</h3>
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="Elder">
|
||||
<el-select v-model="form.elderId" placeholder="Select elder">
|
||||
<el-option v-for="elder in elders" :key="elder.id" :label="elder.name" :value="elder.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="Type">
|
||||
<el-select v-model="form.type">
|
||||
<el-option label="Suggestion" value="SUGGESTION" />
|
||||
<el-option label="Complaint" value="COMPLAINT" />
|
||||
<el-option label="Praise" value="PRAISE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="Content">
|
||||
<el-input v-model="form.content" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Rating">
|
||||
<el-input v-model="form.rating" />
|
||||
</el-form-item>
|
||||
<el-button type="primary" @click="submit">Submit</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { familyElders, familyFeedback } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
elders: [],
|
||||
form: { elderId: "", type: "SUGGESTION", content: "", rating: "" }
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
const res = await familyElders();
|
||||
this.elders = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async submit() {
|
||||
try {
|
||||
await familyFeedback(this.form);
|
||||
this.$message.success("submitted");
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "submit failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
46
frontend/src/views/family/HealthRecords.vue
Normal file
46
frontend/src/views/family/HealthRecords.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Health Records</h3>
|
||||
<div style="margin-bottom: 12px;">
|
||||
<el-select v-model="elderId" placeholder="Select elder" @change="load">
|
||||
<el-option v-for="elder in elders" :key="elder.id" :label="elder.name" :value="elder.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
<el-table :data="items" stripe>
|
||||
<el-table-column prop="recordTime" label="Time" />
|
||||
<el-table-column prop="temperature" label="Temp" />
|
||||
<el-table-column prop="bpSystolic" label="BP S" />
|
||||
<el-table-column prop="bpDiastolic" label="BP D" />
|
||||
<el-table-column prop="heartRate" label="HR" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { familyElders, familyHealthList } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { elders: [], elderId: "", items: [] };
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
const res = await familyElders();
|
||||
this.elders = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
if (!this.elderId) return;
|
||||
try {
|
||||
const res = await familyHealthList(this.elderId);
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
28
frontend/src/views/family/Notices.vue
Normal file
28
frontend/src/views/family/Notices.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Notices</h3>
|
||||
<el-table :data="items" stripe>
|
||||
<el-table-column prop="title" label="Title" />
|
||||
<el-table-column prop="content" label="Content" />
|
||||
<el-table-column prop="createdAt" label="Created" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { familyNoticeList } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { items: [] };
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
const res = await familyNoticeList();
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
72
frontend/src/views/nurse/CareRecords.vue
Normal file
72
frontend/src/views/nurse/CareRecords.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Care Records</h3>
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="Elder ID"><el-input v-model="form.elderId"/></el-form-item>
|
||||
<el-form-item label="Content"><el-input v-model="form.content" type="textarea"/></el-form-item>
|
||||
<el-form-item label="Record Time"><el-date-picker v-model="form.recordTime" type="datetime"/></el-form-item>
|
||||
<el-form-item label="Attachment">
|
||||
<el-upload :http-request="upload" :show-file-list="false">
|
||||
<el-button size="mini">Upload</el-button>
|
||||
</el-upload>
|
||||
<div v-if="form.attachmentUrl" style="margin-top: 6px;">
|
||||
Uploaded: {{ form.attachmentUrl }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-button type="primary" @click="create">Save</el-button>
|
||||
</el-form>
|
||||
|
||||
<div style="margin-top: 16px;">
|
||||
<el-input v-model="filterElderId" placeholder="Elder ID" style="width: 200px;" />
|
||||
<el-button @click="load" style="margin-left: 8px;">Search</el-button>
|
||||
<el-table :data="items" stripe style="margin-top: 12px;">
|
||||
<el-table-column prop="elderId" label="Elder ID" width="100" />
|
||||
<el-table-column prop="content" label="Content" />
|
||||
<el-table-column prop="recordTime" label="Record Time" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { nurseCareCreate, nurseCareList, uploadFile } from "../../api";
|
||||
import { formatDateTime } from "../../utils/date";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: { elderId: "", content: "", recordTime: "", attachmentUrl: "" },
|
||||
filterElderId: "",
|
||||
items: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async upload(option) {
|
||||
try {
|
||||
const res = await uploadFile(option.file);
|
||||
this.form.attachmentUrl = res.data.data.url;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "upload failed");
|
||||
}
|
||||
},
|
||||
async create() {
|
||||
try {
|
||||
const payload = { ...this.form, recordTime: formatDateTime(this.form.recordTime) };
|
||||
await nurseCareCreate(payload);
|
||||
this.$message.success("saved");
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "save failed");
|
||||
}
|
||||
},
|
||||
async load() {
|
||||
if (!this.filterElderId) return;
|
||||
try {
|
||||
const res = await nurseCareList(this.filterElderId);
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
6
frontend/src/views/nurse/Dashboard.vue
Normal file
6
frontend/src/views/nurse/Dashboard.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Nurse Dashboard</h3>
|
||||
<p>Use the menu to manage schedules and records.</p>
|
||||
</div>
|
||||
</template>
|
||||
52
frontend/src/views/nurse/Handovers.vue
Normal file
52
frontend/src/views/nurse/Handovers.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Handovers</h3>
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item label="Date"><el-date-picker v-model="form.date" type="date"/></el-form-item>
|
||||
<el-form-item label="Content"><el-input v-model="form.content" type="textarea"/></el-form-item>
|
||||
<el-button type="primary" @click="create">Save</el-button>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="items" stripe style="margin-top: 12px;">
|
||||
<el-table-column prop="date" label="Date" />
|
||||
<el-table-column prop="content" label="Content" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { nurseHandoverCreate, nurseHandoverList } from "../../api";
|
||||
import { formatDate } from "../../utils/date";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: { date: "", content: "" },
|
||||
items: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.load();
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
try {
|
||||
const res = await nurseHandoverList();
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
},
|
||||
async create() {
|
||||
try {
|
||||
const payload = { ...this.form, date: formatDate(this.form.date) };
|
||||
await nurseHandoverCreate(payload);
|
||||
this.$message.success("saved");
|
||||
this.load();
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "save failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
67
frontend/src/views/nurse/HealthRecords.vue
Normal file
67
frontend/src/views/nurse/HealthRecords.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Health Records</h3>
|
||||
<el-form :model="form" label-width="140px">
|
||||
<el-form-item label="Elder ID"><el-input v-model="form.elderId"/></el-form-item>
|
||||
<el-form-item label="Temperature"><el-input v-model="form.temperature"/></el-form-item>
|
||||
<el-form-item label="Blood Pressure">
|
||||
<div style="display:flex; gap:8px;">
|
||||
<el-input v-model="form.bpSystolic" placeholder="Systolic" />
|
||||
<el-input v-model="form.bpDiastolic" placeholder="Diastolic" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="Heart Rate"><el-input v-model="form.heartRate"/></el-form-item>
|
||||
<el-form-item label="Note"><el-input v-model="form.note"/></el-form-item>
|
||||
<el-form-item label="Record Time"><el-date-picker v-model="form.recordTime" type="datetime"/></el-form-item>
|
||||
<el-button type="primary" @click="create">Save</el-button>
|
||||
</el-form>
|
||||
|
||||
<div style="margin-top: 16px;">
|
||||
<el-input v-model="filterElderId" placeholder="Elder ID" style="width: 200px;" />
|
||||
<el-button @click="load" style="margin-left: 8px;">Search</el-button>
|
||||
<el-table :data="items" stripe style="margin-top: 12px;">
|
||||
<el-table-column prop="elderId" label="Elder ID" width="100" />
|
||||
<el-table-column prop="temperature" label="Temp" />
|
||||
<el-table-column prop="bpSystolic" label="BP S" />
|
||||
<el-table-column prop="bpDiastolic" label="BP D" />
|
||||
<el-table-column prop="heartRate" label="HR" />
|
||||
<el-table-column prop="recordTime" label="Record Time" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { nurseHealthCreate, nurseHealthList } from "../../api";
|
||||
import { formatDateTime } from "../../utils/date";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
form: { elderId: "", temperature: "", bpSystolic: "", bpDiastolic: "", heartRate: "", note: "", recordTime: "" },
|
||||
filterElderId: "",
|
||||
items: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async create() {
|
||||
try {
|
||||
const payload = { ...this.form, recordTime: formatDateTime(this.form.recordTime) };
|
||||
await nurseHealthCreate(payload);
|
||||
this.$message.success("saved");
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "save failed");
|
||||
}
|
||||
},
|
||||
async load() {
|
||||
if (!this.filterElderId) return;
|
||||
try {
|
||||
const res = await nurseHealthList(this.filterElderId);
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
28
frontend/src/views/nurse/Notices.vue
Normal file
28
frontend/src/views/nurse/Notices.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>Notices</h3>
|
||||
<el-table :data="items" stripe>
|
||||
<el-table-column prop="title" label="Title" />
|
||||
<el-table-column prop="content" label="Content" />
|
||||
<el-table-column prop="createdAt" label="Created" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { nurseNoticeList } from "../../api";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { items: [] };
|
||||
},
|
||||
async created() {
|
||||
try {
|
||||
const res = await nurseNoticeList();
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
36
frontend/src/views/nurse/Schedules.vue
Normal file
36
frontend/src/views/nurse/Schedules.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="page-card">
|
||||
<h3>My Schedule</h3>
|
||||
<div style="margin-bottom: 12px; display:flex; gap: 12px;">
|
||||
<el-date-picker v-model="date" type="date" placeholder="Pick date" />
|
||||
<el-button type="primary" @click="load">Search</el-button>
|
||||
</div>
|
||||
<el-table :data="items" stripe>
|
||||
<el-table-column prop="date" label="Date" />
|
||||
<el-table-column prop="shift" label="Shift" />
|
||||
<el-table-column prop="task" label="Task" />
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { nurseSchedules } from "../../api";
|
||||
import { formatDate } from "../../utils/date";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { date: "", items: [] };
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
if (!this.date) return;
|
||||
try {
|
||||
const res = await nurseSchedules(formatDate(this.date));
|
||||
this.items = res.data.data;
|
||||
} catch (e) {
|
||||
this.$message.error(e.message || "load failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
15
frontend/vue.config.js
Normal file
15
frontend/vue.config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
devServer: {
|
||||
port: 5173,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:8080",
|
||||
changeOrigin: true
|
||||
},
|
||||
"/files": {
|
||||
target: "http://localhost:8080",
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
253
张宇-开题报告.md
Normal file
253
张宇-开题报告.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# 大连科技学院
|
||||
|
||||
# 毕业设计(论文)开题报告
|
||||
|
||||
学院 信息科学与技术学院
|
||||
|
||||
专业班级 网络工程(专升本)24-1
|
||||
|
||||
学生姓名 张宇
|
||||
|
||||
学生学号 2406490115
|
||||
|
||||
指导教师 张殿桢
|
||||
|
||||
导师职称 工程师
|
||||
|
||||
# 1 选题的意义和研究现状
|
||||
|
||||
# 1.1 选题的意义
|
||||
|
||||
当前,全球性的人口老龄化浪潮已成为一个不容忽视的议题,我国在这一趋势下面临的形势尤为突出。根据官方发布的最新统计,截至2023年末,我国60岁及以上的人口规模已达2.97亿,占据了总人口的 $21.1\%$ ;与此同时,65岁及以上的老年群体也已增至2.17亿,占比达到 $15.4\%$ 。这一系列数据直观地表明,我国社会正承受着日益沉重的人口结构压力,如何应对老龄化已成为一项必须正视的挑战。随着社会加速迈向深度老龄化阶段,养老难题如何破解,便顺理成章地成为各界关注的焦点。基于此,探索并运用智能化养老方案来应对这一现实需求,就显得格外迫切。
|
||||
|
||||
本研究聚焦养老院管理平台的设计与实现,力求借助现代信息技术手段,为养老机构的管理效率与服务水准带来根本性的改进。传统的养老院管理模式,严重依赖纸质台账和人工协调,在效率、精准度和透明度上,已经远远无法跟上现代养老服务的需求。
|
||||
|
||||
本课题为了解决这一系列管理难题,比如流程繁琐、信息孤岛现象严重、数据统计滞后,以及服务响应不够迅速等,核心目标便是开发一套综合性的信息系统。该系统旨在将
|
||||
|
||||
养老院运营中的“人、财、物、服务”这四大核心要素进行全面的数字化整合,最终能够助力养老机构实现“降本、提质、增效”的数字化转型,从而彻底打破传统管理模式下的信息壁垒,迈向业务在线化、管理可视化和决策数据化的新阶段。
|
||||
|
||||
本课题的技术价值在于,通过基于前后端分离架构的智慧养老系统设计与实现,为同类平台的开发提供了具有参考价值的实践方案与架构范例。在实践层面,“颐云养老院管理平台”的实施将有效优化养老机构的内部管理流程,提升工作人员协同效率与服务响应速度。同时,系统通过构建面向家属的客户端功能,增强了服务过程的透明度与沟通效率,有助于提升家属信任度与服务满意度。整体上,本系统为应对人口老龄化背景下养老服务资源优化与服务模式创新,提供了可行的技术路径与系统化支持。
|
||||
|
||||
# 1.2本课题所涉及问题在国内外设计或研究的现状
|
||||
|
||||
# 1.2.1 国内外研究现状
|
||||
|
||||
在国内,相关研究的焦点普遍集中在如何推动养老服务走向信息化与智能化。无论是学术界还是产业界,都有大量企业投身于各类养老管理信息系统的开发之中。这些系统的功能设计通常相当全面,从老人档案管理、日常健康监测,到护理计划制定乃至费用结算等,其核心目的都是为了提升养老机构的运营效率。举个例子,一些前沿研究已经开
|
||||
|
||||
始引入物联网(IoT)技术,对老人的生命体征和活动状态进行全天候的实时监控,从而能够做到主动预警和快速响应。与此同时,另一个值得注意的趋势是基于大数据分析的应用正悄然兴起,通过对老人健康数据和行为模式的深度挖掘,系统能够为他们量身定制个性化的护理与康复建议。
|
||||
|
||||
在国外,他们在养老科技领域的探索不仅起步更早,体系也已经发展得更为成熟。可以说,国际上的研究视野早已不局限于单纯的技术功能实现,一个很明显的趋势是,他们更加注重服务的个性化、人文关怀与智能化水平。许多先进的养老系统设计理念,都倾向于整合更为全面的服务维度,将重心从满足基本的生理照护,进一步拓展到关怀老年人的心理健康与社会交往需求。同时,在人工智能(AI)、物联网等前沿技术的应用上,其研究的触角也伸得更深,覆盖了从无感化环境监测到智能交互式陪护等多个层面,其最终目的都是为了给老年人营造一个更安全、便捷且富有人文关怀的居住环境。此外,国外研究还有一个普遍的特点,那就是极其强调用户体验(UX),尤其是在人机交互设计方面,会充分考虑到老年群体的特殊性,以确保技术足够普惠和易用。
|
||||
|
||||
综合来看,无论国内还是国外,关于养老院管理平台的研究方向可以说正在趋于一致,核心目标都是希望借助信息化和智能化的手段,来提升管理效率与服务质量。区别在
|
||||
|
||||
于,国内的研究更侧重于快速跟进和应用最新的技术,而国外则在服务的深度、广度以及
|
||||
|
||||
人文关怀这些方面,积累了更为丰富的经验。“颐云养老院管理平台”的设计,恰好顺应
|
||||
|
||||
了这一发展趋势。它的目标很明确,就是希望能结合国内外先进的经验,并利用当下成熟
|
||||
|
||||
稳定的 SpringBoot 与 Vue 技术栈,为我国构建一个既符合自身国情、又具备全面功能且
|
||||
|
||||
易于操作的养老院管理解决方案。综合来看,无论国内还是国外,关于养老院管理平台的
|
||||
|
||||
研究方向可以说正在趋于一致,核心目标都是希望借助信息化和智能化的手段,来提升
|
||||
|
||||
管理效率与服务质量。区别在于,国内的研究更侧重于快速跟进和应用最新的技术,而国
|
||||
|
||||
外则在服务的深度、广度以及人文关怀这些方面,积累了更为丰富的经验。“颐云养老院
|
||||
|
||||
管理平台”的设计,恰好顺应了这一发展趋势。它的目标很明确,就是希望能结合国内外
|
||||
|
||||
先进的经验,并利用当下成熟稳定的 SpringBoot 与 Vue 技术栈,为社会构建一个既符合
|
||||
|
||||
自身国情、又具备全面功能且易于操作的养老院管理解决方案。
|
||||
|
||||
# 2 课题设计或研究的内容、预期目标和实施计划
|
||||
|
||||
# 2.1 要设计或研究的主要内容方案论证分析
|
||||
|
||||
# 2.1.1 主要研究内容
|
||||
|
||||
本课题旨在设计并实现一个名为“颐云养老院管理平台”的综合性信息系统。系统采用B/S架构,基于SpringBoot + Vue + MySql + ElementUI技术栈进行开发。平台主要服务于管理员、护工和家属三类用户,通过为不同角色提供精准的功能模块,实现对养老院业务流程的全面数字化管理。具体功能模块划分如下:
|
||||
|
||||
# (1) 护工用户功能
|
||||
|
||||
登录:护工通过唯一的账号和密码登录系统,访问其权限范围内的功能。
|
||||
|
||||
我的排班:查看个人的工作班次、时间及具体任务安排,便于护工提前规划工作。
|
||||
|
||||
护理记录:记录对长者的日常护理内容,如喂食、翻身、清洁等,并形成可追溯的服务档案。
|
||||
|
||||
健康监测:为所负责的长者录入生命体征数据(如体温、血压、心率),并记录异常情况。
|
||||
|
||||
交班报告:在轮班交接时,填写交接班记录,说明重点关注的长者情况和未完成事项。
|
||||
|
||||
通知中心:查看由管理员发布的内部通知、培训安排等重要信息。
|
||||
|
||||
# (2) 家属用户功能
|
||||
|
||||
注册:家属在系统中注册账号,并输入长者的身份证号进行关系绑定。
|
||||
|
||||
登录:家属通过唯一的账号和密码登录系统,访问其权限范围内的功能。
|
||||
|
||||
亲人档案:查看亲属在养老院的基本档案、入住信息和护理等级。
|
||||
|
||||
每日动态:实时查阅由护工录入的亲属每日健康数据和护理记录,远程了解老人健康状况。
|
||||
|
||||
账单与支付:查询月度账单(如床位费、护理费、餐饮费等),并支持在线完成支付。
|
||||
|
||||
服务反馈:对养老院的服务提出意见、建议或投诉,并可以对护工的服务进行评价。
|
||||
|
||||
# (3) 管理员用户功能模块
|
||||
|
||||
运营管理:总览平台核心数据:在住长者数、员工数、收入情况等统计信息。
|
||||
|
||||
账户管理:管理员可以对员工以及家属的账号进行信息修改,禁用等操作。
|
||||
|
||||
财务管理:设置收费项目标准,生成每位老人的月度账单,并管理缴费记录。
|
||||
|
||||
沟通与改进:查看并处理来自家属的投诉与评价,及时做出响应和改进,并对处理结果进行记录。
|
||||
|
||||
排班管理:为员工安排工作班次,支持在线调整与发布。
|
||||
|
||||
档案管理:对长者的档案信息进行管理,确保数据完整且准确无误。
|
||||
|
||||
总体功能图如图1所示。
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
图1总体功能图
|
||||
|
||||
|
||||
# 2.1.2 研究方案及可行性分析
|
||||
|
||||
本研究的核心方案在于设计并实现一个采用前后端分离架构的Web应用。在前端层面,我们将利用Vue框架与ElementUI组件库相结合,致力于构建一个直观、友好的用户交互界面。而在后端,则选用Java语言及主流的SpringBoot框架,集中处理系统的核心业务逻辑、完成数据持久化,并开发相应的API接口。数据库方面,我们选择了开源且性能稳定的MySQL来承担数据存储任务。在此基础上,前后端将通过RESTful API规范进行数据通信,以此确保二者之间的高效协同与信息交换。
|
||||
|
||||
本研究的实施将严格遵循标准的软件工程规范,以确保项目的科学性与高质量。我们将首先启动详尽的需求分析阶段,通过深入的实地调研和多方沟通,旨在精准捕获并明确定义各角色的功能需求、业务流程及非功能性需求,从而为系统构建清晰、无歧义的目标蓝图。需求明确后,工作将平滑过渡到系统设计阶段,该环节将涵盖软件架构的设
|
||||
|
||||
计、数据库结构的设计与优化(以E-R图为核心工具),形成指导开发工作的技术蓝图。
|
||||
|
||||
为最终保障系统的稳定与可靠,项目后期将执行严谨的系统测试策略,覆盖单元、集成和
|
||||
|
||||
功能等多个层面,确保从代码底层到业务顶层的层层质量保障。对于测试中发现的任何
|
||||
|
||||
缺陷,都将建立闭环管理机制,进行彻底的修复与回归验证,确保最终交付的系统不仅在
|
||||
|
||||
功能上满足预期,更在质量上达到工业级应用标准。
|
||||
|
||||
# 2.1.3 可行性分析
|
||||
|
||||
# (1) 技术可行性
|
||||
|
||||
本系统拟采用的技术均为当前业界主流且成熟的技术。Java语言拥有庞大的开发者
|
||||
|
||||
社区和丰富的生态系统。SpringBoot 框架极大地简化了 Spring 应用的搭建和开发过程,
|
||||
|
||||
能快速构建稳定可靠的后端服务。Vue 作为渐进式 JavaScript 框架, 配合 ElementUI, 能够
|
||||
|
||||
高效地开发出现代化、响应式的用户界面。MySQL是应用最广泛的关系型数据库之一,
|
||||
|
||||
这些技术均有完善的官方文档和丰富的开源资源,开发难度可控,因此技术上完全可行。
|
||||
|
||||
# (2) 经济可行性
|
||||
|
||||
本系统在开发过程中主要采用了一系列开源及免费工具,包括 Java 开发环境(JDK)、SpringBoot 后端框架、Vue 前端框架、MySQL 关系型数据库以及 Visual Studio
|
||||
|
||||
Code 编辑器等,有效避免了昂贵的商业软件授权支出。而在部署层面,所涉及的服务器
|
||||
|
||||
硬件与网络带宽资源,对于大多数中小型养老机构而言,在现有预算内亦具备可操作性。
|
||||
|
||||
从生命周期成本与回报的角度综合评估,该项目具备较高的投入产出比。因此,本课题在
|
||||
|
||||
经济上是可行的。
|
||||
|
||||
# (3) 社会可行性
|
||||
|
||||
随着我国“积极应对人口老龄化”国家战略的推进和“智慧养老”概念的普及,利
|
||||
|
||||
用信息技术提升养老服务质量已成为社会共识。本平台的设计紧密贴合养老院的实际运
|
||||
|
||||
营需求,旨在解决管理痛点,提升服务水平,符合社会发展的趋势。因此,该项目具有显
|
||||
|
||||
著的社会价值和广泛的应用前景,社会可行性高。
|
||||
|
||||
# 2.2本课题选题特色及预期的目标
|
||||
|
||||
本课题的特色在于其高度的针对性和实用性。为管理员、护工、家属三个关键角色量
|
||||
|
||||
身定制功能。采用成熟的 SpringBoot + Vue 前后端分离技术架构,确保了系统的高性能、
|
||||
|
||||
高可维护性和良好的可扩展性。项目致力于打破传统养老院管理中的“信息孤岛”,实
|
||||
|
||||
现业务流程的在线化、管理数据的可视化和运营决策的数据化支持。
|
||||
|
||||
预期目标是通过系统的开发与应用,实现养老院日常管理的数字化和智能化,优化
|
||||
|
||||
服务流程,提升老年人照护质量和家属的满意度,推动养老服务的现代化发展。
|
||||
|
||||
# 2.3 本课题实施计划
|
||||
|
||||
<table><tr><td>周数</td><td>进度计划</td></tr><tr><td>第1周</td><td>确定毕业设计题目,在网络上对“养老院管理平台”进行调研</td></tr><tr><td>第2周</td><td>根据前期的调研情况,查阅相关资料完成开题报告撰写</td></tr><tr><td>第3周</td><td>选择与课题相关的外文文献,完成外文翻译。进行前期资料自查,进行系统可行性分析和需求分析</td></tr><tr><td>第4周</td><td>完成毕设前期检查。依据系统功能需求和业务流程分析,完成用例图和用例说明</td></tr><tr><td>第5周</td><td>进行系统分析,以用例图为基础进行类图、活动图和顺序图的绘制,确保系统的一致性和可维护性</td></tr><tr><td>第6周</td><td>完成数据库设计、界面设计,根据反馈意见进行修改</td></tr><tr><td>第7周</td><td>系统实现,按功能模块进行编码</td></tr><tr><td>第8周</td><td>完成毕设中期检查。系统实现,按功能模块进行编码</td></tr><tr><td>第9周</td><td>系统测试,测试本系统各业务功能运行是否正常,验证功能需求是否都符合规范要求。完成论文主体部分</td></tr><tr><td>第10周</td><td>按照系统测试结果修改代码完善功能,并完成论文剩余相关内容编写</td></tr><tr><td>第11周</td><td>提交论文初稿,根据反馈意见修改论文</td></tr><tr><td>第12周</td><td>继续修改论文,完成论文查重稿定稿。检查系统功能,为软件验收做好准备</td></tr><tr><td>第13周</td><td>进行软件验收,参加校级论文查重,根据论文内容制作答辩PPT</td></tr><tr><td>第14周</td><td>进行毕业设计答辩,并按照答辩组意见修改论文定稿,完成毕设资料存档</td></tr></table>
|
||||
|
||||
# 3 主要参考文献
|
||||
|
||||
|
||||
|
||||
[1] 吴秋燕, 赵杰, 邹慧淋等. 智能家庭养老系统的设计与实现[J], 物联网技术, 2023, 13(09): 111-113.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[2] 张琴.养老院智能管理系统的设计与实现[J],信息与电脑,2022,34(13):158-160.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[3], [4] 魏娇, 白磊. 基于智能数字化的智慧养老管理系统设计[J], 鞋类工艺与设计, 2022, 2(24):168-170.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[5] 王曼维, 杨荻, 李岩等. 基于 SpringBoot 框架的智慧医疗问诊系统设计与实现[J], 中国医学装备, 2022, 19(03): 133-136.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[6] 单树倩,任佳勋.基于 SpringBoot 和 Vue 框架的数据库原理网站设计与实现[J],电脑知识与技术:学术版,2021,17(30):40-41.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[7] Evaluating the impact of retirement leisure planning on retirement satisfaction and vitality: a mixed-method study[J]. ANNALS OF LEISURE RESEARCH, 2023, 26(01): 140-161.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[8] 陈菲.老年健康服务产业高质量发展战略路径研究[M],重庆大学出版社:202302.164.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[9] 马岚.公益性和产业化相结合的养老服务模式研究[M],南京大学出版社:202107.196.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[10] 邹平吉. JavaWeb 文化遗产数字化信息管理平台[J], 数字技术与应用, 2025(1):202-204.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[11] 郭佳丽.H养老院医养信息系统的设计与实现[D],首都经济贸易大学,2022.
|
||||
|
||||
Reference in New Issue
Block a user