重新提交

This commit is contained in:
LAPTOP-I47JE7I7\Lenovo
2026-04-08 10:56:37 +08:00
parent d130742ad9
commit e45549ec5a
245 changed files with 44907 additions and 0 deletions

28
.classpath Normal file
View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="module" value="true"/>
<attribute name="owner.project.facets" value="java"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v9.0">
<attributes>
<attribute name="owner.project.facets" value="jst.web"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/lombok.jar"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/HikariCP-7.0.2-javadoc.jar"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/slf4j-api-1.7.36-javadoc.jar"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/logback-classic-1.2.11-javadoc.jar"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/logback-core-1.2.11-javadoc.jar"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/jackson-core-2.15.2.jar"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/jackson-annotations-2.15.2.jar"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/jackson-databind-2.15.2.jar"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/jackson-datatype-jsr310-2.15.2.jar"/>
<classpathentry kind="lib" path="WebContent/WEB-INF/lib/mysql-connector-java-8.0.30.jar"/>
<classpathentry kind="output" path="build/classes"/>
</classpath>

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

25
.idea/artifacts/LM_Web_exploded.xml generated Normal file
View File

@@ -0,0 +1,25 @@
<component name="ArtifactManager">
<artifact type="exploded-war" name="LM:Web exploded">
<output-path>$PROJECT_DIR$/classes/artifacts/LM_Web_exploded</output-path>
<root id="root">
<element id="directory" name="WEB-INF">
<element id="directory" name="classes">
<element id="module-output" name="LM" />
</element>
<element id="directory" name="lib">
<element id="library" level="module" name="mysql-connector-java-8.0.30.jar" module-name="LM" />
<element id="library" level="module" name="jackson-datatype-jsr310-2.15.2.jar" module-name="LM" />
<element id="library" level="module" name="jackson-databind-2.15.2.jar" module-name="LM" />
<element id="library" level="module" name="jackson-annotations-2.15.2.jar" module-name="LM" />
<element id="library" level="module" name="jackson-core-2.15.2.jar" module-name="LM" />
<element id="library" level="module" name="logback-core-1.2.11-javadoc.jar" module-name="LM" />
<element id="library" level="module" name="logback-classic-1.2.11-javadoc.jar" module-name="LM" />
<element id="library" level="module" name="slf4j-api-1.7.36-javadoc.jar" module-name="LM" />
<element id="library" level="module" name="lombok.jar" module-name="LM" />
<element id="library" level="module" name="HikariCP-7.0.2-javadoc.jar" module-name="LM" />
</element>
</element>
<element id="javaee-facet-resources" facet="LM/web/Web" />
</root>
</artifact>
</component>

8
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
</annotationProcessing>
</component>
</project>

View File

@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="commons-dbutils-1.8.1">
<CLASSES>
<root url="jar://$PROJECT_DIR$/WebContent/WEB-INF/lib/commons-dbutils-1.8.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

9
.idea/libraries/commons_logging.xml generated Normal file
View File

@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="commons-logging">
<CLASSES>
<root url="jar://$PROJECT_DIR$/WebContent/WEB-INF/lib/commons-logging.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="commons-logging-1.1.1">
<CLASSES>
<root url="jar://$PROJECT_DIR$/WebContent/WEB-INF/lib/commons-logging-1.1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

9
.idea/libraries/druid_1_2_24.xml generated Normal file
View File

@@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="druid-1.2.24">
<CLASSES>
<root url="jar://$PROJECT_DIR$/WebContent/WEB-INF/lib/druid-1.2.24.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

6
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/LM.iml" filepath="$PROJECT_DIR$/LM.iml" />
</modules>
</component>
</project>

10
.idea/webContexts.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebContextManager">
<option name="state">
<map>
<entry key="file://$PROJECT_DIR$/WebContent/view/Reader.jsp" value="file://$PROJECT_DIR$/WebContent/view" />
</map>
</option>
</component>
</project>

31
.project Normal file
View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>LM</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>

12
.settings/.jsdtscope Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="**/bower_components/*|**/node_modules/*|**/*.min.js" kind="src" path="WebContent"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
<attributes>
<attribute name="hide" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
<classpathentry kind="output" path=""/>
</classpath>

View File

@@ -0,0 +1,10 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=11

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="LM">
<wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
<property name="context-root" value="LM"/>
<!-- 必须是「项目根下的 build/classes」不能写成 /LM/build/classes否则类文件不会进 WEB-INF/classesTomcat 会 ClassNotFound -->
<property name="java-output-path" value="/build/classes"/>
</wb-module>
</project-modules>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<runtime name="Apache Tomcat v9.0"/>
<fixed facet="java"/>
<fixed facet="wst.jsdt.web"/>
<fixed facet="jst.web"/>
<installed facet="java" version="1.8"/>
<installed facet="jst.web" version="4.0"/>
<installed facet="wst.jsdt.web" version="1.0"/>
</faceted-project>

View File

@@ -0,0 +1 @@
org.eclipse.wst.jsdt.launching.baseBrowserLibrary

View File

@@ -0,0 +1 @@
Window

170
LM.iml Normal file
View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="EclipseModuleManager">
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/lombok.jar!/" />
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/HikariCP-7.0.2-javadoc.jar!/" />
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/slf4j-api-1.7.36-javadoc.jar!/" />
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/logback-classic-1.2.11-javadoc.jar!/" />
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/logback-core-1.2.11-javadoc.jar!/" />
<libelement value="file://WebContent/WEB-INF/lib/jackson-core-3.1.0-rc1-javadoc.jar" />
<libelement value="file://WebContent/WEB-INF/lib/jackson-databind-3.1.0-javadoc.jar" />
<libelement value="file://WebContent/WEB-INF/lib/jackson-annotations-2.15.1-javadoc.jar" />
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/jackson-core-2.15.2.jar!/" />
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/jackson-annotations-2.15.2.jar!/" />
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/jackson-databind-2.15.2.jar!/" />
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/jackson-datatype-jsr310-2.15.2.jar!/" />
<libelement value="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/mysql-connector-java-8.0.30.jar!/" />
<conelement value="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v9.0" />
<conelement value="org.eclipse.jst.j2ee.internal.web.container" />
<conelement value="org.eclipse.jst.j2ee.internal.module.container" />
<src_description expected_position="0">
<src_folder value="file://$MODULE_DIR$/src" expected_position="0" />
</src_description>
</component>
<component name="FacetManager">
<facet type="web" name="Web">
<configuration>
<descriptors>
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/WebContent/WEB-INF/web.xml" />
</descriptors>
<webroots>
<root url="file://$MODULE_DIR$/WebContent" relative="/" />
</webroots>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/build/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="library" name="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v9.0" level="application" />
<orderEntry type="library" name="org.eclipse.jst.j2ee.internal.web.container" level="application" />
<orderEntry type="library" name="org.eclipse.jst.j2ee.internal.module.container" level="application" />
<orderEntry type="module-library">
<library name="lombok.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/lombok.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="HikariCP-7.0.2-javadoc.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/HikariCP-7.0.2-javadoc.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="slf4j-api-1.7.36-javadoc.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/slf4j-api-1.7.36-javadoc.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="logback-classic-1.2.11-javadoc.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/logback-classic-1.2.11-javadoc.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="logback-core-1.2.11-javadoc.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/logback-core-1.2.11-javadoc.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="jackson-core-3.1.0-rc1-javadoc.jar">
<CLASSES>
<root url="file://WebContent/WEB-INF/lib/jackson-core-3.1.0-rc1-javadoc.jar" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="jackson-databind-3.1.0-javadoc.jar">
<CLASSES>
<root url="file://WebContent/WEB-INF/lib/jackson-databind-3.1.0-javadoc.jar" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="jackson-annotations-2.15.1-javadoc.jar">
<CLASSES>
<root url="file://WebContent/WEB-INF/lib/jackson-annotations-2.15.1-javadoc.jar" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="jackson-core-2.15.2.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/jackson-core-2.15.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="jackson-annotations-2.15.2.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/jackson-annotations-2.15.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="jackson-databind-2.15.2.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/jackson-databind-2.15.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="jackson-datatype-jsr310-2.15.2.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/jackson-datatype-jsr310-2.15.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="mysql-connector-java-8.0.30.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/WebContent/WEB-INF/lib/mysql-connector-java-8.0.30.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
<orderEntry type="library" name="commons-dbutils-1.8.1" level="project" />
<orderEntry type="library" name="commons-logging" level="project" />
<orderEntry type="library" name="druid-1.2.24" level="project" />
<orderEntry type="library" name="commons-logging-1.1.1" level="project" />
<orderEntry type="library" name="Tomcat 9.0.115" level="application_server_libraries" />
</component>
</module>

305
SQL/library_management.sql Normal file
View File

