From 2948a25d9f97c6cd6558c69e3855e652e8422254 Mon Sep 17 00:00:00 2001
From: gaoxq <376340421@qq.com>
Date: Thu, 28 Aug 2025 18:09:20 +0800
Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99=E5=A4=8D=E7=8E=B0=E6=96=B9?=
=?UTF-8?q?=E6=B3=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../biz/controller/ApiUserController.java | 18 +
.../com/mini/capi/biz/domain/ApiUser.java | 86 +++++
.../mini/capi/biz/mapper/ApiUserMapper.java | 16 +
.../mini/capi/biz/service/ApiUserService.java | 16 +
.../biz/service/impl/ApiUserServiceImpl.java | 20 ++
.../com/mini/capi/config/AuthInterceptor.java | 9 +-
.../java/com/mini/capi/model/auth/Result.java | 52 +++
src/main/java/com/mini/capi/mybatis/demo.java | 2 +-
.../capi/sys/controller/LoginController.java | 66 ++++
.../sys/controller/LoginPageController.java | 9 +
src/main/resources/mapper/ApiUserMapper.xml | 25 ++
.../static/assets/css/login-style.css | 324 ++++++++++++++++++
.../static/assets/js/login-script.js | 175 ++++++++++
src/main/resources/static/index.html | 67 +++-
src/main/resources/static/views/demo.html | 10 +
15 files changed, 887 insertions(+), 8 deletions(-)
create mode 100644 src/main/java/com/mini/capi/biz/controller/ApiUserController.java
create mode 100644 src/main/java/com/mini/capi/biz/domain/ApiUser.java
create mode 100644 src/main/java/com/mini/capi/biz/mapper/ApiUserMapper.java
create mode 100644 src/main/java/com/mini/capi/biz/service/ApiUserService.java
create mode 100644 src/main/java/com/mini/capi/biz/service/impl/ApiUserServiceImpl.java
create mode 100644 src/main/java/com/mini/capi/model/auth/Result.java
create mode 100644 src/main/java/com/mini/capi/sys/controller/LoginController.java
create mode 100644 src/main/resources/mapper/ApiUserMapper.xml
create mode 100644 src/main/resources/static/assets/css/login-style.css
create mode 100644 src/main/resources/static/assets/js/login-script.js
create mode 100644 src/main/resources/static/views/demo.html
diff --git a/src/main/java/com/mini/capi/biz/controller/ApiUserController.java b/src/main/java/com/mini/capi/biz/controller/ApiUserController.java
new file mode 100644
index 0000000..118ddc2
--- /dev/null
+++ b/src/main/java/com/mini/capi/biz/controller/ApiUserController.java
@@ -0,0 +1,18 @@
+package com.mini.capi.biz.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ *
+ * 前端控制器
+ *
+ *
+ * @author gaoxq
+ * @since 2025-08-28
+ */
+@RestController
+@RequestMapping("/biz/apiUser")
+public class ApiUserController {
+
+}
diff --git a/src/main/java/com/mini/capi/biz/domain/ApiUser.java b/src/main/java/com/mini/capi/biz/domain/ApiUser.java
new file mode 100644
index 0000000..f2f661d
--- /dev/null
+++ b/src/main/java/com/mini/capi/biz/domain/ApiUser.java
@@ -0,0 +1,86 @@
+package com.mini.capi.biz.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ *
+ *
+ *
+ *
+ * @author gaoxq
+ * @since 2025-08-28
+ */
+@Getter
+@Setter
+@TableName("biz_api_user")
+public class ApiUser implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @TableField("create_time")
+ private LocalDateTime createTime;
+
+ /**
+ * 用户编号
+ */
+ @TableId(value = "user_id", type = IdType.AUTO)
+ private String userId;
+
+ /**
+ * 登录名称
+ */
+ @TableField("api_user")
+ private String apiUser;
+
+ /**
+ * 登录密码
+ */
+ @TableField("api_pswd")
+ private String apiPswd;
+
+ /**
+ * 用户名称
+ */
+ @TableField("uname")
+ private String uname;
+
+ /**
+ * 状态
+ */
+ @TableField("ustatus")
+ private String ustatus;
+
+ @TableField("update_time")
+ private LocalDateTime updateTime;
+
+ /**
+ * 租户id
+ */
+ @TableField("f_tenant_id")
+ private String fTenantId;
+
+ /**
+ * 流程id
+ */
+ @TableField("f_flow_id")
+ private String fFlowId;
+
+ /**
+ * 流程任务主键
+ */
+ @TableField("f_flow_task_id")
+ private String fFlowTaskId;
+
+ /**
+ * 流程任务状态
+ */
+ @TableField("f_flow_state")
+ private Integer fFlowState;
+}
diff --git a/src/main/java/com/mini/capi/biz/mapper/ApiUserMapper.java b/src/main/java/com/mini/capi/biz/mapper/ApiUserMapper.java
new file mode 100644
index 0000000..2ddece9
--- /dev/null
+++ b/src/main/java/com/mini/capi/biz/mapper/ApiUserMapper.java
@@ -0,0 +1,16 @@
+package com.mini.capi.biz.mapper;
+
+import com.mini.capi.biz.domain.ApiUser;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ *
+ * Mapper 接口
+ *
+ *
+ * @author gaoxq
+ * @since 2025-08-28
+ */
+public interface ApiUserMapper extends BaseMapper {
+
+}
diff --git a/src/main/java/com/mini/capi/biz/service/ApiUserService.java b/src/main/java/com/mini/capi/biz/service/ApiUserService.java
new file mode 100644
index 0000000..36dbda1
--- /dev/null
+++ b/src/main/java/com/mini/capi/biz/service/ApiUserService.java
@@ -0,0 +1,16 @@
+package com.mini.capi.biz.service;
+
+import com.mini.capi.biz.domain.ApiUser;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ *
+ * 服务类
+ *
+ *
+ * @author gaoxq
+ * @since 2025-08-28
+ */
+public interface ApiUserService extends IService {
+
+}
diff --git a/src/main/java/com/mini/capi/biz/service/impl/ApiUserServiceImpl.java b/src/main/java/com/mini/capi/biz/service/impl/ApiUserServiceImpl.java
new file mode 100644
index 0000000..9ee1a51
--- /dev/null
+++ b/src/main/java/com/mini/capi/biz/service/impl/ApiUserServiceImpl.java
@@ -0,0 +1,20 @@
+package com.mini.capi.biz.service.impl;
+
+import com.mini.capi.biz.domain.ApiUser;
+import com.mini.capi.biz.mapper.ApiUserMapper;
+import com.mini.capi.biz.service.ApiUserService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * 服务实现类
+ *
+ *
+ * @author gaoxq
+ * @since 2025-08-28
+ */
+@Service
+public class ApiUserServiceImpl extends ServiceImpl implements ApiUserService {
+
+}
diff --git a/src/main/java/com/mini/capi/config/AuthInterceptor.java b/src/main/java/com/mini/capi/config/AuthInterceptor.java
index 865e807..355904c 100644
--- a/src/main/java/com/mini/capi/config/AuthInterceptor.java
+++ b/src/main/java/com/mini/capi/config/AuthInterceptor.java
@@ -1,10 +1,11 @@
package com.mini.capi.config;
-import com.mini.capi.utils.vToken;
+import com.mini.capi.biz.domain.ApiUser;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
+import jakarta.servlet.http.HttpSession;
@Component
public class AuthInterceptor implements HandlerInterceptor {
@@ -13,9 +14,9 @@ public class AuthInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
-
- String token = request.getHeader("Authorization");
- if (token == null || !vToken.isValidToken(token)) {
+ HttpSession session = request.getSession();
+ ApiUser apiUser = (ApiUser) session.getAttribute("Authorization");
+ if (apiUser == null) {
response.sendRedirect(request.getContextPath() + "/login");
return false;
}
diff --git a/src/main/java/com/mini/capi/model/auth/Result.java b/src/main/java/com/mini/capi/model/auth/Result.java
new file mode 100644
index 0000000..85bc79d
--- /dev/null
+++ b/src/main/java/com/mini/capi/model/auth/Result.java
@@ -0,0 +1,52 @@
+package com.mini.capi.model.auth;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class Result implements Serializable {
+
+ // 状态码:200表示成功,其他表示错误
+ private int code;
+ // 响应信息
+ private String msg;
+ // 响应数据(可选)
+ private Object data;
+
+ // 私有构造方法,防止直接创建实例
+ private Result() {}
+
+ // 成功响应
+ public static Result success(String msg) {
+ Result result = new Result();
+ result.code = 200;
+ result.msg = msg;
+ return result;
+ }
+
+ // 带数据的成功响应
+ public static Result success(String msg, Object data) {
+ Result result = new Result();
+ result.code = 200;
+ result.msg = msg;
+ result.data = data;
+ return result;
+ }
+
+ // 错误响应
+ public static Result error(String msg) {
+ Result result = new Result();
+ result.code = 500; // 500表示服务器错误,也可以根据实际情况使用其他错误码
+ result.msg = msg;
+ return result;
+ }
+
+ // 带自定义错误码的错误响应
+ public static Result error(int code, String msg) {
+ Result result = new Result();
+ result.code = code;
+ result.msg = msg;
+ return result;
+ }
+}
diff --git a/src/main/java/com/mini/capi/mybatis/demo.java b/src/main/java/com/mini/capi/mybatis/demo.java
index 455c6db..5bf931d 100644
--- a/src/main/java/com/mini/capi/mybatis/demo.java
+++ b/src/main/java/com/mini/capi/mybatis/demo.java
@@ -29,7 +29,7 @@ public class demo {
.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/mapper"));
})
.strategyConfig(builder -> {
- builder.addInclude("biz_sync_task_log")
+ builder.addInclude("biz_api_user")
.addTablePrefix("biz_")
.entityBuilder()
.enableLombok()
diff --git a/src/main/java/com/mini/capi/sys/controller/LoginController.java b/src/main/java/com/mini/capi/sys/controller/LoginController.java
new file mode 100644
index 0000000..33c6ba4
--- /dev/null
+++ b/src/main/java/com/mini/capi/sys/controller/LoginController.java
@@ -0,0 +1,66 @@
+package com.mini.capi.sys.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.mini.capi.biz.domain.ApiUser;
+import com.mini.capi.biz.service.ApiUserService;
+import com.mini.capi.model.auth.Result;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpSession;
+import lombok.Data;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+
+@RestController
+@RequestMapping("/Sys/login")
+public class LoginController {
+
+
+ @Resource
+ private ApiUserService userService;
+
+
+ @Data
+ public static class LoginRequest implements Serializable {
+ private String username;
+ private String password;
+ }
+
+
+ /**
+ * 密码校验(生产环境需替换为加密比对)
+ */
+ private boolean verifyPassword(String rawPassword, String encodedPassword) {
+ return Objects.equals(rawPassword, encodedPassword);
+ }
+
+
+ /**
+ * 用户登录
+ */
+ @PostMapping("/userLogin")
+ public Result login(@RequestBody LoginRequest user, HttpSession session) {
+ try {
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ queryWrapper.eq("api_user", user.getUsername())
+ .eq("ustatus", 1);
+ ApiUser apiUser = userService.getOne(queryWrapper);
+ if (apiUser == null) {
+ return Result.error("账户不存在");
+ }
+ if (!verifyPassword(user.getPassword(), apiUser.getApiPswd())) {
+ // 可记录登录失败日志,用于后续风控
+ return Result.error("账户或密码错误");
+ }
+ session.setAttribute("Authorization", apiUser);
+ return Result.success("登录成功");
+ } catch (Exception e) {
+ return Result.error("登录失败,请稍后重试");
+ }
+ }
+}
diff --git a/src/main/java/com/mini/capi/sys/controller/LoginPageController.java b/src/main/java/com/mini/capi/sys/controller/LoginPageController.java
index 06b984a..661f02f 100644
--- a/src/main/java/com/mini/capi/sys/controller/LoginPageController.java
+++ b/src/main/java/com/mini/capi/sys/controller/LoginPageController.java
@@ -10,4 +10,13 @@ public class LoginPageController {
public String loginPage() {
return "forward:/index.html";
}
+
+
+ /**
+ * 首页
+ */
+ @GetMapping("/welcome")
+ public String welcomePage() {
+ return "forward:/views/demo.html";
+ }
}
diff --git a/src/main/resources/mapper/ApiUserMapper.xml b/src/main/resources/mapper/ApiUserMapper.xml
new file mode 100644
index 0000000..16bd74c
--- /dev/null
+++ b/src/main/resources/mapper/ApiUserMapper.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ create_time, user_id, api_user, api_pswd, uname, ustatus, update_time, f_tenant_id, f_flow_id, f_flow_task_id, f_flow_state
+
+
+
diff --git a/src/main/resources/static/assets/css/login-style.css b/src/main/resources/static/assets/css/login-style.css
new file mode 100644
index 0000000..1cff237
--- /dev/null
+++ b/src/main/resources/static/assets/css/login-style.css
@@ -0,0 +1,324 @@
+/* 全局样式重置:增加box-sizing强制继承,避免尺寸计算偏差 */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ font-family: "Microsoft YaHei", sans-serif;
+}
+
+/* 样式变量:统一管理,减少硬编码 */
+:root {
+ --primary-color: #3498db;
+ --primary-dark: #2980b9;
+ --primary-light: #e3f2fd;
+ --bg-gradient-1: #2c3e50;
+ --bg-gradient-2: #3498db;
+ --white: #fff;
+ --gray-light: #f8f9fa;
+ --gray-border: #ddd;
+ --gray-hover: #f1f1f1;
+ --text-gray: #555;
+ --text-light-gray: #888;
+ --danger-color: #e74c3c;
+ --shadow-normal: 0 10px 30px rgba(0, 0, 0, 0.15);
+ --shadow-modal: 0 10px 25px rgba(0, 0, 0, 0.2);
+ --shadow-input: 0 0 0 3px rgba(52, 152, 219, 0.1);
+ --transition-base: all 0.25s ease;
+ --login-box-width: 420px; /* 固定登录框宽度,避免变形 */
+ --input-height: 48px; /* 固定输入框高度,统一视觉 */
+}
+
+/* 页面主体:固定布局,确保登录框不被挤压 */
+body {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 100vh;
+ background: linear-gradient(135deg, var(--bg-gradient-1), var(--bg-gradient-2));
+ padding: 0 80px;
+ overflow: hidden; /* 禁止页面滚动 */
+}
+
+/* -------------------------- 左侧品牌区域:固定尺寸和位置 -------------------------- */
+.brand-side {
+ width: 55%;
+ max-width: 700px;
+ color: var(--white);
+ padding: 40px;
+ text-align: center;
+ margin-right: 60px;
+}
+
+.brand-title-group {
+ margin-top: 10px;
+}
+
+.brand-side h1 {
+ font-size: 3.2rem;
+ margin-bottom: 15px;
+ text-shadow: 0 3px 12px rgba(0, 0, 0, 0.25);
+ letter-spacing: 1px;
+}
+
+.brand-divider {
+ width: 80px;
+ height: 3px;
+ background: linear-gradient(90deg, transparent, var(--white), transparent);
+ margin: 0 auto 20px;
+ opacity: 0.8;
+}
+
+.brand-side p {
+ font-size: 1.15rem;
+ opacity: 0.95;
+ line-height: 1.7;
+ text-shadow: 0 1px 5px rgba(0, 0, 0, 0.15);
+}
+
+.brand-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 18px;
+ margin-bottom: 25px;
+}
+
+.brand-icon i {
+ font-size: 2.8rem;
+ opacity: 0.95;
+ transition: var(--transition-base);
+}
+
+.brand-icon i:hover {
+ transform: translateY(-6px) scale(1.05);
+ opacity: 1;
+}
+
+/* -------------------------- 右侧登录容器:固定尺寸,彻底解决变形 -------------------------- */
+.login-container {
+ width: var(--login-box-width); /* 固定宽度,不随内容拉伸 */
+ background: var(--white);
+ border-radius: 12px;
+ box-shadow: var(--shadow-normal);
+ overflow: hidden;
+ transition: var(--transition-base);
+ position: relative;
+ z-index: 10;
+}
+
+.login-container:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 12px 35px rgba(0, 0, 0, 0.18);
+}
+
+.login-header {
+ text-align: center;
+ padding: 25px;
+ background: var(--gray-light);
+ border-bottom: 1px solid var(--gray-border);
+}
+
+.login-header h2 {
+ font-size: 1.7rem;
+ color: #333;
+ font-weight: 600;
+ letter-spacing: 0.5px;
+}
+
+/* 表单区域:固定内边距,避免内容溢出 */
+.login-form {
+ padding: 35px 30px;
+ width: 100%;
+}
+
+/* 表单组:固定间距,不随内容变化 */
+.form-group {
+ margin-bottom: 28px;
+ width: 100%;
+}
+
+.required-mark {
+ color: var(--danger-color);
+ margin-left: 4px;
+ font-size: 1rem;
+}
+
+.form-group label {
+ display: block;
+ margin-bottom: 9px;
+ color: var(--text-gray);
+ font-size: 1rem;
+ font-weight: 500;
+}
+
+/* -------------------------- 输入框组:固定高度+定位,彻底解决变形 -------------------------- */
+.input-group {
+ position: relative;
+ display: flex;
+ align-items: center;
+ width: 100%;
+ height: var(--input-height); /* 固定容器高度,与输入框一致 */
+}
+
+/* 输入框:固定高度+内边距,不拉伸 */
+.form-group input {
+ width: 100%;
+ height: 100%; /* 继承容器高度,避免自行拉伸 */
+ padding: 0 18px 0 45px; /* 左侧留足图标空间,右侧留足按钮空间 */
+ border: 1px solid var(--gray-border);
+ border-radius: 8px;
+ font-size: 1rem;
+ outline: none;
+ transition: var(--transition-base);
+ background-color: var(--white);
+ resize: none;
+ appearance: none;
+}
+
+/* 输入框聚焦:仅改变边框和阴影,不改变尺寸 */
+.form-group input:focus {
+ border-color: var(--primary-color);
+ box-shadow: var(--shadow-input);
+ background-color: var(--primary-light);
+}
+
+.form-group input:not(:placeholder-shown) {
+ border-color: #b3d9f2;
+}
+
+/* 输入框图标:固定左侧定位,不随输入框变化 */
+.input-icon {
+ position: absolute;
+ left: 16px; /* 固定左侧距离,不偏移 */
+ color: var(--text-light-gray);
+ font-size: 1.1rem;
+ z-index: 1;
+ width: 20px; /* 固定图标宽度,避免抖动 */
+ text-align: center;
+}
+
+.form-group input:not(:placeholder-shown) + .input-icon {
+ color: var(--primary-color);
+}
+
+/* 密码切换按钮:固定最右侧,不偏移 */
+.toggle-pwd {
+ position: absolute;
+ right: 12px; /* 固定右侧距离 */
+ top: 50%;
+ transform: translateY(-50%); /* 垂直居中 */
+ background: transparent;
+ border: none;
+ color: var(--text-light-gray);
+ cursor: pointer;
+ font-size: 1rem;
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ transition: var(--transition-base);
+ z-index: 2;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.toggle-pwd:hover {
+ background-color: var(--gray-hover);
+ color: var(--text-gray);
+}
+
+/* 登录按钮:固定尺寸,不拉伸 */
+.btn-login {
+ width: 100%;
+ height: 52px; /* 固定按钮高度 */
+ background: var(--primary-color);
+ color: var(--white);
+ border: none;
+ border-radius: 8px;
+ font-size: 1.05rem;
+ font-weight: 500;
+ cursor: pointer;
+ transition: var(--transition-base);
+ letter-spacing: 0.5px;
+ box-shadow: 0 3px 8px rgba(52, 152, 219, 0.2);
+ white-space: nowrap;
+}
+
+.btn-login:hover {
+ background: var(--primary-dark);
+ transform: translateY(-2px);
+ box-shadow: 0 5px 12px rgba(41, 128, 185, 0.25);
+}
+
+.btn-login:active {
+ transform: translateY(0);
+ box-shadow: 0 2px 5px rgba(41, 128, 185, 0.2);
+}
+
+/* -------------------------- 弹窗:固定层级,避免被遮挡 -------------------------- */
+.modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ justify-content: center;
+ align-items: center;
+ z-index: 9999; /* 最高层级,不被任何元素遮挡 */
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.modal.show {
+ opacity: 1;
+}
+
+.modal-content {
+ background: var(--white);
+ padding: 25px;
+ border-radius: 10px;
+ width: 320px;
+ text-align: center;
+ box-shadow: var(--shadow-modal);
+ transform: translateY(-20px);
+ transition: transform 0.3s ease;
+}
+
+.modal.show .modal-content {
+ transform: translateY(0);
+}
+
+.modal-content .icon {
+ font-size: 2.5rem;
+ color: var(--danger-color);
+ margin-bottom: 15px;
+}
+
+.modal-content p {
+ margin-bottom: 20px;
+ color: var(--text-gray);
+ font-size: 1rem;
+ line-height: 1.5;
+}
+
+.modal-content button {
+ padding: 10px 25px;
+ border: none;
+ border-radius: 6px;
+ background: var(--primary-color);
+ color: var(--white);
+ cursor: pointer;
+ transition: var(--transition-base);
+ font-size: 0.95rem;
+}
+
+.modal-content button:hover {
+ background: var(--primary-dark);
+ transform: translateY(-2px);
+}
+
+.modal-content button:active {
+ transform: translateY(0);
+}
diff --git a/src/main/resources/static/assets/js/login-script.js b/src/main/resources/static/assets/js/login-script.js
new file mode 100644
index 0000000..cd15122
--- /dev/null
+++ b/src/main/resources/static/assets/js/login-script.js
@@ -0,0 +1,175 @@
+/**
+ * 登录逻辑处理:阻止表单默认提交,校验用户名/密码,调用登录接口
+ * @param {Event} e - 表单提交事件
+ * @returns {boolean} - 返回false阻止默认提交
+ */
+function handleLogin(e) {
+ // 1. 阻止表单默认提交行为(避免页面刷新)
+ e.preventDefault();
+
+ // 2. 获取并清理用户名、密码(去除前后空格)
+ const username = document.getElementById('username').value.trim();
+ const password = document.getElementById('password').value.trim();
+
+ // 3. 前端简单校验(空值判断)
+ if (!username) {
+ showModal('请输入用户名');
+ return false;
+ }
+ if (!password) {
+ showModal('请输入密码');
+ return false;
+ }
+
+ // 4. 调用后端登录接口(POST请求)
+ fetch('Sys/login/userLogin', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json' // 声明请求体为JSON格式
+ },
+ body: JSON.stringify({username, password}) // 转换为JSON字符串
+ })
+ .then(response => {
+ // 4.1 校验HTTP响应状态(非200-299范围视为错误)
+ if (!response.ok) {
+ throw new Error(`请求失败,状态码:${response.status}`);
+ }
+ // 4.2 解析响应体(假设后端返回JSON格式)
+ return response.json();
+ })
+ .then(data => {
+ // 5. 处理后端返回结果(假设接口返回 { code: 200, msg: '成功' } 结构)
+ if (data.code === 200) {
+ // 登录成功:跳转到demo页面(可根据实际需求修改跳转路径)
+ window.location.href = 'welcome';
+ } else {
+ // 业务错误:显示后端返回的错误信息,无信息时显示默认提示
+ showModal(data.msg || '登录失败,请检查账号密码是否正确');
+ }
+ })
+ .catch(error => {
+ // 6. 捕获请求异常(网络错误、接口500等)
+ showModal(`登录异常:${error.message}`);
+ });
+
+ return false;
+}
+
+/**
+ * 显示错误弹窗
+ * @param {string} msg - 弹窗提示内容
+ */
+function showModal(msg) {
+ const errorMsgElem = document.getElementById('errorMsg');
+ const modalElem = document.getElementById('errorModal');
+
+ // 设置弹窗文本内容
+ errorMsgElem.innerText = msg;
+ // 显示弹窗(通过flex布局实现居中)
+ modalElem.style.display = 'flex';
+ // 延迟添加show类(触发过渡动画,避免动画不生效)
+ setTimeout(() => {
+ modalElem.classList.add('show');
+ }, 10);
+}
+
+/**
+ * 关闭错误弹窗
+ */
+function closeModal() {
+ const modalElem = document.getElementById('errorModal');
+
+ // 移除show类(触发淡出动画)
+ modalElem.classList.remove('show');
+ // 动画结束后隐藏弹窗(300ms与CSS过渡时间保持一致)
+ setTimeout(() => {
+ modalElem.style.display = 'none';
+ }, 300);
+}
+
+/**
+ * 清空指定输入框内容
+ * @param {string} inputId - 输入框的ID属性值
+ */
+function clearInput(inputId) {
+ const inputElem = document.getElementById(inputId);
+ if (inputElem) {
+ // 清空输入框内容
+ inputElem.value = '';
+ // 触发input事件(更新清空按钮的显示状态)
+ inputElem.dispatchEvent(new Event('input'));
+ // 聚焦到当前输入框(提升用户体验)
+ inputElem.focus();
+ }
+}
+
+/**
+ * 切换密码输入框的可见性(明文/密文切换)
+ */
+function togglePasswordVisibility() {
+ const passwordInput = document.getElementById('password');
+ const toggleBtn = passwordInput.parentElement.querySelector('.toggle-pwd i');
+
+ if (passwordInput.type === 'password') {
+ // 切换为明文显示
+ passwordInput.type = 'text';
+ // 更换图标为"眼睛"(表示当前是明文)
+ toggleBtn.classList.remove('bi-eye-slash');
+ toggleBtn.classList.add('bi-eye');
+ } else {
+ // 切换为密文显示
+ passwordInput.type = 'password';
+ // 更换图标为"眼睛斜杠"(表示当前是密文)
+ toggleBtn.classList.remove('bi-eye');
+ toggleBtn.classList.add('bi-eye-slash');
+ }
+}
+
+/**
+ * 输入框内容变化时的动态反馈:控制清空按钮的显示/隐藏
+ * @param {HTMLInputElement} inputElem - 输入框元素
+ */
+function handleInputChange(inputElem) {
+ const clearBtn = inputElem.parentElement.querySelector('.clear-btn');
+ if (clearBtn) {
+ // 输入框有内容时显示清空按钮,无内容时隐藏
+ clearBtn.hidden = !inputElem.value.trim();
+ }
+}
+
+// 页面加载完成后:绑定全局交互事件
+document.addEventListener('DOMContentLoaded', () => {
+ // 1. 获取用户名和密码输入框元素
+ const usernameInput = document.getElementById('username');
+ const passwordInput = document.getElementById('password');
+
+ // 2. 绑定输入框内容变化事件(控制清空按钮显示)
+ if (usernameInput) {
+ usernameInput.addEventListener('input', () => handleInputChange(usernameInput));
+ }
+ if (passwordInput) {
+ passwordInput.addEventListener('input', () => handleInputChange(passwordInput));
+ }
+
+ // 3. 点击弹窗外部关闭弹窗
+ const modalElem = document.getElementById('errorModal');
+ modalElem.addEventListener('click', (e) => {
+ // 仅当点击弹窗背景(而非内容区)时关闭
+ if (e.target === modalElem) {
+ closeModal();
+ }
+ });
+
+ // 4. 按ESC键关闭弹窗
+ document.addEventListener('keydown', (e) => {
+ // 仅当弹窗显示时生效
+ if (e.key === 'Escape' && modalElem.style.display === 'flex') {
+ closeModal();
+ }
+ });
+
+ // 5. 页面初始化时,默认聚焦到用户名输入框(提升用户体验)
+ if (usernameInput) {
+ usernameInput.focus();
+ }
+});
diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html
index 566549b..1068291 100644
--- a/src/main/resources/static/index.html
+++ b/src/main/resources/static/index.html
@@ -1,10 +1,71 @@
-
+
- Title
+
+
+
+ cApi登录
+
+
+
+
+
+
+
+
+
cApi管理系统
+
+
安全、高效的一站式管理解决方案,为您的业务保驾护航
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/src/main/resources/static/views/demo.html b/src/main/resources/static/views/demo.html
new file mode 100644
index 0000000..566549b
--- /dev/null
+++ b/src/main/resources/static/views/demo.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Title
+
+
+
+
+
\ No newline at end of file