@@ -0,0 +1,305 @@
/*
Navicat Premium Dump SQL
Source Server : home
Source Server Type : MySQL
Source Server Version : 90500 (9.5.0)
Source Host : localhost:3306
Source Schema : library_management
Target Server Type : MySQL
Target Server Version : 90500 (9.5.0)
File Encoding : 65001
Date: 21/03/2026 11:35:12
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- 删除旧表(如果存在)
-- ----------------------------
DROP TABLE IF EXISTS `library_management_activity_registration`;
DROP TABLE IF EXISTS `library_management_assistance_log`;
DROP TABLE IF EXISTS `library_management_borrow_record`;
DROP TABLE IF EXISTS `library_management_book_copy`;
DROP TABLE IF EXISTS `library_management_activity`;
DROP TABLE IF EXISTS `library_management_book`;
DROP TABLE IF EXISTS `library_management_employee`;
DROP TABLE IF EXISTS `library_management_reader`;
-- ----------------------------
-- Table structure for library_management_reader读者信息表
-- ----------------------------
CREATE TABLE `library_management_reader` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`card_number` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '读者证号',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '读者姓名',
`age` int NULL DEFAULT NULL COMMENT '年龄',
`reader_type` tinyint NOT NULL DEFAULT 3 COMMENT '读者类型1-老年2-成人3-儿童',
`contact` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '联系方式',
`reg_date` date NULL DEFAULT NULL COMMENT '注册日期',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_card_number`(`card_number` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '读者信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of library_management_reader测试数据不同类型的读者
-- ----------------------------
INSERT INTO `library_management_reader` VALUES
(1, 'R2024001', '张小明', 8, 3, '13800000001', '2024-01-15', 'E001', '2024-01-15 10:00:00', NULL, NULL),
(2, 'R2024002', '李华', 65, 1, '13800000002', '2024-02-10', 'E001', '2024-02-10 09:30:00', NULL, NULL),
(3, 'R2024003', '王芳', 35, 2, '13800000003', '2024-03-05', 'E002', '2024-03-05 14:20:00', NULL, NULL),
(4, 'R2024004', '陈思思', 12, 3, '13800000004', '2024-04-01', 'E002', '2024-04-01 11:00:00', NULL, NULL),
(5, 'R2024005', '刘建国', 72, 1, '13800000005', '2024-04-20', 'E002', '2024-04-20 16:45:00', NULL, NULL),
(6, 'R2024006', '赵敏', 28, 2, '13800000006', '2024-05-10', 'E001', '2024-05-10 08:00:00', NULL, NULL),
(7, 'R2024007', '孙小燕', 6, 3, '13800000007', '2024-06-01', 'E003', '2024-06-01 10:30:00', NULL, NULL),
(8, 'R2024008', '周大伟', 45, 2, '13800000008', '2024-06-15', 'E003', '2024-06-15 13:00:00', NULL, NULL);
-- ----------------------------
-- Table structure for library_management_employee员工信息表
-- ----------------------------
CREATE TABLE `library_management_employee` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`employee_code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '员工工号',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '员工姓名',
`position` tinyint NOT NULL DEFAULT 1 COMMENT '职位1-馆员2-工作人员3-经理',
`contact` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '联系方式',
`hire_date` date NULL DEFAULT NULL COMMENT '入职日期',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_employee_code`(`employee_code` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '员工信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of library_management_employee测试数据三种角色的员工
-- ----------------------------
INSERT INTO `library_management_employee` VALUES
(1, 'E001', '张经理', 3, '13900000001', '2020-01-01', NULL, '2020-01-01 08:00:00', NULL, NULL),
(2, 'E002', '李工作人员', 2, '13900000002', '2021-03-15', 'E001', '2021-03-15 09:00:00', NULL, NULL),
(3, 'E003', '王馆员', 1, '13900000003', '2022-06-01', 'E001', '2022-06-01 10:00:00', NULL, NULL),
(4, 'E004', '刘工作人员', 2, '13900000004', '2023-01-10', 'E001', '2023-01-10 08:30:00', NULL, NULL),
(5, 'E005', '陈馆员', 1, '13900000005', '2023-09-01', 'E001', '2023-09-01 09:00:00', NULL, NULL);
-- ----------------------------
-- Table structure for library_management_book图书基本信息表
-- ----------------------------
CREATE TABLE `library_management_book` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`isbn` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '国际标准书号',
`title` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '书名',
`author` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作者',
`publisher` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '出版社',
`publish_year` int NULL DEFAULT NULL COMMENT '出版年份',
`total_stock` int NOT NULL DEFAULT 0 COMMENT '总副本数',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '图书简介',
`target_audience` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '通用' COMMENT '面向群体:通用/儿童/成人/老年',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_isbn`(`isbn` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '图书基本信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of library_management_book测试数据多种类型图书
-- ----------------------------
INSERT INTO `library_management_book` VALUES
(1, '978-7-111-00001-1', 'Java核心技术卷I', 'Cay S. Horstmann', '机械工业出版社', 2020, 3, 'Java语言经典教材涵盖Java基础语法和核心库', '通用', 'E003', '2024-01-10 10:00:00', NULL, NULL),
(2, '978-7-111-00002-8', 'Python编程从入门到实践', 'Eric Matthes', '人民邮电出版社', 2019, 5, 'Python入门经典书籍配有大量实践项目', '通用', 'E003', '2024-01-12 11:00:00', NULL, NULL),
(3, '978-7-111-00003-5', '小王子', '安托万·德·圣-埃克苏佩里', '商务印书馆', 2018, 4, '儿童文学经典,讲述小王子的星际旅行', '儿童', 'E003', '2024-01-15 09:00:00', NULL, NULL),
(4, '978-7-111-00004-2', '格林童话全集', '格林兄弟', '译林出版社', 2021, 3, '经典童话故事集,适合儿童阅读', '儿童', 'E003', '2024-01-18 14:00:00', NULL, NULL),
(5, '978-7-111-00005-9', '活着', '余华', '作家出版社', 2012, 2, '余华代表作,讲述普通人的命运', '成人', 'E003', '2024-02-01 10:00:00', NULL, NULL),
(6, '978-7-111-00006-6', '老年健康养生指南', '张明', '卫生出版社', 2023, 2, '老年人健康养生实用手册', '老年', 'E002', '2024-02-05 15:00:00', NULL, NULL),
(7, '978-7-111-00007-3', '数据结构与算法分析', 'Mark Allen Weiss', '电子工业出版社', 2020, 3, '经典算法教材,深入分析各种数据结构', '通用', 'E003', '2024-02-10 09:00:00', NULL, NULL),
(8, '978-7-111-00008-0', '安徒生童话选', '汉斯·克里斯蒂安·安徒生', '少年儿童出版社', 2022, 3, '安徒生经典童话故事,适合亲子阅读', '儿童', 'E002', '2024-02-15 11:00:00', NULL, NULL),
(9, '978-7-111-00009-7', '红楼梦', '曹雪芹', '人民文学出版社', 2019, 2, '中国古典四大名著之一', '成人', 'E003', '2024-03-01 10:00:00', NULL, NULL),
(10, '978-7-111-00010-3', '书法与养生', '李明', '文化艺术出版社', 2023, 2, '书法练习对老年人健康的好处', '老年', 'E002', '2024-03-10 14:00:00', NULL, NULL);
-- ----------------------------
-- Table structure for library_management_book_copy图书副本表
-- ----------------------------
CREATE TABLE `library_management_book_copy` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`book_id` bigint NOT NULL COMMENT '所属图书ID',
`barcode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '条形码',
`location` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '存放位置',
`status` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '可借' COMMENT '状态:可借/借出/损坏',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_barcode`(`barcode` ASC) USING BTREE,
INDEX `idx_book_id`(`book_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '图书副本表(每本实体书)' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of library_management_book_copy每本书的副本数据
-- ----------------------------
INSERT INTO `library_management_book_copy` VALUES
(1, 1, '978-7-111-00001-1-001', 'A区-1-01', '可借', 'E003', '2024-01-10 10:00:00', NULL, NULL),
(2, 1, '978-7-111-00001-1-002', 'A区-1-02', '可借', 'E003', '2024-01-10 10:00:00', NULL, NULL),
(3, 1, '978-7-111-00001-1-003', 'A区-1-03', '可借', 'E003', '2024-01-10 10:00:00', NULL, NULL),
(4, 2, '978-7-111-00002-8-001', 'A区-2-01', '可借', 'E003', '2024-01-12 11:00:00', NULL, NULL),
(5, 2, '978-7-111-00002-8-002', 'A区-2-02', '可借', 'E003', '2024-01-12 11:00:00', NULL, NULL),
(6, 2, '978-7-111-00002-8-003', 'A区-2-03', '可借', 'E003', '2024-01-12 11:00:00', NULL, NULL),
(7, 2, '978-7-111-00002-8-004', 'A区-2-04', '可借', 'E003', '2024-01-12 11:00:00', NULL, NULL),
(8, 2, '978-7-111-00002-8-005', 'A区-2-05', '可借', 'E003', '2024-01-12 11:00:00', NULL, NULL),
(9, 3, '978-7-111-00003-5-001', 'B区-1-01', '可借', 'E003', '2024-01-15 09:00:00', NULL, NULL),
(10, 3, '978-7-111-00003-5-002', 'B区-1-02', '可借', 'E003', '2024-01-15 09:00:00', NULL, NULL),
(11, 3, '978-7-111-00003-5-003', 'B区-1-03', '可借', 'E003', '2024-01-15 09:00:00', NULL, NULL),
(12, 3, '978-7-111-00003-5-004', 'B区-1-04', '可借', 'E003', '2024-01-15 09:00:00', NULL, NULL),
(13, 4, '978-7-111-00004-2-001', 'B区-2-01', '可借', 'E003', '2024-01-18 14:00:00', NULL, NULL),
(14, 4, '978-7-111-00004-2-002', 'B区-2-02', '可借', 'E003', '2024-01-18 14:00:00', NULL, NULL),
(15, 4, '978-7-111-00004-2-003', 'B区-2-03', '可借', 'E003', '2024-01-18 14:00:00', NULL, NULL),
(16, 5, '978-7-111-00005-9-001', 'C区-1-01', '可借', 'E003', '2024-02-01 10:00:00', NULL, NULL),
(17, 5, '978-7-111-00005-9-002', 'C区-1-02', '可借', 'E003', '2024-02-01 10:00:00', NULL, NULL),
(18, 6, '978-7-111-00006-6-001', 'D区-1-01', '可借', 'E002', '2024-02-05 15:00:00', NULL, NULL),
(19, 6, '978-7-111-00006-6-002', 'D区-1-02', '可借', 'E002', '2024-02-05 15:00:00', NULL, NULL),
(20, 7, '978-7-111-00007-3-001', 'A区-3-01', '可借', 'E003', '2024-02-10 09:00:00', NULL, NULL),
(21, 7, '978-7-111-00007-3-002', 'A区-3-02', '可借', 'E003', '2024-02-10 09:00:00', NULL, NULL),
(22, 7, '978-7-111-00007-3-003', 'A区-3-03', '可借', 'E003', '2024-02-10 09:00:00', NULL, NULL),
(23, 8, '978-7-111-00008-0-001', 'B区-3-01', '可借', 'E002', '2024-02-15 11:00:00', NULL, NULL),
(24, 8, '978-7-111-00008-0-002', 'B区-3-02', '可借', 'E002', '2024-02-15 11:00:00', NULL, NULL),
(25, 8, '978-7-111-00008-0-003', 'B区-3-03', '可借', 'E002', '2024-02-15 11:00:00', NULL, NULL),
(26, 9, '978-7-111-00009-7-001', 'C区-2-01', '可借', 'E003', '2024-03-01 10:00:00', NULL, NULL),
(27, 9, '978-7-111-00009-7-002', 'C区-2-02', '可借', 'E003', '2024-03-01 10:00:00', NULL, NULL),
(28, 10, '978-7-111-00010-3-001', 'D区-2-01', '可借', 'E002', '2024-03-10 14:00:00', NULL, NULL),
(29, 10, '978-7-111-00010-3-002', 'D区-2-02', '可借', 'E002', '2024-03-10 14:00:00', NULL, NULL);
-- ----------------------------
-- Table structure for library_management_borrow_record借阅记录表
-- ----------------------------
CREATE TABLE `library_management_borrow_record` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`reader_id` bigint NOT NULL COMMENT '读者ID',
`copy_id` bigint NOT NULL COMMENT '副本ID',
`borrow_date` date NOT NULL COMMENT '借出日期',
`due_date` date NOT NULL COMMENT '应还日期',
`return_date` date NULL DEFAULT NULL COMMENT '实际归还日期',
`renew_count` int NOT NULL DEFAULT 0 COMMENT '续借次数',
`operator_id` bigint NULL DEFAULT NULL COMMENT '办理员工ID读者自助借书时为空',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_reader_id`(`reader_id` ASC) USING BTREE,
INDEX `idx_copy_id`(`copy_id` ASC) USING BTREE,
INDEX `idx_operator_id`(`operator_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '借阅记录表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of library_management_borrow_record测试数据部分借阅记录
-- ----------------------------
INSERT INTO `library_management_borrow_record` VALUES
(1, 3, 1, '2024-02-10', '2024-03-11', '2024-03-05', 0, 2, 'E002', '2024-02-10 10:00:00', NULL, NULL),
(2, 6, 5, '2024-02-20', '2024-03-21', '2024-03-18', 0, 2, 'E002', '2024-02-20 14:00:00', NULL, NULL),
(3, 8, 20, '2024-03-01', '2024-03-31', NULL, 0, 3, 'E003', '2024-03-01 09:00:00', NULL, NULL),
(4, 3, 9, '2024-03-05', '2024-04-04', NULL, 0, 2, 'E002', '2024-03-05 11:00:00', NULL, NULL),
(5, 6, 16, '2024-03-10', '2024-04-09', NULL, 0, 3, 'E003', '2024-03-10 10:00:00', NULL, NULL),
(6, 8, 4, '2024-03-15', '2024-04-14', NULL, 0, 2, 'E002', '2024-03-15 15:00:00', NULL, NULL);
-- ----------------------------
-- Table structure for library_management_activity活动信息表
-- ----------------------------
CREATE TABLE `library_management_activity` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '活动名称',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '活动描述',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`location` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '活动地点',
`target_reader_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '面向群体,如"老年,儿童"',
`manager_id` bigint NULL DEFAULT NULL COMMENT '负责人ID读者端创建活动时为空',
`max_participants` int NOT NULL DEFAULT 0 COMMENT '最大参与人数',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_manager_id`(`manager_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '活动信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of library_management_activity测试数据面向不同群体的活动
-- ----------------------------
INSERT INTO `library_management_activity` VALUES
(1, '儿童绘本故事会', '邀请专业讲师为小朋友讲述经典绘本故事,培养阅读兴趣', '2026-04-05 10:00:00', '2026-04-05 11:30:00', '二楼儿童阅览室', '儿童', 1, 30, 'E001', '2026-03-20 09:00:00', NULL, NULL),
(2, '老年健康讲座', '邀请医学专家讲解老年健康养生知识', '2026-04-12 14:00:00', '2026-04-12 16:00:00', '三楼报告厅', '老年', 1, 50, 'E001', '2026-03-21 10:00:00', NULL, NULL),
(3, '成人读书分享会', '本月主题:《活着》读书分享与讨论', '2026-04-20 19:00:00', '2026-04-20 21:00:00', '一楼多功能厅', '成人', 1, 40, 'E001', '2026-03-22 08:00:00', NULL, NULL),
(4, '青少年编程体验课', 'Python入门体验让青少年感受编程乐趣', '2026-04-08 14:00:00', '2026-04-08 16:00:00', '电子阅览室', '儿童,成人', 1, 20, 'E001', '2026-03-23 09:00:00', NULL, NULL),
(5, '书法交流活动', '老年人书法爱好者交流活动', '2026-04-15 09:00:00', '2026-04-15 12:00:00', '五楼书法室', '老年', 1, 25, 'E001', '2026-03-23 14:00:00', NULL, NULL);
-- ----------------------------
-- Table structure for library_management_activity_registration活动报名表
-- ----------------------------
CREATE TABLE `library_management_activity_registration` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`activity_id` bigint NOT NULL COMMENT '活动ID',
`reader_id` bigint NOT NULL COMMENT '读者ID',
`register_time` datetime NOT NULL COMMENT '报名时间',
`check_in` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否签到0-未签到1-已签到',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_activity_reader`(`activity_id` ASC, `reader_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '活动报名表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of library_management_activity_registration
-- ----------------------------
INSERT INTO `library_management_activity_registration` VALUES
(1, 1, 1, '2026-03-20 10:00:00', 0, NULL, '2026-03-20 10:00:00', NULL, NULL),
(2, 1, 4, '2026-03-20 11:00:00', 0, NULL, '2026-03-20 11:00:00', NULL, NULL),
(3, 1, 7, '2026-03-21 09:00:00', 0, NULL, '2026-03-21 09:00:00', NULL, NULL),
(4, 2, 2, '2026-03-21 10:00:00', 0, NULL, '2026-03-21 10:00:00', NULL, NULL),
(5, 2, 5, '2026-03-21 14:00:00', 0, NULL, '2026-03-21 14:00:00', NULL, NULL),
(6, 3, 3, '2026-03-22 08:00:00', 0, NULL, '2026-03-22 08:00:00', NULL, NULL),
(7, 3, 6, '2026-03-22 09:00:00', 0, NULL, '2026-03-22 09:00:00', NULL, NULL),
(8, 3, 8, '2026-03-22 15:00:00', 0, NULL, '2026-03-22 15:00:00', NULL, NULL),
(9, 4, 1, '2026-03-23 09:00:00', 0, NULL, '2026-03-23 09:00:00', NULL, NULL),
(10, 5, 2, '2026-03-23 14:00:00', 0, NULL, '2026-03-23 14:00:00', NULL, NULL),
(11, 5, 5, '2026-03-23 16:00:00', 0, NULL, '2026-03-23 16:00:00', NULL, NULL);
-- ----------------------------
-- Table structure for library_management_assistance_log读者咨询记录表
-- ----------------------------
CREATE TABLE `library_management_assistance_log` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`reader_id` bigint NOT NULL COMMENT '读者ID',
`worker_id` bigint NOT NULL COMMENT '工作人员ID',
`assist_time` datetime NOT NULL COMMENT '咨询时间',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '咨询内容',
`notes` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
`create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建者',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新者',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_reader_id`(`reader_id` ASC) USING BTREE,
INDEX `idx_worker_id`(`worker_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '读者咨询记录表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of library_management_assistance_log
-- ----------------------------
INSERT INTO `library_management_assistance_log` VALUES
(1, 1, 2, '2024-02-10 10:00:00', '帮助小读者张小明注册办证', '家长陪同', NULL, '2024-02-10 10:00:00', NULL, NULL),
(2, 2, 2, '2024-02-15 14:00:00', '老年读者李华咨询借阅规则', '已解答', NULL, '2024-02-15 14:00:00', NULL, NULL),
(3, 4, 4, '2024-04-05 11:00:00', '帮助陈思思注册办证(儿童)', '家长陪同', NULL, '2024-04-05 11:00:00', NULL, NULL),
(4, 7, 4, '2024-06-10 10:30:00', '帮助孙小燕注册办证(儿童)', '家长陪同', NULL, '2024-06-10 10:30:00', NULL, NULL);
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Class-Path:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>view/Login.jsp</welcome-file>
</welcome-file-list>
<!-- 过滤器统一使用 @WebFilter 注解注册,不需要在 web.xml 中重复配置 -->
<!-- 执行顺序由 @WebFilter 的 filter-name 字母顺序决定A → E → EmployeePermission -->
</web-app>

448
WebContent/view/Login.jsp Normal file
View File

@@ -0,0 +1,448 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>读者门户 · 简洁版 (合并读者类型)</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
/* 样式保持不变,省略(实际使用时应保留原有样式) */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #eef2f6;
font-family: 'Inter', sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 1rem;
}
#app {
width: 100%;
max-width: 520px;
}
.card {
background: white;
border-radius: 28px;
box-shadow: 0 25px 45px -12px rgba(0, 0, 0, 0.15);
padding: 2.2rem 2rem;
width: 100%;
}
h2 {
font-size: 1.9rem;
font-weight: 600;
color: #1a2b3c;
margin-bottom: 1.5rem;
border-bottom: 2px solid #f0f4f9;
padding-bottom: 1rem;
}
h3 {
font-size: 1.2rem;
font-weight: 500;
color: #2c3e50;
margin-bottom: 1.2rem;
}
.field {
margin-bottom: 1.3rem;
}
label {
display: block;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
color: #4a5e74;
margin-bottom: 0.3rem;
}
input,
select {
width: 100%;
padding: 0.8rem 1rem;
font-size: 1rem;
border: 1.5px solid #e2e9f2;
border-radius: 16px;
background: #fff;
transition: border 0.15s;
outline: none;
}
input:focus,
select:focus {
border-color: #7c8ea0;
background: #fafcff;
}
button {
background: #2c3e50;
color: white;
border: none;
padding: 0.9rem 1.5rem;
font-size: 1rem;
font-weight: 500;
border-radius: 40px;
cursor: pointer;
transition: background 0.15s;
width: 100%;
margin-top: 0.3rem;
}
button:hover {
background: #1d2c3a;
}
.secondary-btn {
background: white;
color: #2c3e50;
border: 1.5px solid #d0ddee;
margin-top: 1rem;
}
.secondary-btn:hover {
background: #f2f6fc;
border-color: #a0b8cf;
}
.nav-links {
display: flex;
justify-content: space-between;
margin-top: 1.5rem;
gap: 0.5rem;
flex-wrap: wrap;
}
.nav-link {
background: #f0f5fb;
color: #2e405b;
padding: 0.6rem 1.2rem;
border-radius: 40px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
border: 1px solid transparent;
transition: background 0.15s;
text-align: center;
flex: 1 1 auto;
}
.nav-link:hover {
background: #dae2ed;
color: #0e1e2f;
}
.active-view {
margin-bottom: 1rem;
}
.note {
font-size: 0.75rem;
color: #8f9fb1;
margin-top: 0.5rem;
text-align: center;
}
.compact-form {
background: #f9fcff;
border-radius: 20px;
padding: 1.5rem 1.2rem;
margin: 0.5rem 0 1rem;
}
.nav-link.active-nav {
background: #2c3e50;
color: white;
border-color: #2c3e50;
}
.nav-link.active-nav:hover {
background: #1d2c3a;
}
</style>
</head>
<body>
<div id="app">
<div class="card">
<h2>📖 读者门户</h2>
<!-- 动态视图:读者登录 / 读者注册 / 员工登录 -->
<div class="active-view">
<!-- 读者登录视图 -->
<div v-if="currentView === 'reader-login'">
<h3>👤 读者登录</h3>
<div class="field">
<label>证号</label>
<input v-model="readerLogin.cardNumber" type="text" placeholder="例如R2024001" />
</div>
<div class="field">
<label>姓名</label>
<input v-model="readerLogin.name" type="text" placeholder="你的名字" />
</div>
<button @click="handleReaderLogin">登录</button>
<div class="note">跳转与验证由后端处理</div>
</div>
<!-- 读者注册视图 -->
<div v-else-if="currentView === 'reader-register'">
<h3>📝 注册新读者</h3>
<div class="compact-form">
<div class="field">
<label>证号</label>
<input v-model="registerForm.cardNumber" type="text" placeholder="唯一证号" />
</div>
<div class="field">
<label>姓名</label>
<input v-model="registerForm.name" type="text" placeholder="真实姓名" />
</div>
<div class="field">
<label>年龄(选填)</label>
<input v-model="registerForm.age" type="number" placeholder="可选" min="0" max="120" />
</div>
<div class="field">
<label>读者类型</label>
<select v-model="registerForm.type">
<option>老年</option>
<option>成人</option>
<option>儿童</option>
</select>
</div>
<div class="field">
<label>联系方式</label>
<input v-model="registerForm.contact" type="text" placeholder="手机 / 邮箱" />
</div>
<div class="field">
<label>注册日期</label>
<input v-model="registerForm.regDate" type="date" />
</div>
</div>
<button @click="handleRegister">提交注册</button>
</div>
<!-- 员工登录视图 -->
<div v-else-if="currentView === 'employee-login'">
<h3>🔐 员工登录</h3>
<div class="compact-form">
<div class="field">
<label>员工工号</label>
<input v-model="employeeLogin.employeeCode" type="text" placeholder="员工工号" />
</div>
<div class="field">
<label>姓名</label>
<input v-model="employeeLogin.name" type="text" placeholder="姓名" />
</div>
</div>
<button @click="handleEmployeeLogin">登录</button>
<div class="note">经理页面另有添加用户功能</div>
</div>
</div>
<!-- 导航链接 -->
<div class="nav-links">
<span class="nav-link" @click="switchView('reader-login')" :class="{ 'active-nav': currentView === 'reader-login' }">🔑 读者登录</span>
<span class="nav-link" @click="switchView('reader-register')" :class="{ 'active-nav': currentView === 'reader-register' }"> 注册</span>
<span class="nav-link" @click="switchView('employee-login')" :class="{ 'active-nav': currentView === 'employee-login' }">👔 员工登录</span>
</div>
<div style="margin-top: 1.2rem; text-align: center; font-size:0.8rem; color:#abb9c9;">
点击上方标签切换功能
</div>
</div>
</div>
<script>
(function() {
const { createApp, ref } = Vue;
// 工具函数:获取今日日期字符串 yyyy-MM-dd
const getTodayString = () => {
const d = new Date();
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
// 工具函数:验证日期字符串是否符合 yyyy-MM-dd 格式
const isValidDate = (dateStr) => {
if (!dateStr) return false;
const regex = /^\d{4}-\d{2}-\d{2}$/;
if (!regex.test(dateStr)) return false;
const date = new Date(dateStr);
return !isNaN(date.getTime());
};
const app = createApp({
setup() {
const currentView = ref('reader-login');
// 读者登录数据
const readerLogin = ref({
cardNumber: '',
name: ''
});
// 员工登录数据
const employeeLogin = ref({
employeeCode: '',
name: ''
});
// 注册表单默认值
const getDefaultRegisterForm = () => ({
cardNumber: '',
name: '',
age: '',
type: '成人',
contact: '',
regDate: getTodayString()
});
const registerForm = ref(getDefaultRegisterForm());
// 切换视图时重置数据
const switchView = (view) => {
currentView.value = view;
if (view === 'reader-register') {
registerForm.value = getDefaultRegisterForm();
} else if (view === 'reader-login') {
readerLogin.value = { cardNumber: '', name: '' };
} else if (view === 'employee-login') {
employeeLogin.value = { employeeCode: '', name: '' };
}
};
// 读者登录
const handleReaderLogin = async () => {
if (!readerLogin.value.cardNumber || !readerLogin.value.name) {
alert('请填写证号和姓名');
return;
}
const params = new URLSearchParams({
cardNumber: readerLogin.value.cardNumber,
name: readerLogin.value.name
});
try {
const response = await fetch('${pageContext.request.contextPath}/api/login/reader', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const result = await response.json();
if (result.success) {
window.location.href = '${pageContext.request.contextPath}/view/Reader.jsp';
} else {
alert(result.message || '登录失败');
}
} catch (e) {
console.error(e);
alert('网络错误,请检查后端服务是否正常');
}
};
// 读者注册
const handleRegister = async () => {
// 前端基础校验
if (!registerForm.value.cardNumber) {
alert('证号不能为空');
return;
}
if (!registerForm.value.name) {
alert('姓名不能为空');
return;
}
// 年龄如果是空字符串或非数字设为空后端会处理为null
let ageVal = registerForm.value.age;
if (ageVal !== '' && isNaN(Number(ageVal))) {
alert('年龄请输入数字');
return;
}
// 日期处理:如果无效或为空,使用今天日期
let regDateVal = registerForm.value.regDate;
if (!isValidDate(regDateVal)) {
regDateVal = getTodayString();
registerForm.value.regDate = regDateVal; // 同步更新显示
}
const params = new URLSearchParams({
cardNumber: registerForm.value.cardNumber,
name: registerForm.value.name,
age: ageVal,
type: registerForm.value.type,
contact: registerForm.value.contact || '',
regDate: regDateVal
});
try {
const response = await fetch('${pageContext.request.contextPath}/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const result = await response.json();
if (result.success) {
alert('注册成功,请登录');
switchView('reader-login');
} else {
alert(result.message || '注册失败');
}
} catch (e) {
console.error(e);
alert('网络错误,请检查后端服务是否正常');
}
};
// 员工登录
const handleEmployeeLogin = async () => {
if (!employeeLogin.value.employeeCode || !employeeLogin.value.name) {
alert('请填写工号和姓名');
return;
}
const params = new URLSearchParams({
employeeCode: employeeLogin.value.employeeCode,
name: employeeLogin.value.name
});
try {
const response = await fetch('${pageContext.request.contextPath}/api/login/employee', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const result = await response.json();
if (result.success) {
window.location.href = '${pageContext.request.contextPath}/view/Work.jsp';
} else {
alert(result.message || '登录失败');
}
} catch (e) {
console.error(e);
alert('网络错误,请检查后端服务是否正常');
}
};
return {
currentView,
readerLogin,
employeeLogin,
registerForm,
switchView,
handleReaderLogin,
handleRegister,
handleEmployeeLogin
};
}
});
app.mount('#app');
})();
</script>
</body>
</html>

688
WebContent/view/Reader.jsp Normal file
View File

@@ -0,0 +1,688 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.chinasofti.model.Reader" %>
<%@ page import="com.chinasofti.enums.ReaderType" %>
<%
Reader currentReader = (Reader) session.getAttribute("currentReader");
// Login.jsp 在 /view/Login.jsp需要返回上级目录
if (currentReader == null) {
response.sendRedirect(request.getContextPath() + "/../Login.jsp");
return;
}
// 准备传递给前端的数据
String readerName = currentReader.getName();
String readerTypeDesc = "";
int typeCode = currentReader.getReaderType();
if (typeCode == ReaderType.SENIOR.getCode()) readerTypeDesc = "老年";
else if (typeCode == ReaderType.ADULT.getCode()) readerTypeDesc = "成人";
else if (typeCode == ReaderType.CHILD.getCode()) readerTypeDesc = "儿童";
String readerAccount = currentReader.getCardNumber();
String readerJson = String.format(
"{\"name\":\"%s\",\"type\":\"%s\",\"account\":\"%s\"}",
readerName, readerTypeDesc, readerAccount
);
%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>读者主页 · 借阅与活动</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
/* 样式与原来相同,此处省略(请保留原样式) */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #eef2f6;
font-family: 'Inter', sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 1rem;
}
#app {
width: 100%;
max-width: 1400px;
}
.app-container {
background: white;
border-radius: 32px;
box-shadow: 0 30px 50px -20px rgba(0, 0, 0, 0.2);
overflow: hidden;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.2rem 2rem;
background: #fff;
border-bottom: 1px solid #eaf0f6;
}
.user-info {
display: flex;
align-items: center;
gap: 1.5rem;
flex-wrap: wrap;
}
.user-greeting {
font-weight: 600;
font-size: 1.2rem;
color: #1a2b3c;
background: #f0f6fe;
padding: 0.4rem 1.2rem;
border-radius: 40px;
}
.logout-btn {
background: white;
border: 1.5px solid #dae2ed;
color: #3b4e62;
padding: 0.4rem 1.4rem;
border-radius: 40px;
font-weight: 500;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.15s;
}
.logout-btn:hover {
background: #f1f7fd;
border-color: #9bb1c9;
}
.child-note {
background: #fdf6e6;
color: #926d39;
border-radius: 40px;
padding: 0.3rem 1.2rem;
font-size: 0.8rem;
font-weight: 500;
display: inline-block;
}
.nav-tabs {
display: flex;
gap: 0.8rem;
}
.nav-tab {
background: transparent;
border: none;
padding: 0.6rem 1.8rem;
font-size: 1rem;
font-weight: 600;
color: #5d7184;
border-radius: 40px;
cursor: pointer;
transition: 0.15s;
border: 1.5px solid transparent;
}
.nav-tab.active {
background: #2c3e50;
color: white;
}
.nav-tab:not(.active):hover {
background: #eef3f9;
color: #2c3e50;
}
.main-content {
padding: 2rem;
}
.borrow-layout {
display: flex;
flex-direction: column;
gap: 2.5rem;
}
.section-card {
background: #f9fcff;
border-radius: 24px;
padding: 1.6rem 1.8rem;
border: 1px solid #e6edf5;
}
.section-title {
font-size: 1.2rem;
font-weight: 600;
color: #1e3b5c;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.search-row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: flex-end;
margin-bottom: 2rem;
}
.search-field {
flex: 1 1 160px;
}
.search-field label {
display: block;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
color: #5f748b;
margin-bottom: 0.3rem;
}
.search-field input {
width: 100%;
padding: 0.6rem 1rem;
border: 1.5px solid #d8e2ee;
border-radius: 30px;
font-size: 0.95rem;
background: white;
}
.search-field input:focus {
outline: none;
border-color: #7e99b3;
}
.search-btn {
background: #2c3e50;
color: white;
border: none;
padding: 0.6rem 2rem;
border-radius: 30px;
font-weight: 500;
cursor: pointer;
font-size: 0.95rem;
transition: background 0.15s;
height: fit-content;
align-self: center;
}
.search-btn:hover {
background: #1a2b3c;
}
.table-wrapper {
overflow-x: auto;
border-radius: 18px;
background: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.02);
margin-top: 1rem;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
min-width: 1000px;
}
th {
background: #eef4fa;
color: #1d3b5c;
font-weight: 600;
padding: 1rem 0.8rem;
text-align: left;
white-space: nowrap;
}
td {
padding: 1rem 0.8rem;
border-bottom: 1px solid #eef3f8;
color: #1f2f40;
}
.action-btn {
background: #2c3e50;
color: white;
border: none;
padding: 0.4rem 1.2rem;
border-radius: 30px;
font-size: 0.8rem;
font-weight: 500;
cursor: pointer;
transition: background 0.15s;
}
.action-btn.return {
background: #9b6b43;
}
.action-btn.return:hover {
background: #7e4f2e;
}
.action-btn:hover {
background: #1a2b3c;
}
.child-badge {
background: #c9dff3;
color: #144a70;
padding: 0.2rem 0.8rem;
border-radius: 30px;
font-size: 0.75rem;
font-weight: 600;
display: inline-block;
}
.empty-row td {
padding: 2rem;
text-align: center;
color: #8c9db2;
font-style: italic;
}
.activity-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 1.8rem;
margin-top: 1.5rem;
}
.activity-card {
background: white;
border-radius: 24px;
padding: 1.5rem;
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.03);
border: 1px solid #e6edf5;
transition: transform 0.1s;
display: flex;
flex-direction: column;
}
.activity-card:hover {
transform: translateY(-2px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.05);
}
.activity-name {
font-size: 1.2rem;
font-weight: 600;
color: #1e3b5c;
margin-bottom: 0.5rem;
}
.activity-time {
color: #6e8aa8;
font-size: 0.9rem;
margin-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 0.3rem;
}
.activity-desc {
color: #3a4e66;
font-size: 0.95rem;
line-height: 1.4;
margin: 0.8rem 0 1.2rem;
flex: 1;
}
.signup-btn {
background: #2c3e50;
color: white;
border: none;
padding: 0.6rem 1rem;
border-radius: 40px;
font-weight: 500;
font-size: 0.9rem;
cursor: pointer;
transition: background 0.15s;
align-self: flex-start;
width: 100%;
}
.signup-btn:hover {
background: #1a2b3c;
}
.info-note {
background: #eaf2fb;
padding: 0.5rem 1.2rem;
border-radius: 40px;
color: #2c577c;
font-size: 0.85rem;
display: inline-block;
}
.empty-placeholder {
text-align: center;
padding: 2rem;
background: white;
border-radius: 18px;
color: #8f9fb1;
}
</style>
</head>
<body>
<div id="app">
<div class="app-container">
<div class="header">
<div class="user-info">
<span class="user-greeting">👋 {{ currentUser.name }}</span>
<button class="logout-btn" @click="logout">退出登录</button>
<span v-if="currentUser.type === '儿童'" class="child-note">🧸 儿童读者 · 仅显示儿童书目</span>
</div>
<div class="nav-tabs">
<button class="nav-tab" :class="{ active: activeTab === 'borrow' }" @click="activeTab = 'borrow'">📚 借阅</button>
<button class="nav-tab" :class="{ active: activeTab === 'activity' }" @click="activeTab = 'activity'">🎉 活动</button>
</div>
</div>
<div class="main-content">
<div v-if="activeTab === 'borrow'" class="borrow-layout">
<!-- 书目查询 -->
<div class="section-card">
<div class="section-title">
<span>🔍 书目查询 · 借阅</span>
<span class="info-note">支持 ISBN / 书名 / 作者</span>
</div>
<div class="search-row">
<div class="search-field">
<label>ISBN</label>
<input v-model="searchIsbn" type="text" placeholder="如 978-7-01" />
</div>
<div class="search-field">
<label>书名</label>
<input v-model="searchTitle" type="text" placeholder="关键词" />
</div>
<div class="search-field">
<label>作者</label>
<input v-model="searchAuthor" type="text" placeholder="关键词" />
</div>
<button class="search-btn" @click="searchBooks">查询书目</button>
</div>
<div v-if="bookTableVisible" class="table-wrapper">
<table>
<thead>
<tr>
<th>book_id</th><th>ISBN</th><th>书名</th><th>作者</th><th>出版社</th><th>出版年</th><th>可借</th><th>儿童书</th><th>简介</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr v-if="filteredBooks.length === 0">
<td colspan="10" class="empty-row">没有符合条件的书籍</td>
</tr>
<tr v-for="book in filteredBooks" :key="book.bookId">
<td>{{ book.bookId }}</td>
<td>{{ book.isbn }}</td>
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
<td>{{ book.publisher }}</td>
<td>{{ book.publishYear }}</td>
<td>{{ book.availableCopies }}</td>
<td>
<span v-if="book.targetAudience === '儿童'" class="child-badge">儿童书</span>
<span v-else style="color:#8a9bb0;">通用</span>
</td>
<td>{{ book.description || '—' }}</td>
<td><button class="action-btn" @click="borrowBook(book)">借阅</button></td>
</tr>
</tbody>
</table>
</div>
<div v-else class="empty-placeholder">
⚡ 请输入条件并点击“查询书目”查看结果
</div>
</div>
<!-- 我的借阅 -->
<div class="section-card">
<div class="section-title">
<span>📋 我的借阅 · 归还</span>
<span class="info-note">输入账号号码查询</span>
</div>
<div class="search-row" style="margin-bottom:1.5rem;">
<div class="search-field" style="flex:2;">
<label>读者证号</label>
<input v-model="borrowerAccount" type="text" placeholder="例如 R2024001" readonly/>
</div>
<button class="search-btn" @click="searchBorrowed">查询借阅</button>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>借阅ID</th><th>ISBN</th><th>书名</th><th>作者</th><th>借阅日期</th><th>应还日期</th><th>状态</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr v-if="borrowedList.length === 0">
<td colspan="8" class="empty-row">暂无借阅记录</td>
</tr>
<tr v-for="item in borrowedList" :key="item.borrowId">
<td>{{ item.borrowId }}</td>
<td>{{ item.isbn }}</td>
<td>{{ item.bookTitle }}</td>
<td>{{ item.author }}</td>
<td>{{ item.borrowDate }}</td>
<td>{{ item.dueDate }}</td>
<td><span style="color:#2e7d5e;">{{ item.status }}</span></td>
<td><button class="action-btn return" @click="returnBook(item)">归还</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 活动页面 -->
<div v-else class="activity-page">
<h3 style="font-size:1.4rem; color:#1e3b5c; margin-bottom:1.2rem;">🎈 近期活动</h3>
<div class="activity-grid">
<div v-for="act in activities" :key="act.id" class="activity-card">
<div class="activity-name">{{ act.name }}</div>
<div class="activity-time">📅 {{ act.startTime }} ~ {{ act.endTime }}</div>
<div class="activity-desc">{{ act.description }}</div>
<button class="signup-btn" @click="signupActivity(act)">报名参加</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
(function() {
const { createApp, ref, computed, onMounted } = Vue;
// 从服务器端传递的初始数据
const initialUser = <%= readerJson %>;
const app = createApp({
setup() {
const currentUser = ref(initialUser);
const activeTab = ref('borrow');
// 书目查询
const searchIsbn = ref('');
const searchTitle = ref('');
const searchAuthor = ref('');
const bookTableVisible = ref(false);
const books = ref([]); // 存储查询结果
const filteredBooks = computed(() => books.value);
const searchBooks = async () => {
const params = new URLSearchParams({
isbn: searchIsbn.value,
title: searchTitle.value,
author: searchAuthor.value
});
try {
const response = await fetch('${pageContext.request.contextPath}/api/books/search?' + params, {
method: 'GET',
credentials: 'include',
});
if (!response.ok) throw new Error('查询失败');
books.value = await response.json();
bookTableVisible.value = true;
} catch (e) {
alert('查询失败:' + e.message);
}
};
const borrowBook = async (book) => {
const params = new URLSearchParams({
readerCard: currentUser.value.account,
bookId: book.bookId
});
try {
const response = await fetch('${pageContext.request.contextPath}/api/borrow', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include' // 添加此行
});
const result = await response.json();
if (result.success) {
alert('借阅成功');
await searchBorrowed(); // 刷新借阅列表
} else {
alert(result.message || '借阅失败');
}
} catch (e) {
alert('网络错误');
}
};
// 借阅记录
const borrowerAccount = ref(currentUser.value.account);
const borrowedList = ref([]);
const searchBorrowed = async () => {
if (!borrowerAccount.value) {
alert('请输入读者证号');
return;
}
try {
const response = await fetch('${pageContext.request.contextPath}/api/borrows?readerCard=' + encodeURIComponent(borrowerAccount.value), {
method: 'GET',
credentials: 'include',
});
if (!response.ok) throw new Error('查询失败');
borrowedList.value = await response.json();
} catch (e) {
alert('查询借阅记录失败');
}
};
const returnBook = async (item) => {
console.log('>>> [归还] 点击归还按钮borrowId=' + item.borrowId);
const params = new URLSearchParams({
borrowId: item.borrowId
});
try {
const response = await fetch('${pageContext.request.contextPath}/api/borrow', {
method: 'PUT',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include',
});
console.log('>>> [归还] HTTP状态码=' + response.status);
const result = await response.json();
console.log('>>> [归还] 后端返回=' + JSON.stringify(result));
if (result.success) {
alert('归还成功');
await searchBorrowed();
} else {
alert('归还失败:' + (result.message || '未知原因,请查看控制台日志'));
}
} catch (e) {
console.error('>>> [归还] 请求异常:' + e);
alert('网络错误:' + e.message);
}
};
// 活动
const activities = ref([]);
const loadActivities = async () => {
try {
const response = await fetch('${pageContext.request.contextPath}/api/activities');
if (!response.ok) throw new Error('加载活动失败');
activities.value = await response.json();
} catch (e) {
console.error('加载活动失败', e);
}
};
const signupActivity = async (act) => {
const params = new URLSearchParams({
activityId: act.id
});
try {
const response = await fetch('${pageContext.request.contextPath}/api/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include' // 确保携带 session cookie
});
const result = await response.json();
if (result.success) {
alert('报名成功');
} else {
alert(result.message || '报名失败');
}
} catch (e) {
alert('网络错误');
}
};
// 退出登录
const logout = async () => {
// 可调用登出 API如清除 session然后跳转
window.location.href = '${pageContext.request.contextPath}/view/Login.jsp';
};
onMounted(() => {
loadActivities();
searchBorrowed(); // 加载当前读者的借阅记录
});
return {
currentUser,
activeTab,
searchIsbn,
searchTitle,
searchAuthor,
bookTableVisible,
filteredBooks,
searchBooks,
borrowBook,
borrowerAccount,
borrowedList,
searchBorrowed,
returnBook,
activities,
signupActivity,
logout
};
}
});
app.mount('#app');
})();
</script>
</body>
</html>

886
WebContent/view/Work.jsp Normal file
View File

@@ -0,0 +1,886 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
com.chinasofti.model.Employee currentEmployee = (com.chinasofti.model.Employee) session.getAttribute("currentEmployee");
if (currentEmployee == null) {
response.sendRedirect(request.getContextPath() + "/../Login.jsp");
return;
}
String positionDesc = "";
int positionCode = currentEmployee.getPosition();
switch (positionCode) {
case 1: positionDesc = "馆员"; break;
case 2: positionDesc = "工作人员"; break;
case 3: positionDesc = "经理"; break;
}
String empJson = String.format(
"{\"id\":\"%s\",\"name\":\"%s\",\"position\":\"%s\",\"positionCode\":%d}",
currentEmployee.getEmployeeCode(),
currentEmployee.getName(),
positionDesc,
positionCode
);
%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>员工门户</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #eef2f6;
font-family: 'Inter', sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 1rem;
}
#app {
width: 100%;
max-width: 1400px;
}
.app-container {
background: white;
border-radius: 32px;
box-shadow: 0 30px 50px -20px rgba(0, 0, 0, 0.2);
overflow: hidden;
}
.employee-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.2rem 2rem;
background: #fff;
border-bottom: 1px solid #eaf0f6;
flex-wrap: wrap;
gap: 1rem;
}
.employee-info {
display: flex;
align-items: center;
gap: 2rem;
flex-wrap: wrap;
}
.info-item {
background: #f0f6fe;
padding: 0.4rem 1.2rem;
border-radius: 40px;
font-weight: 500;
color: #1a2b3c;
}
.role-badge {
background: #2c3e50;
color: white;
padding: 0.3rem 1rem;
border-radius: 40px;
font-size: 0.85rem;
font-weight: 600;
}
.role-badge.staff {
background: #3b7dd8;
}
.role-badge.worker {
background: #27ae60;
}
.role-badge.manager {
background: #e67e22;
}
.logout-btn {
background: white;
border: 1.5px solid #dae2ed;
color: #3b4e62;
padding: 0.4rem 1.4rem;
border-radius: 40px;
font-weight: 500;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.15s;
}
.logout-btn:hover {
background: #f1f7fd;
border-color: #9bb1c9;
}
.main-content {
padding: 2rem;
}
.section-card {
background: #f9fcff;
border-radius: 24px;
padding: 1.6rem 1.8rem;
border: 1px solid #e6edf5;
margin-bottom: 2rem;
}
.section-title {
font-size: 1.2rem;
font-weight: 600;
color: #1e3b5c;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.form-row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: flex-end;
margin-bottom: 1.5rem;
}
.form-field {
flex: 1 1 160px;
}
.form-field label {
display: block;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
color: #5f748b;
margin-bottom: 0.3rem;
}
.form-field input,
.form-field select {
width: 100%;
padding: 0.6rem 1rem;
border: 1.5px solid #d8e2ee;
border-radius: 30px;
font-size: 0.95rem;
background: white;
}
.form-field input:focus,
.form-field select:focus {
outline: none;
border-color: #7e99b3;
}
.btn {
background: #2c3e50;
color: white;
border: none;
padding: 0.6rem 1.8rem;
border-radius: 30px;
font-weight: 500;
cursor: pointer;
font-size: 0.95rem;
transition: background 0.15s;
height: fit-content;
border: 1px solid transparent;
}
.btn:hover {
background: #1a2b3c;
}
.btn-outline {
background: white;
color: #2c3e50;
border: 1.5px solid #d0ddee;
}
.btn-outline:hover {
background: #f2f6fc;
border-color: #a0b8cf;
}
.btn-sm {
padding: 0.3rem 1rem;
font-size: 0.8rem;
}
.table-wrapper {
overflow-x: auto;
border-radius: 18px;
background: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.02);
margin-top: 1.5rem;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
min-width: 0px;
}
th {
background: #eef4fa;
color: #1d3b5c;
font-weight: 600;
padding: 1rem 0.8rem;
text-align: left;
}
td {
padding: 1rem 0.8rem;
border-bottom: 1px solid #eef3f8;
color: #1f2f40;
}
.action-cell {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.badge {
background: #c9dff3;
color: #144a70;
padding: 0.2rem 0.8rem;
border-radius: 30px;
font-size: 0.75rem;
font-weight: 600;
display: inline-block;
}
.grid-2col {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
@media (max-width:800px) {
.grid-2col {
grid-template-columns: 1fr;
}
}
.empty-placeholder {
text-align: center;
padding: 2rem;
background: white;
border-radius: 18px;
color: #8f9fb1;
}
.perm-note {
background: #fff3cd;
color: #856404;
border-radius: 12px;
padding: 0.8rem 1.2rem;
font-size: 0.85rem;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
</style>
</head>
<body>
<div id="app">
<div class="app-container">
<!-- 头部:员工信息(无职位切换) -->
<div class="employee-header">
<div class="employee-info">
<span class="info-item">工号:{{ employee.id }}</span>
<span class="info-item">姓名:{{ employee.name }}</span>
<span class="role-badge" :class="employee.positionCode === 1 ? 'staff' : employee.positionCode === 2 ? 'worker' : 'manager'">
职位:{{ employee.position }}
</span>
</div>
<button class="logout-btn" @click="logout">退出登录</button>
</div>
<!-- 主区域根据真实职位positionCode渲染功能 -->
<div class="main-content">
<!-- ====== 馆员视图:图书管理 ====== -->
<div v-if="employee.positionCode === 1">
<div class="perm-note">
您的权限:管理图书(增删改查)
</div>
<div class="section-card">
<div class="section-title">图书管理</div>
<div class="form-row">
<div class="form-field">
<label>ISBN</label>
<input v-model="bookForm.isbn" type="text" placeholder="ISBN" />
</div>
<div class="form-field">
<label>书名</label>
<input v-model="bookForm.title" type="text" placeholder="书名" />
</div>
<div class="form-field">
<label>作者</label>
<input v-model="bookForm.author" type="text" placeholder="作者" />
</div>
<div class="form-field">
<label>出版社</label>
<input v-model="bookForm.publisher" type="text" placeholder="出版社" />
</div>
<div class="form-field">
<label>出版年</label>
<input v-model.number="bookForm.publishYear" type="number" placeholder="2025" />
</div>
<div class="form-field">
<label>总库存</label>
<input v-model.number="bookForm.totalStock" type="number" placeholder="库存" />
</div>
<div class="form-field">
<label>面向群体</label>
<select v-model="bookForm.targetAudience">
<option value="通用">通用</option>
<option value="儿童">儿童</option>
<option value="成人">成人</option>
<option value="老年">老年</option>
</select>
</div>
<div class="form-field">
<label>简介</label>
<input v-model="bookForm.description" type="text" placeholder="简介" />
</div>
<button v-if="!editingBookId" class="btn" @click="addBook">添加图书</button>
<template v-else>
<button class="btn" style="background:#27ae60;" @click="updateBook">更新图书</button>
<button class="btn btn-outline" @click="cancelEdit">取消</button>
</template>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>ID</th><th>ISBN</th><th>书名</th><th>作者</th><th>出版社</th><th>出版年</th><th>库存</th><th>群体</th><th>简介</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr v-if="books.length === 0">
<td colspan="10" class="empty-placeholder">暂无图书</td>
</tr>
<tr v-for="book in books" :key="book.id">
<td>{{ book.id }}</td>
<td>{{ book.isbn }}</td>
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
<td>{{ book.publisher }}</td>
<td>{{ book.publishYear }}</td>
<td>{{ book.totalStock }}</td>
<td><span v-if="book.targetAudience === '儿童'" class="badge">儿童</span><span v-else>通用</span></td>
<td>{{ book.description || '—' }}</td>
<td class="action-cell">
<button v-if="editingBookId !== book.id" class="btn btn-sm" @click="startEdit(book)">编辑</button>
<button class="btn btn-sm btn-outline" @click="deleteBook(book)">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- ====== 工作人员视图:帮助注册 + 代借代还 ====== -->
<div v-else-if="employee.positionCode === 2">
<div class="perm-note">
您的权限:帮助注册读者、代借代还图书
</div>
<div class="grid-2col">
<!-- 左侧:帮助注册读者 -->
<div class="section-card">
<div class="section-title">帮助注册读者</div>
<div class="form-field" style="margin-bottom:1rem;">
<label>证号</label>
<input v-model="registerForm.cardNumber" placeholder="例如 R2025001" />
</div>
<div class="form-field" style="margin-bottom:1rem;">
<label>姓名</label>
<input v-model="registerForm.name" placeholder="姓名" />
</div>
<div class="form-field" style="margin-bottom:1rem;">
<label>年龄(选填)</label>
<input v-model.number="registerForm.age" type="number" placeholder="年龄" />
</div>
<div class="form-field" style="margin-bottom:1rem;">
<label>读者类型</label>
<select v-model="registerForm.type">
<option>老年</option>
<option>成人</option>
<option>儿童</option>
</select>
</div>
<div class="form-field" style="margin-bottom:1.5rem;">
<label>联系方式</label>
<input v-model="registerForm.contact" placeholder="手机/邮箱" />
</div>
<button class="btn" @click="staffRegister">注册读者</button>
</div>
<!-- 右侧:代借代还 -->
<div class="section-card">
<div class="section-title">代借 / 代还图书</div>
<div class="form-row" style="margin-bottom:1.5rem;">
<div class="form-field" style="flex:2;">
<label>读者证号</label>
<input v-model="borrowerAccount" placeholder="如 R2024001" />
</div>
<button class="btn" @click="searchBorrowedStaff">查询</button>
</div>
<div class="table-wrapper">
<table>
<thead><tr><th>ID</th><th>ISBN</th><th>书名</th><th>借阅日</th><th>应还日</th><th>操作</th></tr></thead>
<tbody>
<tr v-if="staffBorrowedList.length === 0"><td colspan="6" class="empty-placeholder">暂无借阅记录</td></tr>
<tr v-for="item in staffBorrowedList" :key="item.borrowId">
<td>{{ item.borrowId }}</td>
<td>{{ item.isbn }}</td>
<td>{{ item.bookTitle }}</td>
<td>{{ item.borrowDate }}</td>
<td>{{ item.dueDate }}</td>
<td><button class="btn btn-sm" @click="returnBookStaff(item)">归还</button></td>
</tr>
</tbody>
</table>
</div>
<div style="margin-top:1.5rem;">
<div class="form-row">
<div class="form-field" style="flex:2;">
<label>ISBN / 书名</label>
<input v-model="quickBorrowIsbn" placeholder="输入ISBN或书名" />
</div>
<button class="btn" @click="quickBorrow">快速借书</button>
</div>
</div>
</div>
</div>
</div>
<!-- ====== 经理视图:活动管理 + 员工管理 ====== -->
<div v-else-if="employee.positionCode === 3">
<div class="perm-note">
您的权限:管理活动、管理员工
</div>
<div class="grid-2col">
<!-- 左侧:活动管理 -->
<div class="section-card">
<div class="section-title">活动管理</div>
<div class="form-row">
<div class="form-field">
<label>活动名称</label>
<input v-model="activityForm.name" />
</div>
<div class="form-field">
<label>开始时间</label>
<input v-model="activityForm.startTime" type="datetime-local" />
</div>
<div class="form-field">
<label>结束时间</label>
<input v-model="activityForm.endTime" type="datetime-local" />
</div>
<div class="form-field">
<label>地点</label>
<input v-model="activityForm.location" />
</div>
<div class="form-field">
<label>简介</label>
<input v-model="activityForm.description" />
</div>
<div class="form-field">
<label>最大人数</label>
<input v-model.number="activityForm.maxParticipants" type="number" placeholder="50" />
</div>
<button class="btn" @click="addActivity">添加活动</button>
</div>
<div class="table-wrapper">
<table>
<thead><tr><th>ID</th><th>名称</th><th>时间</th><th>地点</th><th>简介</th><th>最大人数</th><th>操作</th></tr></thead>
<tbody>
<tr v-if="activities.length === 0"><td colspan="7" class="empty-placeholder">暂无活动</td></tr>
<tr v-for="act in activities" :key="act.id">
<td>{{ act.id }}</td>
<td>{{ act.name }}</td>
<td>{{ act.startTime }}</td>
<td>{{ act.location }}</td>
<td>{{ act.description || '—' }}</td>
<td>{{ act.maxParticipants }}</td>
<td class="action-cell">
<button class="btn btn-sm btn-outline" @click="deleteActivity(act)">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 右侧:员工管理 -->
<div class="section-card">
<div class="section-title">员工管理</div>
<div class="form-field" style="margin-bottom:1rem;">
<label>工号</label>
<input v-model="newEmployee.employeeCode" placeholder="如 E006" />
</div>
<div class="form-field" style="margin-bottom:1rem;">
<label>姓名</label>
<input v-model="newEmployee.name" placeholder="姓名" />
</div>
<div class="form-field" style="margin-bottom:1.5rem;">
<label>职位</label>
<select v-model="newEmployee.position">
<option>馆员</option>
<option>工作人员</option>
<option>经理</option>
</select>
</div>
<button class="btn" @click="addEmployee">注册新员工</button>
<div style="margin-top:2rem;">
<div class="section-title" style="margin-bottom:0.8rem;">现有员工</div>
<div class="table-wrapper">
<table>
<thead><tr><th>工号</th><th>姓名</th><th>职位</th></tr></thead>
<tbody>
<tr v-if="employees.length === 0"><td colspan="3" class="empty-placeholder">暂无员工</td></tr>
<tr v-for="emp in employees" :key="emp.employeeCode">
<td>{{ emp.employeeCode }}</td>
<td>{{ emp.name }}</td>
<td>{{ emp.positionDesc }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
(function() {
const { createApp, ref, onMounted } = Vue;
const initialEmployee = <%= empJson %>;
const app = createApp({
setup() {
const employee = ref(initialEmployee);
// ---------- 图书数据(馆员) ----------
const books = ref([]);
const editingBookId = ref(null);
const bookForm = ref({
isbn: '', title: '', author: '', publisher: '',
publishYear: null, totalStock: null,
targetAudience: '通用', description: ''
});
<%--const loadBooks = async () => {--%>
<%-- try {--%>
<%-- const response = await fetch('${pageContext.request.contextPath}/api/books');--%>
<%-- if (response.ok) books.value = await response.json();--%>
<%-- } catch (e) { console.error('加载图书失败', e); }--%>
<%--};--%>
const loadBooks = async () => {
console.log('>>> 开始加载图书');
try {
const response = await fetch('${pageContext.request.contextPath}/api/books', {
credentials: 'include'
});
console.log('>>> HTTP状态码:', response.status);
if (response.ok) {
books.value = await response.json();
console.log('>>> 图书数量:', books.value.length);
} else {
const errorText = await response.text();
console.error('>>> 错误:', errorText);
alert('加载失败: ' + response.status);
}
} catch (e) {
console.error('>>> 异常:', e);
}
};
const addBook = async () => {
if (!bookForm.value.isbn || !bookForm.value.title) {
alert('ISBN和书名不能为空'); return;
}
const params = new URLSearchParams(bookForm.value);
try {
const response = await fetch('${pageContext.request.contextPath}/api/books', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include'
});
const result = await response.json();
if (result.success) {
alert('添加成功');
await loadBooks();
bookForm.value = { isbn:'',title:'',author:'',publisher:'',publishYear:null,totalStock:null,targetAudience:'通用',description:'' };
} else {
alert(result.message || '添加失败');
}
} catch (e) { alert('网络错误'); }
};
const startEdit = (book) => {
editingBookId.value = book.id;
bookForm.value = {
isbn: book.isbn, title: book.title, author: book.author,
publisher: book.publisher, publishYear: book.publishYear,
totalStock: book.totalStock, targetAudience: book.targetAudience || '通用',
description: book.description || ''
};
};
const updateBook = async () => {
if (!bookForm.value.isbn || !bookForm.value.title) {
alert('ISBN和书名不能为空'); return;
}
const params = new URLSearchParams(bookForm.value);
try {
const response = await fetch('${pageContext.request.contextPath}/api/books/' + editingBookId.value, {
method: 'PUT',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include'
});
const result = await response.json();
if (result.success) {
alert('更新成功');
editingBookId.value = null;
bookForm.value = { isbn:'',title:'',author:'',publisher:'',publishYear:null,totalStock:null,targetAudience:'通用',description:'' };
await loadBooks();
} else {
alert(result.message || '更新失败');
}
} catch (e) { alert('网络错误'); }
};
const cancelEdit = () => {
editingBookId.value = null;
bookForm.value = { isbn:'',title:'',author:'',publisher:'',publishYear:null,totalStock:null,targetAudience:'通用',description:'' };
};
const deleteBook = async (book) => {
if (!confirm('确定删除《' + book.title + '》吗?')) return;
try {
const response = await fetch('${pageContext.request.contextPath}/api/books/' + book.id, { method: 'DELETE' });
const result = await response.json();
if (result.success) { alert('删除成功'); await loadBooks(); }
else { alert(result.message || '删除失败'); }
} catch (e) { alert('网络错误'); }
};
// ---------- 工作人员:注册表单 ----------
const registerForm = ref({
cardNumber: '', name: '', age: null, type: '老年', contact: '',
regDate: new Date().toISOString().slice(0,10)
});
const staffRegister = async () => {
if (!registerForm.value.cardNumber || !registerForm.value.name) {
alert('证号和姓名不能为空'); return;
}
const params = new URLSearchParams(registerForm.value);
try {
const response = await fetch('${pageContext.request.contextPath}/api/staff/register', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include'
});
const result = await response.json();
if (result.success) {
alert('注册成功');
registerForm.value = { cardNumber:'',name:'',age:null,type:'老年',contact:'',regDate:new Date().toISOString().slice(0,10) };
} else {
alert(result.message || '注册失败');
}
} catch (e) { alert('网络错误'); }
};
// ---------- 代借代还 ----------
const staffBorrowedList = ref([]);
const borrowerAccount = ref('');
const quickBorrowIsbn = ref('');
const searchBorrowedStaff = async () => {
if (!borrowerAccount.value) { alert('请输入读者证号'); return; }
try {
const response = await fetch('${pageContext.request.contextPath}/api/borrows?readerCard=' + encodeURIComponent(borrowerAccount.value));
if (response.ok) staffBorrowedList.value = await response.json();
} catch (e) { alert('查询失败'); }
};
const returnBookStaff = async (item) => {
const params = new URLSearchParams({ borrowId: item.borrowId });
try {
const response = await fetch('${pageContext.request.contextPath}/api/borrow', {
method: 'PUT',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include'
});
const result = await response.json();
if (result.success) { alert('归还成功'); await searchBorrowedStaff(); }
else { alert(result.message || '归还失败'); }
} catch (e) { alert('网络错误'); }
};
const quickBorrow = async () => {
if (!borrowerAccount.value) { alert('请先输入读者证号'); return; }
if (!quickBorrowIsbn.value) { alert('请输入ISBN或书名'); return; }
const params = new URLSearchParams({
readerCard: borrowerAccount.value,
isbnOrTitle: quickBorrowIsbn.value
});
try {
const response = await fetch('${pageContext.request.contextPath}/api/borrow/quick', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include'
});
const result = await response.json();
if (result.success) {
alert('快速借书成功');
await searchBorrowedStaff();
quickBorrowIsbn.value = '';
} else { alert(result.message || '快速借书失败'); }
} catch (e) { alert('网络错误'); }
};
// ---------- 经理:活动管理 ----------
const activities = ref([]);
const activityForm = ref({
name: '', startTime: '', endTime: '', location: '',
description: '', maxParticipants: 50
});
const loadActivities = async () => {
try {
const response = await fetch('${pageContext.request.contextPath}/api/activities');
if (response.ok) activities.value = await response.json();
} catch (e) { console.error('加载活动失败', e); }
};
const addActivity = async () => {
if (!activityForm.value.name) { alert('活动名称不能为空'); return; }
const params = new URLSearchParams(activityForm.value);
try {
const response = await fetch('${pageContext.request.contextPath}/api/activities', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include'
});
const result = await response.json();
if (result.success) {
alert('活动添加成功');
await loadActivities();
activityForm.value = { name:'',startTime:'',endTime:'',location:'',description:'',maxParticipants:50 };
} else { alert(result.message || '添加失败'); }
} catch (e) { alert('网络错误'); }
};
const deleteActivity = async (act) => {
if (!confirm('确定删除活动"' + act.name + '"吗?')) return;
try {
const response = await fetch('${pageContext.request.contextPath}/api/activities/' + act.id, { method: 'DELETE' });
const result = await response.json();
if (result.success) { alert('删除成功'); await loadActivities(); }
else { alert(result.message || '删除失败'); }
} catch (e) { alert('网络错误'); }
};
// ---------- 经理:员工管理 ----------
const employees = ref([]);
const newEmployee = ref({ employeeCode: '', name: '', position: '馆员' });
const loadEmployees = async () => {
try {
const response = await fetch('${pageContext.request.contextPath}/api/employees');
if (response.ok) employees.value = await response.json();
} catch (e) { console.error('加载员工失败', e); }
};
const addEmployee = async () => {
// 在 addEmployee 方法中打印
<%--console.log('请求URL:', '${pageContext.request.contextPath}/api/employees');--%>
if (!newEmployee.value.employeeCode || !newEmployee.value.name) {
alert('工号和姓名不能为空'); return;
}
const params = new URLSearchParams(newEmployee.value);
try {
const response = await fetch('${pageContext.request.contextPath}/api/employees', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params,
credentials: 'include'
});
const result = await response.json();
if (result.success) {
alert('员工注册成功');
await loadEmployees();
newEmployee.value = { employeeCode:'',name:'',position:'馆员' };
} else { alert(result.message || '注册失败'); }
} catch (e) { alert('网络错误'); }
};
// ---------- 退出登录 ----------
const logout = () => {
window.location.href = '${pageContext.request.contextPath}/view/Login.jsp';
};
// ---------- 挂载时根据职位加载数据 ----------
// onMounted(() => {
// if (employee.value.positionCode === 1) {
// loadBooks();
// } else if (employee.value.positionCode === 3) {
// loadActivities();
// loadEmployees();
// }
// });
onMounted(() => {
console.log('>>> positionCode:', employee.value.positionCode);
if (employee.value.positionCode === 1) {
loadBooks();
} else if (employee.value.positionCode === 3) {
loadActivities();
loadEmployees();
}
});
return {
employee,
books, bookForm, editingBookId,
startEdit, addBook, updateBook, cancelEdit, deleteBook,
registerForm, staffRegister,
staffBorrowedList, borrowerAccount, searchBorrowedStaff,
returnBookStaff, quickBorrowIsbn, quickBorrow,
activities, activityForm, addActivity, deleteActivity,
employees, newEmployee, addEmployee,
logout
};
}
});
app.mount('#app');
})();
</script>
</body>
</html>

2
WebContent/view/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

18272
WebContent/view/vue3.js Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More