邮件API
This commit is contained in:
@@ -1,19 +1,31 @@
|
||||
package com.mini.capi.api.job;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.mini.capi.biz.domain.BizDeviceInfo;
|
||||
import com.mini.capi.biz.domain.BizMonitorAccount;
|
||||
import com.mini.capi.biz.domain.BizMonitorHost;
|
||||
import com.mini.capi.biz.domain.BizServerInfo;
|
||||
import com.mini.capi.biz.service.BizDeviceInfoService;
|
||||
import com.mini.capi.biz.service.BizMonitorAccountService;
|
||||
import com.mini.capi.biz.service.BizMonitorHostService;
|
||||
import com.mini.capi.biz.service.BizServerInfoService;
|
||||
import com.mini.capi.model.ApiResult;
|
||||
import com.mini.capi.model.info.CpuInfo;
|
||||
import com.mini.capi.model.info.DiskInfo;
|
||||
import com.mini.capi.model.info.ServerInfo;
|
||||
import com.mini.capi.utils.NetworkUtils;
|
||||
import com.mini.capi.utils.SystemInfoUtil;
|
||||
import com.mini.capi.utils.vId;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -25,6 +37,16 @@ public class jobController {
|
||||
@Resource
|
||||
private BizMonitorHostService bizMonitorHostService;
|
||||
|
||||
|
||||
@Resource
|
||||
private BizMonitorAccountService bizMonitorAccountService;
|
||||
|
||||
@Resource
|
||||
private BizServerInfoService bizServerInfoService;
|
||||
|
||||
@Resource
|
||||
private BizDeviceInfoService bizDeviceInfoService;
|
||||
|
||||
// 注入配置好的线程池
|
||||
@Resource(name = "hostMonitorExecutor")
|
||||
private ThreadPoolTaskExecutor hostMonitorExecutor;
|
||||
@@ -44,6 +66,7 @@ public class jobController {
|
||||
boolean isReachable = NetworkUtils.isNetworkReachable(monitorHost.getIpAddress());
|
||||
monitorHost.setUstatus(isReachable ? "1" : "0");
|
||||
if (isReachable) {
|
||||
syncServerInfo(monitorHost);
|
||||
monitorHost.setLastOnlineTime(LocalDateTime.now());
|
||||
}
|
||||
bizMonitorHostService.update(monitorHost, updateWrapper);
|
||||
@@ -53,7 +76,6 @@ public class jobController {
|
||||
}, hostMonitorExecutor); // 指定使用配置的线程池
|
||||
futures.add(future);
|
||||
}
|
||||
|
||||
try {
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||
.get(60, TimeUnit.SECONDS); // 超时时间可根据业务调整
|
||||
@@ -62,4 +84,68 @@ public class jobController {
|
||||
return ApiResult.error(101, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void syncServerInfo(BizMonitorHost host) {
|
||||
try {
|
||||
QueryWrapper<BizMonitorAccount> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("host_id", host.getHostId()).eq("ssh_username", "ogsapp");
|
||||
BizMonitorAccount account = bizMonitorAccountService.getOne(queryWrapper);
|
||||
if (account != null) {
|
||||
CpuInfo cpuInfo = SystemInfoUtil.getCpuMemUsage(host.getIpAddress(), account.getSshPort(), account.getSshUsername(), account.getSshPassword());
|
||||
ServerInfo info = SystemInfoUtil.getServerBasicInfo(host.getIpAddress(), account.getSshPort(), account.getSshUsername(), account.getSshPassword(), host.getIpAddress());
|
||||
List<DiskInfo> diskInfos = SystemInfoUtil.getDiskInfos(host.getIpAddress(), account.getSshPort(), account.getSshUsername(), account.getSshPassword());
|
||||
syncDeviceInfo(host, diskInfos);
|
||||
Optional<BizServerInfo> serverInfoOpt = Optional.ofNullable(
|
||||
bizServerInfoService.getOne(new QueryWrapper<BizServerInfo>().eq("host_id", host.getHostId()))
|
||||
);
|
||||
BizServerInfo serverInfo = serverInfoOpt.orElseGet(() -> {
|
||||
BizServerInfo newInfo = new BizServerInfo();
|
||||
newInfo.setHostId(host.getHostId()); // 初始化唯一标识
|
||||
return newInfo;
|
||||
});
|
||||
serverInfo.setUptime(info.getUptime());
|
||||
serverInfo.setOs(info.getOs());
|
||||
serverInfo.setKernelVersion(info.getKernelVersion());
|
||||
serverInfo.setHostname(info.getHostname());
|
||||
serverInfo.setIpAddress(info.getIpAddress());
|
||||
serverInfo.setCpuModel(info.getCpuModel());
|
||||
serverInfo.setMemoryTotal(info.getMemoryTotal());
|
||||
|
||||
serverInfo.setCpuUsage(cpuInfo.getCpuUsage());
|
||||
serverInfo.setMemoryUsage(cpuInfo.getMemoryUsage());
|
||||
serverInfo.setLastOnlineTime(LocalDateTime.now());
|
||||
|
||||
UpdateWrapper<BizServerInfo> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("host_id", host.getHostId());
|
||||
bizServerInfoService.saveOrUpdate(serverInfo, updateWrapper);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.print(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void syncDeviceInfo(BizMonitorHost host, List<DiskInfo> diskInfos) {
|
||||
for (DiskInfo diskInfo : diskInfos) {
|
||||
Optional<BizDeviceInfo> deviceInfoOpt = Optional.ofNullable(
|
||||
bizDeviceInfoService.getOne(new QueryWrapper<BizDeviceInfo>().eq("host_id", host.getHostId()).eq("device", diskInfo.getDevice()).eq("mount_point", diskInfo.getMountPoint()))
|
||||
);
|
||||
BizDeviceInfo deviceInfo = deviceInfoOpt.orElseGet(() -> {
|
||||
BizDeviceInfo newInfo = new BizDeviceInfo();
|
||||
newInfo.setHostId(host.getHostId()); // 初始化唯一标识
|
||||
return newInfo;
|
||||
});
|
||||
deviceInfo.setDevice(diskInfo.getDevice());
|
||||
deviceInfo.setMountPoint(diskInfo.getMountPoint());
|
||||
deviceInfo.setTotalSize(diskInfo.getTotalSize());
|
||||
deviceInfo.setUsedSize(diskInfo.getUsedSize());
|
||||
deviceInfo.setUsageRate(diskInfo.getUsageRate());
|
||||
deviceInfo.setLastOnlineTime(LocalDateTime.now());
|
||||
|
||||
UpdateWrapper<BizDeviceInfo> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("host_id", host.getHostId()).eq("device", diskInfo.getDevice()).eq("mount_point", diskInfo.getMountPoint());
|
||||
bizDeviceInfoService.saveOrUpdate(deviceInfo, updateWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,7 @@ public class sysController {
|
||||
|
||||
|
||||
@Resource
|
||||
private AppRuntimeInfo appRuntimeInfo;
|
||||
@Resource
|
||||
private AppPathInfo appPathInfo;
|
||||
@Resource
|
||||
private AppProcessInfo appProcessInfo;
|
||||
@Resource
|
||||
private AppNetworkInfo appNetworkInfo;
|
||||
private sysService service;
|
||||
|
||||
/**
|
||||
* 运行信息
|
||||
@@ -28,35 +22,7 @@ public class sysController {
|
||||
@GetMapping("getRunInfo")
|
||||
public ApiResult<?> getRunInfo() {
|
||||
try {
|
||||
// 用构建者模式链式创建对象,避免重复的参数传递和构造函数调用
|
||||
RunInfo runInfo = RunInfo.builder()
|
||||
.runtimeInfo(RuntimeInfo.builder()
|
||||
.appName(appRuntimeInfo.getAppName())
|
||||
.serverPort(appRuntimeInfo.getServerPort())
|
||||
.activeProfiles(appRuntimeInfo.getActiveProfiles())
|
||||
.springBootVersion(appRuntimeInfo.getSpringBootVersion())
|
||||
.jdkVersion(appRuntimeInfo.getJdkVersion())
|
||||
.build())
|
||||
.pathInfo(PathInfo.builder()
|
||||
.workDir(appPathInfo.getWorkDir())
|
||||
.jarDir(appPathInfo.getJarDir())
|
||||
.resourceDir(appPathInfo.getResourceDir())
|
||||
.build())
|
||||
.processInfo(ProcessInfo.builder()
|
||||
.pid(appProcessInfo.getPid())
|
||||
.uptime(appProcessInfo.getUptime())
|
||||
.usedHeapMemory(appProcessInfo.getUsedHeapMemory())
|
||||
.maxHeapMemory(appProcessInfo.getMaxHeapMemory())
|
||||
.jvmArgs(appProcessInfo.getJvmArgs())
|
||||
.build())
|
||||
.networkInfo(NetworkInfo.builder()
|
||||
.hostName(appNetworkInfo.getHostName())
|
||||
.localIp(appNetworkInfo.getLocalIp())
|
||||
.allIps(appNetworkInfo.getAllIps())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
return ApiResult.success(runInfo);
|
||||
return ApiResult.success(service.getRunInfo());
|
||||
} catch (Exception e) {
|
||||
return ApiResult.error(101, e.getMessage());
|
||||
}
|
||||
|
||||
54
src/main/java/com/mini/capi/api/sys/sysService.java
Normal file
54
src/main/java/com/mini/capi/api/sys/sysService.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package com.mini.capi.api.sys;
|
||||
|
||||
import com.mini.capi.config.component.AppNetworkInfo;
|
||||
import com.mini.capi.config.component.AppPathInfo;
|
||||
import com.mini.capi.config.component.AppProcessInfo;
|
||||
import com.mini.capi.config.component.AppRuntimeInfo;
|
||||
import com.mini.capi.model.info.*;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class sysService {
|
||||
|
||||
|
||||
@Resource
|
||||
private AppRuntimeInfo appRuntimeInfo;
|
||||
@Resource
|
||||
private AppPathInfo appPathInfo;
|
||||
@Resource
|
||||
private AppProcessInfo appProcessInfo;
|
||||
@Resource
|
||||
private AppNetworkInfo appNetworkInfo;
|
||||
|
||||
|
||||
public RunInfo getRunInfo() throws Exception {
|
||||
RunInfo runInfo = RunInfo.builder()
|
||||
.runtimeInfo(RuntimeInfo.builder()
|
||||
.appName(appRuntimeInfo.getAppName())
|
||||
.serverPort(appRuntimeInfo.getServerPort())
|
||||
.activeProfiles(appRuntimeInfo.getActiveProfiles())
|
||||
.springBootVersion(appRuntimeInfo.getSpringBootVersion())
|
||||
.jdkVersion(appRuntimeInfo.getJdkVersion())
|
||||
.build())
|
||||
.pathInfo(PathInfo.builder()
|
||||
.workDir(appPathInfo.getWorkDir())
|
||||
.jarDir(appPathInfo.getJarDir())
|
||||
.resourceDir(appPathInfo.getResourceDir())
|
||||
.build())
|
||||
.processInfo(ProcessInfo.builder()
|
||||
.pid(appProcessInfo.getPid())
|
||||
.uptime(appProcessInfo.getUptime())
|
||||
.usedHeapMemory(appProcessInfo.getUsedHeapMemory())
|
||||
.maxHeapMemory(appProcessInfo.getMaxHeapMemory())
|
||||
.jvmArgs(appProcessInfo.getJvmArgs())
|
||||
.build())
|
||||
.networkInfo(NetworkInfo.builder()
|
||||
.hostName(appNetworkInfo.getHostName())
|
||||
.localIp(appNetworkInfo.getLocalIp())
|
||||
.allIps(appNetworkInfo.getAllIps())
|
||||
.build())
|
||||
.build();
|
||||
return runInfo;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,80 @@
|
||||
package com.mini.capi.biz;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.mini.capi.biz.domain.BizHomeUser;
|
||||
import com.mini.capi.biz.service.BizHomeUserService;
|
||||
import com.mini.capi.model.auth.LoginRequest;
|
||||
import com.mini.capi.model.info.TodoHandleDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
@Controller
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/biz")
|
||||
public class LoginController {
|
||||
|
||||
|
||||
@GetMapping("/login")
|
||||
public String showLoginPage() {
|
||||
return "login";
|
||||
@Resource
|
||||
private BizHomeUserService userService;
|
||||
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
@PostMapping("/userLogin")
|
||||
public Map<String, Object> userLogin(@RequestBody LoginRequest loginRequest, HttpServletRequest request, HttpServletResponse response) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
String username = loginRequest.getUsername();
|
||||
String password = loginRequest.getPassword();
|
||||
|
||||
QueryWrapper<BizHomeUser> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_name", username).eq("password", password);
|
||||
BizHomeUser user = userService.getOne(queryWrapper);
|
||||
if (user != null) {
|
||||
request.getSession().setAttribute("currentUser", user);
|
||||
// 3.3 返回成功响应
|
||||
result.put("success", true);
|
||||
result.put("message", "登录成功");
|
||||
} else {
|
||||
result.put("success", false);
|
||||
result.put("message", "账号或密码错误");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
result.put("success", false);
|
||||
result.put("message", "服务器内部错误," + e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/swagger")
|
||||
public String redirectToSwagger() {
|
||||
return "redirect:/swagger-ui/index.html";
|
||||
/**
|
||||
* 处理退出登录请求
|
||||
* 对应前端 fetch('/logout', {method: 'POST'})
|
||||
*/
|
||||
@PostMapping("/logout")
|
||||
public ResponseEntity<Void> logout(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
|
||||
try {
|
||||
// 3. 清除Session中的用户信息
|
||||
if (session != null) {
|
||||
session.removeAttribute("currentUser"); // 清除之前存入的currentUser
|
||||
session.invalidate(); // 使Session失效
|
||||
}
|
||||
// 5. 返回成功响应(200 OK)
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (Exception e) {
|
||||
// 处理异常情况
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 设备存储信息表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/bizDeviceInfo")
|
||||
public class BizDeviceInfoController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件夹信息表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/bizFileFolders")
|
||||
public class BizFileFoldersController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件信息表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/bizFiles")
|
||||
public class BizFilesController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-14
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/bizHomeUser")
|
||||
public class BizHomeUserController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 快捷登录系统信息表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-15
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/bizQuickLogin")
|
||||
public class BizQuickLoginController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务器信息表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/bizServerInfo")
|
||||
public class BizServerInfoController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 待办任务子表(关联主任务) 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/bizSubTask")
|
||||
public class BizSubTaskController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* VIEW 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/bizTodoTaskView")
|
||||
public class BizTodoTaskViewController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表字段信息表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/dataTableField")
|
||||
public class DataTableFieldController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mini.capi.biz.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表基础信息表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/biz/dataTableInfo")
|
||||
public class DataTableInfoController {
|
||||
|
||||
}
|
||||
83
src/main/java/com/mini/capi/biz/domain/BizDeviceInfo.java
Normal file
83
src/main/java/com/mini/capi/biz/domain/BizDeviceInfo.java
Normal file
@@ -0,0 +1,83 @@
|
||||
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.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 设备存储信息表
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("biz_device_info")
|
||||
public class BizDeviceInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 记录时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 设备
|
||||
*/
|
||||
@TableField("device")
|
||||
private String device;
|
||||
|
||||
/**
|
||||
* 挂载点
|
||||
*/
|
||||
@TableField("mount_point")
|
||||
private String mountPoint;
|
||||
|
||||
/**
|
||||
* 总容量
|
||||
*/
|
||||
@TableField("total_size")
|
||||
private String totalSize;
|
||||
|
||||
/**
|
||||
* 已使用容量
|
||||
*/
|
||||
@TableField("used_size")
|
||||
private String usedSize;
|
||||
|
||||
/**
|
||||
* 使用率(%)
|
||||
*/
|
||||
@TableField("usage_rate")
|
||||
private double usageRate;
|
||||
|
||||
/**
|
||||
* 最后一次检测到在线的时间
|
||||
*/
|
||||
@TableField("last_online_time")
|
||||
private LocalDateTime lastOnlineTime;
|
||||
|
||||
/**
|
||||
* 主机唯一标识
|
||||
*/
|
||||
@TableField("host_id")
|
||||
private String hostId;
|
||||
}
|
||||
74
src/main/java/com/mini/capi/biz/domain/BizFileFolders.java
Normal file
74
src/main/java/com/mini/capi/biz/domain/BizFileFolders.java
Normal file
@@ -0,0 +1,74 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件夹信息表
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("biz_file_folders")
|
||||
public class BizFileFolders implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 文件夹ID
|
||||
*/
|
||||
@TableId(value = "folder_id", type = IdType.AUTO)
|
||||
private String folderId;
|
||||
|
||||
/**
|
||||
* 文件夹名称
|
||||
*/
|
||||
@TableField("folder_name")
|
||||
private String folderName;
|
||||
|
||||
/**
|
||||
* 父文件夹ID(0表示根目录)
|
||||
*/
|
||||
@TableField("parent_id")
|
||||
private Integer parentId;
|
||||
|
||||
/**
|
||||
* 创建人ID(关联用户表)
|
||||
*/
|
||||
@TableField("creator_id")
|
||||
private Integer creatorId;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField("update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除(0-未删除,1-已删除)
|
||||
*/
|
||||
@TableField("is_deleted")
|
||||
private Byte isDeleted;
|
||||
|
||||
/**
|
||||
* 文件夹描述
|
||||
*/
|
||||
@TableField("description")
|
||||
private String description;
|
||||
}
|
||||
98
src/main/java/com/mini/capi/biz/domain/BizFiles.java
Normal file
98
src/main/java/com/mini/capi/biz/domain/BizFiles.java
Normal file
@@ -0,0 +1,98 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件信息表
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("biz_files")
|
||||
public class BizFiles implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 文件ID
|
||||
*/
|
||||
@TableId(value = "file_id", type = IdType.AUTO)
|
||||
private String fileId;
|
||||
|
||||
/**
|
||||
* 文件名称(含扩展名)
|
||||
*/
|
||||
@TableField("file_name")
|
||||
private String fileName;
|
||||
|
||||
/**
|
||||
* 所属文件夹ID(关联folders表)
|
||||
*/
|
||||
@TableField("folder_id")
|
||||
private String folderId;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
@TableField("file_size")
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 文件类型(如:image/jpeg、application/pdf)
|
||||
*/
|
||||
@TableField("file_type")
|
||||
private String fileType;
|
||||
|
||||
/**
|
||||
* 文件存储路径(物理路径或云存储URL)
|
||||
*/
|
||||
@TableField("file_path")
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
* 上传人ID(关联用户表)
|
||||
*/
|
||||
@TableField("creator_id")
|
||||
private Integer creatorId;
|
||||
|
||||
/**
|
||||
* 上传时间
|
||||
*/
|
||||
@TableField("upload_time")
|
||||
private LocalDateTime uploadTime;
|
||||
|
||||
/**
|
||||
* 更新时间(如修改名称)
|
||||
*/
|
||||
@TableField("update_time")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* 是否删除(0-未删除,1-已删除)
|
||||
*/
|
||||
@TableField("is_deleted")
|
||||
private Byte isDeleted;
|
||||
|
||||
/**
|
||||
* 文件版本号(用于版本控制)
|
||||
*/
|
||||
@TableField("version")
|
||||
private Integer version;
|
||||
|
||||
/**
|
||||
* 文件MD5值(用于去重校验)
|
||||
*/
|
||||
@TableField("md5")
|
||||
private String md5;
|
||||
}
|
||||
41
src/main/java/com/mini/capi/biz/domain/BizHomeUser.java
Normal file
41
src/main/java/com/mini/capi/biz/domain/BizHomeUser.java
Normal file
@@ -0,0 +1,41 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
*
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-14
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("biz_home_user")
|
||||
public class BizHomeUser 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("user_name")
|
||||
private String userName;
|
||||
|
||||
@TableField("password")
|
||||
private String password;
|
||||
|
||||
@TableField("uname")
|
||||
private String uname;
|
||||
}
|
||||
104
src/main/java/com/mini/capi/biz/domain/BizQuickLogin.java
Normal file
104
src/main/java/com/mini/capi/biz/domain/BizQuickLogin.java
Normal file
@@ -0,0 +1,104 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 快捷登录系统信息表
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-15
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("biz_quick_login")
|
||||
public class BizQuickLogin implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 自增主键
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 系统名称
|
||||
*/
|
||||
@TableField("system_name")
|
||||
private String systemName;
|
||||
|
||||
/**
|
||||
* 首页地址
|
||||
*/
|
||||
@TableField("homepage_url")
|
||||
private String homepageUrl;
|
||||
|
||||
/**
|
||||
* 图标类名(Font Awesome)
|
||||
*/
|
||||
@TableField("icon_class")
|
||||
private String iconClass;
|
||||
|
||||
/**
|
||||
* 图标颜色(关联Tailwind配置)
|
||||
*/
|
||||
@TableField("icon_color")
|
||||
private String iconColor;
|
||||
|
||||
/**
|
||||
* 排序序号(升序排列)
|
||||
*/
|
||||
@TableField("sort_order")
|
||||
private Integer sortOrder;
|
||||
|
||||
/**
|
||||
* 是否启用(1:启用 0:禁用)
|
||||
*/
|
||||
@TableField("is_enabled")
|
||||
private Integer isEnabled;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
106
src/main/java/com/mini/capi/biz/domain/BizServerInfo.java
Normal file
106
src/main/java/com/mini/capi/biz/domain/BizServerInfo.java
Normal file
@@ -0,0 +1,106 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务器信息表
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("biz_server_info")
|
||||
public class BizServerInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 记录时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 主机运行时间
|
||||
*/
|
||||
@TableField("uptime")
|
||||
private String uptime;
|
||||
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
@TableField("os")
|
||||
private String os;
|
||||
|
||||
/**
|
||||
* 内核版本
|
||||
*/
|
||||
@TableField("kernel_version")
|
||||
private String kernelVersion;
|
||||
|
||||
/**
|
||||
* 主机名
|
||||
*/
|
||||
@TableField("hostname")
|
||||
private String hostname;
|
||||
|
||||
/**
|
||||
* IP地址
|
||||
*/
|
||||
@TableField("ip_address")
|
||||
private String ipAddress;
|
||||
|
||||
/**
|
||||
* CPU型号
|
||||
*/
|
||||
@TableField("cpu_model")
|
||||
private String cpuModel;
|
||||
|
||||
/**
|
||||
* 内存总量
|
||||
*/
|
||||
@TableField("memory_total")
|
||||
private String memoryTotal;
|
||||
|
||||
/**
|
||||
* CPU使用率(%)
|
||||
*/
|
||||
@TableField("cpu_usage")
|
||||
private Double cpuUsage;
|
||||
|
||||
/**
|
||||
* 内存使用率(%)
|
||||
*/
|
||||
@TableField("memory_usage")
|
||||
private Double memoryUsage;
|
||||
|
||||
/**
|
||||
* 最后一次检测到在线的时间
|
||||
*/
|
||||
@TableField("last_online_time")
|
||||
private LocalDateTime lastOnlineTime;
|
||||
|
||||
/**
|
||||
* 主机唯一标识
|
||||
*/
|
||||
@TableField("host_id")
|
||||
private String hostId;
|
||||
}
|
||||
95
src/main/java/com/mini/capi/biz/domain/BizSubTask.java
Normal file
95
src/main/java/com/mini/capi/biz/domain/BizSubTask.java
Normal file
@@ -0,0 +1,95 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 待办任务子表(关联主任务)
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("biz_sub_task")
|
||||
public class BizSubTask implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 记录时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 唯一标识
|
||||
*/
|
||||
@TableId(value = "sub_task_id", type = IdType.AUTO)
|
||||
private String subTaskId;
|
||||
|
||||
/**
|
||||
* 关联的主任务ID(外键)
|
||||
*/
|
||||
@TableField("parent_task_id")
|
||||
private String parentTaskId;
|
||||
|
||||
/**
|
||||
* 子任务详明细
|
||||
*/
|
||||
@TableField("sub_task_name")
|
||||
private String subTaskName;
|
||||
|
||||
/**
|
||||
* 子任务优先级
|
||||
*/
|
||||
@TableField("priority")
|
||||
private String priority;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
@TableField("ustatus")
|
||||
private String ustatus;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
@TableField("finish_time")
|
||||
private LocalDateTime finishTime;
|
||||
|
||||
@TableField("remark")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 租户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;
|
||||
}
|
||||
120
src/main/java/com/mini/capi/biz/domain/BizTodoTaskView.java
Normal file
120
src/main/java/com/mini/capi/biz/domain/BizTodoTaskView.java
Normal file
@@ -0,0 +1,120 @@
|
||||
package com.mini.capi.biz.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* VIEW
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("biz_todo_task_view")
|
||||
public class BizTodoTaskView implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 记录时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 唯一标识
|
||||
*/
|
||||
@TableField("task_id")
|
||||
private String taskId;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
*/
|
||||
@TableField("task_name")
|
||||
private String taskName;
|
||||
|
||||
/**
|
||||
* 任务详细描述
|
||||
*/
|
||||
@TableField("task_desc")
|
||||
private String taskDesc;
|
||||
|
||||
/**
|
||||
* 任务截止时间
|
||||
*/
|
||||
@TableField("deadline")
|
||||
private LocalDateTime deadline;
|
||||
|
||||
/**
|
||||
* 待办人员
|
||||
*/
|
||||
@TableField("todo_user")
|
||||
private String todoUser;
|
||||
|
||||
/**
|
||||
* 创建人员
|
||||
*/
|
||||
@TableField("login_user")
|
||||
private String loginUser;
|
||||
|
||||
/**
|
||||
* 租户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;
|
||||
|
||||
/**
|
||||
* 唯一标识
|
||||
*/
|
||||
@TableField("sub_task_id")
|
||||
private String subTaskId;
|
||||
|
||||
/**
|
||||
* 子任务详明细
|
||||
*/
|
||||
@TableField("sub_task_name")
|
||||
private String subTaskName;
|
||||
|
||||
/**
|
||||
* 子任务优先级
|
||||
*/
|
||||
@TableField("priority")
|
||||
private String priority;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
@TableField("ustatus")
|
||||
private String ustatus;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
@TableField("finish_time")
|
||||
private LocalDateTime finishTime;
|
||||
}
|
||||
98
src/main/java/com/mini/capi/biz/domain/DataTableField.java
Normal file
98
src/main/java/com/mini/capi/biz/domain/DataTableField.java
Normal file
@@ -0,0 +1,98 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表字段信息表
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("data_table_field")
|
||||
public class DataTableField implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 字段唯一标识(主键)
|
||||
*/
|
||||
@TableId(value = "field_id", type = IdType.AUTO)
|
||||
private String fieldId;
|
||||
|
||||
/**
|
||||
* 关联的数据表ID(外键,关联data_table_info.table_id)
|
||||
*/
|
||||
@TableField("table_id")
|
||||
private String tableId;
|
||||
|
||||
/**
|
||||
* 字段序号(表示字段在表中的顺序)
|
||||
*/
|
||||
@TableField("field_order")
|
||||
private Integer fieldOrder;
|
||||
|
||||
/**
|
||||
* 字段类型(如:int、varchar、datetime等)
|
||||
*/
|
||||
@TableField("field_type")
|
||||
private String fieldType;
|
||||
|
||||
/**
|
||||
* 字段名称
|
||||
*/
|
||||
@TableField("field_name")
|
||||
private String fieldName;
|
||||
|
||||
/**
|
||||
* 字段长度(如varchar(50)中的50,数值型可表示精度)
|
||||
*/
|
||||
@TableField("field_length")
|
||||
private Integer fieldLength;
|
||||
|
||||
/**
|
||||
* 字段说明
|
||||
*/
|
||||
@TableField("field_remark")
|
||||
private String fieldRemark;
|
||||
|
||||
/**
|
||||
* 租户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;
|
||||
}
|
||||
111
src/main/java/com/mini/capi/biz/domain/DataTableInfo.java
Normal file
111
src/main/java/com/mini/capi/biz/domain/DataTableInfo.java
Normal file
@@ -0,0 +1,111 @@
|
||||
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.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表基础信息表
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("data_table_info")
|
||||
public class DataTableInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 数据表唯一标识(主键)
|
||||
*/
|
||||
@TableId(value = "table_id", type = IdType.AUTO)
|
||||
private String tableId;
|
||||
|
||||
/**
|
||||
* 数据表名称
|
||||
*/
|
||||
@TableField("table_name")
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 数据表描述
|
||||
*/
|
||||
@TableField("table_comment")
|
||||
private String tableComment;
|
||||
|
||||
/**
|
||||
* 数据表大小(单位:MB)
|
||||
*/
|
||||
@TableField("table_size")
|
||||
private BigDecimal tableSize;
|
||||
|
||||
/**
|
||||
* 数据来源(如:业务系统、文件导入等)
|
||||
*/
|
||||
@TableField("data_source")
|
||||
private String dataSource;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField("creator")
|
||||
private String creator;
|
||||
|
||||
/**
|
||||
* 记录条数
|
||||
*/
|
||||
@TableField("data_rows")
|
||||
private Long dataRows;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@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;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
@TableField("remarks")
|
||||
private String remarks;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.mini.capi.biz.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@@ -41,4 +42,18 @@ public class ErpSummaryAllView implements Serializable {
|
||||
|
||||
@TableField("f_cycle")
|
||||
private String fCycle;
|
||||
|
||||
|
||||
public BigDecimal getThisValue() {
|
||||
return thisValue != null ? thisValue : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
public BigDecimal getPrevValue() {
|
||||
return prevValue != null ? prevValue : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getMomRate() {
|
||||
return momRate != null ? momRate : BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.mini.capi.biz.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.BizDeviceInfo;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 设备存储信息表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizDeviceInfoMapper extends BaseMapper<BizDeviceInfo> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.BizFileFolders;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件夹信息表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizFileFoldersMapper extends BaseMapper<BizFileFolders> {
|
||||
|
||||
}
|
||||
16
src/main/java/com/mini/capi/biz/mapper/BizFilesMapper.java
Normal file
16
src/main/java/com/mini/capi/biz/mapper/BizFilesMapper.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.BizFiles;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件信息表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizFilesMapper extends BaseMapper<BizFiles> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.BizHomeUser;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-14
|
||||
*/
|
||||
public interface BizHomeUserMapper extends BaseMapper<BizHomeUser> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.BizQuickLogin;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 快捷登录系统信息表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-15
|
||||
*/
|
||||
public interface BizQuickLoginMapper extends BaseMapper<BizQuickLogin> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.BizServerInfo;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务器信息表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizServerInfoMapper extends BaseMapper<BizServerInfo> {
|
||||
|
||||
}
|
||||
16
src/main/java/com/mini/capi/biz/mapper/BizSubTaskMapper.java
Normal file
16
src/main/java/com/mini/capi/biz/mapper/BizSubTaskMapper.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.BizSubTask;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 待办任务子表(关联主任务) Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizSubTaskMapper extends BaseMapper<BizSubTask> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.BizTodoTaskView;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* VIEW Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizTodoTaskViewMapper extends BaseMapper<BizTodoTaskView> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.DataTableField;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表字段信息表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface DataTableFieldMapper extends BaseMapper<DataTableField> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.mapper;
|
||||
|
||||
import com.mini.capi.biz.domain.DataTableInfo;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表基础信息表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface DataTableInfoMapper extends BaseMapper<DataTableInfo> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.BizDeviceInfo;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 设备存储信息表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizDeviceInfoService extends IService<BizDeviceInfo> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.BizFileFolders;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件夹信息表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizFileFoldersService extends IService<BizFileFolders> {
|
||||
|
||||
}
|
||||
16
src/main/java/com/mini/capi/biz/service/BizFilesService.java
Normal file
16
src/main/java/com/mini/capi/biz/service/BizFilesService.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.BizFiles;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件信息表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizFilesService extends IService<BizFiles> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.BizHomeUser;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-14
|
||||
*/
|
||||
public interface BizHomeUserService extends IService<BizHomeUser> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.BizQuickLogin;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 快捷登录系统信息表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-15
|
||||
*/
|
||||
public interface BizQuickLoginService extends IService<BizQuickLogin> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.BizServerInfo;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务器信息表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizServerInfoService extends IService<BizServerInfo> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.BizSubTask;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 待办任务子表(关联主任务) 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizSubTaskService extends IService<BizSubTask> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.BizTodoTaskView;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* VIEW 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface BizTodoTaskViewService extends IService<BizTodoTaskView> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.DataTableField;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表字段信息表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface DataTableFieldService extends IService<DataTableField> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mini.capi.biz.service;
|
||||
|
||||
import com.mini.capi.biz.domain.DataTableInfo;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表基础信息表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
public interface DataTableInfoService extends IService<DataTableInfo> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.BizDeviceInfo;
|
||||
import com.mini.capi.biz.mapper.BizDeviceInfoMapper;
|
||||
import com.mini.capi.biz.service.BizDeviceInfoService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 设备存储信息表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Service
|
||||
public class BizDeviceInfoServiceImpl extends ServiceImpl<BizDeviceInfoMapper, BizDeviceInfo> implements BizDeviceInfoService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.BizFileFolders;
|
||||
import com.mini.capi.biz.mapper.BizFileFoldersMapper;
|
||||
import com.mini.capi.biz.service.BizFileFoldersService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件夹信息表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Service
|
||||
public class BizFileFoldersServiceImpl extends ServiceImpl<BizFileFoldersMapper, BizFileFolders> implements BizFileFoldersService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.BizFiles;
|
||||
import com.mini.capi.biz.mapper.BizFilesMapper;
|
||||
import com.mini.capi.biz.service.BizFilesService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 文件信息表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Service
|
||||
public class BizFilesServiceImpl extends ServiceImpl<BizFilesMapper, BizFiles> implements BizFilesService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.BizHomeUser;
|
||||
import com.mini.capi.biz.mapper.BizHomeUserMapper;
|
||||
import com.mini.capi.biz.service.BizHomeUserService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-14
|
||||
*/
|
||||
@Service
|
||||
public class BizHomeUserServiceImpl extends ServiceImpl<BizHomeUserMapper, BizHomeUser> implements BizHomeUserService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.BizQuickLogin;
|
||||
import com.mini.capi.biz.mapper.BizQuickLoginMapper;
|
||||
import com.mini.capi.biz.service.BizQuickLoginService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 快捷登录系统信息表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-15
|
||||
*/
|
||||
@Service
|
||||
public class BizQuickLoginServiceImpl extends ServiceImpl<BizQuickLoginMapper, BizQuickLogin> implements BizQuickLoginService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.BizServerInfo;
|
||||
import com.mini.capi.biz.mapper.BizServerInfoMapper;
|
||||
import com.mini.capi.biz.service.BizServerInfoService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务器信息表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Service
|
||||
public class BizServerInfoServiceImpl extends ServiceImpl<BizServerInfoMapper, BizServerInfo> implements BizServerInfoService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.BizSubTask;
|
||||
import com.mini.capi.biz.mapper.BizSubTaskMapper;
|
||||
import com.mini.capi.biz.service.BizSubTaskService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 待办任务子表(关联主任务) 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Service
|
||||
public class BizSubTaskServiceImpl extends ServiceImpl<BizSubTaskMapper, BizSubTask> implements BizSubTaskService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.BizTodoTaskView;
|
||||
import com.mini.capi.biz.mapper.BizTodoTaskViewMapper;
|
||||
import com.mini.capi.biz.service.BizTodoTaskViewService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* VIEW 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Service
|
||||
public class BizTodoTaskViewServiceImpl extends ServiceImpl<BizTodoTaskViewMapper, BizTodoTaskView> implements BizTodoTaskViewService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.DataTableField;
|
||||
import com.mini.capi.biz.mapper.DataTableFieldMapper;
|
||||
import com.mini.capi.biz.service.DataTableFieldService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表字段信息表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Service
|
||||
public class DataTableFieldServiceImpl extends ServiceImpl<DataTableFieldMapper, DataTableField> implements DataTableFieldService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mini.capi.biz.service.impl;
|
||||
|
||||
import com.mini.capi.biz.domain.DataTableInfo;
|
||||
import com.mini.capi.biz.mapper.DataTableInfoMapper;
|
||||
import com.mini.capi.biz.service.DataTableInfoService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据表基础信息表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author gaoxq
|
||||
* @since 2025-11-16
|
||||
*/
|
||||
@Service
|
||||
public class DataTableInfoServiceImpl extends ServiceImpl<DataTableInfoMapper, DataTableInfo> implements DataTableInfoService {
|
||||
|
||||
}
|
||||
155
src/main/java/com/mini/capi/biz/viewController.java
Normal file
155
src/main/java/com/mini/capi/biz/viewController.java
Normal file
@@ -0,0 +1,155 @@
|
||||
package com.mini.capi.biz;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.mini.capi.api.sys.sysService;
|
||||
import com.mini.capi.biz.domain.*;
|
||||
import com.mini.capi.biz.service.*;
|
||||
import com.mini.capi.model.info.CpuInfo;
|
||||
import com.mini.capi.model.info.DiskInfo;
|
||||
import com.mini.capi.model.info.RunInfo;
|
||||
import com.mini.capi.model.info.ServerInfo;
|
||||
import com.mini.capi.utils.SqlUtils;
|
||||
import com.mini.capi.utils.SystemInfoUtil;
|
||||
import com.mini.capi.utils.vDate;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Controller
|
||||
public class viewController {
|
||||
|
||||
|
||||
@Resource
|
||||
private BizMonitorHostService hostService;
|
||||
|
||||
|
||||
@Resource
|
||||
private BizQuickLoginService loginService;
|
||||
|
||||
@Resource
|
||||
private BizFilesService filesService;
|
||||
|
||||
@Resource
|
||||
private BizFileFoldersService foldersService;
|
||||
|
||||
@Resource
|
||||
private sysService service;
|
||||
|
||||
|
||||
@Resource
|
||||
private BizServerInfoService bizServerInfoService;
|
||||
|
||||
@Resource
|
||||
private BizDeviceInfoService bizDeviceInfoService;
|
||||
|
||||
@Resource
|
||||
private DataTableInfoService dataTableInfoService;
|
||||
|
||||
@Resource
|
||||
private DataTableFieldService dataTableFieldService;
|
||||
|
||||
@Resource
|
||||
private BizTodoTaskViewService bizTodoTaskViewService;
|
||||
|
||||
|
||||
@GetMapping("/login")
|
||||
public String showLoginPage() {
|
||||
return "login";
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/swagger")
|
||||
public String redirectToSwagger() {
|
||||
return "redirect:/swagger-ui/index.html";
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/biz/index")
|
||||
public String getUserHome(Model model, HttpSession session) throws Exception {
|
||||
RunInfo runInfo = service.getRunInfo();
|
||||
List<BizMonitorHost> hosts = hostService.list();
|
||||
List<BizQuickLogin> storages = loginService.list();
|
||||
BizHomeUser user = (BizHomeUser) session.getAttribute("currentUser");
|
||||
QueryWrapper<BizTodoTaskView> taskViewQueryWrapper = new QueryWrapper<>();
|
||||
taskViewQueryWrapper.notIn("ustatus", "CPT");
|
||||
List<BizTodoTaskView> todoViews = bizTodoTaskViewService.list(taskViewQueryWrapper);
|
||||
|
||||
model.addAttribute("hosts", hosts);
|
||||
model.addAttribute("storages", storages);
|
||||
model.addAttribute("uname", user.getUname());
|
||||
model.addAttribute("todoViews", todoViews);
|
||||
model.addAttribute("times", vDate.getRunTimes(runInfo.getProcessInfo().getUptime()));
|
||||
return "index";
|
||||
}
|
||||
|
||||
@GetMapping("/biz/getServerInfo")
|
||||
public String getServerInfo(Model model, String hostId) {
|
||||
// 1. 查询服务器信息,确保不为null
|
||||
QueryWrapper<BizServerInfo> infoQueryWrapper = new QueryWrapper<>();
|
||||
infoQueryWrapper.eq("host_id", hostId);
|
||||
BizServerInfo info = bizServerInfoService.getOne(infoQueryWrapper);
|
||||
// 若查询不到数据,创建空对象避免前端null指针
|
||||
model.addAttribute("info", info != null ? info : new BizServerInfo());
|
||||
// 2. 查询设备列表,确保集合始终存在
|
||||
QueryWrapper<BizDeviceInfo> deviceQueryWrapper = new QueryWrapper<>();
|
||||
deviceQueryWrapper.eq("host_id", hostId);
|
||||
List<BizDeviceInfo> devices = bizDeviceInfoService.list(deviceQueryWrapper);
|
||||
// 若查询不到数据,返回空集合而非null
|
||||
model.addAttribute("devices", devices != null ? devices : Collections.emptyList());
|
||||
return "server";
|
||||
}
|
||||
|
||||
@GetMapping("/biz/getFieldDetail")
|
||||
public String getFieldDetail(Model model, String tableId) {
|
||||
DataTableInfo tableInfo = dataTableInfoService.getById(tableId);
|
||||
QueryWrapper<DataTableField> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("table_id", tableId);
|
||||
queryWrapper.orderByAsc("field_order");
|
||||
List<DataTableField> fields = dataTableFieldService.list(queryWrapper);
|
||||
String createSql = SqlUtils.CreateTableSql(tableInfo, fields);
|
||||
String selectSql = SqlUtils.SelectSqlComments(tableInfo, fields);
|
||||
|
||||
model.addAttribute("createSql", createSql != null ? createSql : null);
|
||||
model.addAttribute("selectSql", selectSql != null ? selectSql : null);
|
||||
model.addAttribute("tableInfo", tableInfo != null ? tableInfo : new DataTableInfo());
|
||||
model.addAttribute("fields", fields != null ? fields : Collections.emptyList());
|
||||
return "field";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 数据地图
|
||||
*/
|
||||
@GetMapping("/biz/dataMap")
|
||||
public String getDataMap(Model model) {
|
||||
List<DataTableInfo> tables = dataTableInfoService.list();
|
||||
Collections.sort(tables,
|
||||
Comparator.nullsLast(Comparator.comparing(DataTableInfo::getCreateTime)).reversed()
|
||||
);
|
||||
model.addAttribute("tables", tables);
|
||||
return "data";
|
||||
}
|
||||
|
||||
/**
|
||||
* 文档中心
|
||||
*/
|
||||
|
||||
@GetMapping("/biz/dataDoc")
|
||||
public String getDataDox(Model model) {
|
||||
List<BizFiles> files = filesService.list();
|
||||
List<BizFileFolders> folders = foldersService.list();
|
||||
|
||||
model.addAttribute("files", files);
|
||||
model.addAttribute("folders", folders);
|
||||
return "file";
|
||||
}
|
||||
|
||||
}
|
||||
48
src/main/java/com/mini/capi/biz/workController.java
Normal file
48
src/main/java/com/mini/capi/biz/workController.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package com.mini.capi.biz;
|
||||
|
||||
import com.mini.capi.biz.domain.BizSubTask;
|
||||
import com.mini.capi.biz.service.BizSubTaskService;
|
||||
import com.mini.capi.model.info.TodoHandleDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
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.time.LocalDateTime;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/biz")
|
||||
public class workController {
|
||||
|
||||
|
||||
@Resource
|
||||
private BizSubTaskService bizSubTaskService;
|
||||
|
||||
|
||||
/**
|
||||
* 完成待办
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/finishTodoSub")
|
||||
public ResponseEntity<Void> finishTodoSub(@RequestBody TodoHandleDTO handleDTO) {
|
||||
// 验证参数
|
||||
if (handleDTO.getTaskId() == null || handleDTO.getOpinion() == null || handleDTO.getOpinion().trim().isEmpty()) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
try {
|
||||
// 调用服务层处理任务
|
||||
BizSubTask bizSubTask = bizSubTaskService.getById(handleDTO.getTaskId());
|
||||
bizSubTask.setUstatus("CPT");
|
||||
bizSubTask.setFinishTime(LocalDateTime.now());
|
||||
bizSubTask.setRemark(handleDTO.getOpinion());
|
||||
bizSubTaskService.updateById(bizSubTask);
|
||||
return ResponseEntity.ok().build();
|
||||
} catch (Exception e) {
|
||||
// 处理异常(如任务不存在、数据库错误等)
|
||||
return ResponseEntity.status(500).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/main/java/com/mini/capi/config/LoginInterceptor.java
Normal file
23
src/main/java/com/mini/capi/config/LoginInterceptor.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.mini.capi.config;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
public class LoginInterceptor implements HandlerInterceptor {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
String cPath = request.getContextPath();
|
||||
HttpSession session = request.getSession();
|
||||
Object currentUser = session.getAttribute("currentUser");
|
||||
if (currentUser != null) {
|
||||
return true;
|
||||
} else {
|
||||
response.sendRedirect(cPath + "/login");
|
||||
return false; // 拦截
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,33 @@
|
||||
package com.mini.capi.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer{
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
// 访问根路径 "/" 时,自动重定向到 Swagger UI
|
||||
registry.addRedirectViewController("/", "/login");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册登录拦截器
|
||||
registry.addInterceptor(new LoginInterceptor())
|
||||
.addPathPatterns("/**") // 拦截所有路径
|
||||
.excludePathPatterns( // 排除不需要拦截的路径
|
||||
"/", // 登录页
|
||||
"/login", // 登录页
|
||||
"/biz/userLogin", // 登录接口
|
||||
"/static/**", // 静态资源(CSS、JS、图片等)
|
||||
"/erpApi/**",
|
||||
"/appApi/**",
|
||||
"/jobApi/**"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
13
src/main/java/com/mini/capi/model/auth/LoginRequest.java
Normal file
13
src/main/java/com/mini/capi/model/auth/LoginRequest.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.mini.capi.model.auth;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class LoginRequest implements Serializable {
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
private boolean remember;
|
||||
}
|
||||
22
src/main/java/com/mini/capi/model/info/CpuInfo.java
Normal file
22
src/main/java/com/mini/capi/model/info/CpuInfo.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.mini.capi.model.info;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class CpuInfo implements Serializable {
|
||||
|
||||
private double cpuUsage; // CPU使用率
|
||||
private double memoryUsage; // 内存使用率
|
||||
|
||||
|
||||
public CpuInfo() {
|
||||
}
|
||||
|
||||
public CpuInfo(double cpuUsage, double memoryUsage) {
|
||||
this.cpuUsage = cpuUsage;
|
||||
this.memoryUsage = memoryUsage;
|
||||
}
|
||||
|
||||
}
|
||||
26
src/main/java/com/mini/capi/model/info/DiskInfo.java
Normal file
26
src/main/java/com/mini/capi/model/info/DiskInfo.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.mini.capi.model.info;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class DiskInfo implements Serializable {
|
||||
|
||||
private String device; // 设备
|
||||
private String mountPoint; // 挂载点
|
||||
private String totalSize; // 总容量
|
||||
private String usedSize; // 已使用
|
||||
private double usageRate; // 使用率
|
||||
|
||||
public DiskInfo() {
|
||||
}
|
||||
|
||||
public DiskInfo(String device, String mountPoint, String totalSize, String usedSize, double usageRate) {
|
||||
this.device = device;
|
||||
this.mountPoint = mountPoint;
|
||||
this.totalSize = totalSize;
|
||||
this.usedSize = usedSize;
|
||||
this.usageRate = usageRate;
|
||||
}
|
||||
}
|
||||
19
src/main/java/com/mini/capi/model/info/FileInfo.java
Normal file
19
src/main/java/com/mini/capi/model/info/FileInfo.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.mini.capi.model.info;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class FileInfo implements Serializable {
|
||||
|
||||
private String id; // 文件唯一标识
|
||||
private String name; // 文件名
|
||||
private String type; // 文件类型(pdf、excel、image等)
|
||||
private String size; // 文件大小
|
||||
private String source; // 文件来源(本地上传、共享文件等)
|
||||
private LocalDateTime createTime; // 创建时间
|
||||
private String creator; // 创建人
|
||||
private String folderId; // 所属文件夹ID
|
||||
}
|
||||
30
src/main/java/com/mini/capi/model/info/ServerInfo.java
Normal file
30
src/main/java/com/mini/capi/model/info/ServerInfo.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package com.mini.capi.model.info;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class ServerInfo implements Serializable {
|
||||
|
||||
private String uptime; // 主机运行时间
|
||||
private String os; // 操作系统
|
||||
private String kernelVersion; // 内核版本
|
||||
private String hostname; // 主机名
|
||||
private String ipAddress; // IP地址
|
||||
private String cpuModel; // CPU型号
|
||||
private String memoryTotal; // 内存总量
|
||||
|
||||
public ServerInfo() {
|
||||
}
|
||||
|
||||
public ServerInfo(String uptime, String os, String kernelVersion, String hostname, String ipAddress, String cpuModel, String memoryTotal) {
|
||||
this.uptime = uptime;
|
||||
this.os = os;
|
||||
this.kernelVersion = kernelVersion;
|
||||
this.hostname = hostname;
|
||||
this.ipAddress = ipAddress;
|
||||
this.cpuModel = cpuModel;
|
||||
this.memoryTotal = memoryTotal;
|
||||
}
|
||||
}
|
||||
15
src/main/java/com/mini/capi/model/info/TodoHandleDTO.java
Normal file
15
src/main/java/com/mini/capi/model/info/TodoHandleDTO.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.mini.capi.model.info;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class TodoHandleDTO implements Serializable {
|
||||
|
||||
private Long taskId;
|
||||
private String opinion;
|
||||
|
||||
// 必须有默认无参构造函数
|
||||
public TodoHandleDTO() {}
|
||||
}
|
||||
@@ -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_cities,biz_company,biz_mail_account,biz_project_info,biz_project_requirements,biz_province,biz_resume_employee,biz_website_storage,biz_municipalities,biz_monitor_host,biz_monitor_account,erp_summary_all_view,erp_account,erp_transaction_flow,erp_expense,erp_category,erp_income,erp_summary_source_view,biz_project_report,biz_mail_sent")
|
||||
builder.addInclude("biz_sub_task")
|
||||
.addTablePrefix("biz_,erp_")
|
||||
.entityBuilder()
|
||||
.enableLombok()
|
||||
|
||||
@@ -150,4 +150,57 @@ public class FileUtils {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getFileType(String fileName) {
|
||||
if (fileName == null || fileName.isEmpty()) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
|
||||
|
||||
switch (extension) {
|
||||
case "pdf":
|
||||
return "pdf";
|
||||
case "doc":
|
||||
case "docx":
|
||||
return "word";
|
||||
case "xls":
|
||||
case "xlsx":
|
||||
return "excel";
|
||||
case "ppt":
|
||||
case "pptx":
|
||||
return "ppt";
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
case "png":
|
||||
case "gif":
|
||||
return "image";
|
||||
case "mp4":
|
||||
case "avi":
|
||||
case "mov":
|
||||
return "video";
|
||||
case "mp3":
|
||||
case "wav":
|
||||
return "audio";
|
||||
default:
|
||||
// 如果没有扩展名,视为文件夹
|
||||
if (!fileName.contains(".")) {
|
||||
return "folder";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化文件大小(字节转人类可读格式)
|
||||
public static String formatFileSize(long bytes) {
|
||||
if (bytes < 1024) {
|
||||
return bytes + " B";
|
||||
} else if (bytes < 1024 * 1024) {
|
||||
return String.format("%.1f KB", bytes / 1024.0);
|
||||
} else if (bytes < 1024 * 1024 * 1024) {
|
||||
return String.format("%.1f MB", bytes / (1024.0 * 1024));
|
||||
} else {
|
||||
return String.format("%.1f GB", bytes / (1024.0 * 1024 * 1024));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package com.mini.capi.utils;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.mail.*;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMultipart;
|
||||
import javax.mail.internet.MimeUtility;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MailParseUtils {
|
||||
|
||||
/**
|
||||
* 解析邮件发件人(名称+地址)
|
||||
*/
|
||||
public static String parseFrom(Message message) throws MessagingException {
|
||||
Address[] froms = message.getFrom();
|
||||
if (froms == null || froms.length == 0) {
|
||||
return "";
|
||||
}
|
||||
InternetAddress from = (InternetAddress) froms[0];
|
||||
String fromName = from.getPersonal(); // 发件人名称(可能为null)
|
||||
String fromAddr = from.getAddress(); // 发件人地址
|
||||
return StringUtils.hasText(fromName) ? fromName + "<" + fromAddr + ">" : fromAddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析邮件收件人(多个用逗号分隔)
|
||||
*/
|
||||
public static String parseTo(Message message) throws MessagingException {
|
||||
return parseRecipients(message, Message.RecipientType.TO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析邮件抄送人(多个用逗号分隔)
|
||||
*/
|
||||
public static String parseCc(Message message) throws MessagingException {
|
||||
return parseRecipients(message, Message.RecipientType.CC);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用解析收件人/抄送人
|
||||
*/
|
||||
private static String parseRecipients(Message message, Message.RecipientType type) throws MessagingException {
|
||||
Address[] recipients = message.getRecipients(type);
|
||||
if (recipients == null || recipients.length == 0) {
|
||||
return "";
|
||||
}
|
||||
List<String> addrList = new ArrayList<>();
|
||||
for (Address addr : recipients) {
|
||||
InternetAddress internetAddr = (InternetAddress) addr;
|
||||
String name = internetAddr.getPersonal();
|
||||
String address = internetAddr.getAddress();
|
||||
addrList.add(StringUtils.hasText(name) ? name + "<" + address + ">" : address);
|
||||
}
|
||||
return String.join(",", addrList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析邮件内容(支持文本/HTML)
|
||||
*/
|
||||
public static String parseContent(Part part) throws MessagingException, IOException {
|
||||
if (part.isMimeType("text/plain") || part.isMimeType("text/html")) {
|
||||
// 文本/HTML直接读取
|
||||
return (String) part.getContent();
|
||||
}
|
||||
// 多部分内容(含附件),递归解析文本部分
|
||||
if (part.isMimeType("multipart/*")) {
|
||||
MimeMultipart multipart = (MimeMultipart) part.getContent();
|
||||
for (int i = 0; i < multipart.getCount(); i++) {
|
||||
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||
String content = parseContent(bodyPart);
|
||||
if (StringUtils.hasText(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取邮件附件(返回附件流+原始文件名)
|
||||
*/
|
||||
public static List<AttachmentInfo> extractAttachments(Part part) throws MessagingException, IOException {
|
||||
List<AttachmentInfo> attachments = new ArrayList<>();
|
||||
// 多部分内容才可能有附件
|
||||
if (part.isMimeType("multipart/*")) {
|
||||
MimeMultipart multipart = (MimeMultipart) part.getContent();
|
||||
for (int i = 0; i < multipart.getCount(); i++) {
|
||||
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||
// 判断是否为附件(Disposition为ATTACHMENT或INLINE)
|
||||
String disposition = bodyPart.getDisposition();
|
||||
if (disposition != null && (disposition.equals(Part.ATTACHMENT) || disposition.equals(Part.INLINE))) {
|
||||
// 获取原始文件名(处理中文乱码)
|
||||
String originalFileName = MimeUtility.decodeText(bodyPart.getFileName());
|
||||
// 获取附件输入流
|
||||
InputStream inputStream = bodyPart.getInputStream();
|
||||
attachments.add(new AttachmentInfo(originalFileName, inputStream));
|
||||
} else {
|
||||
// 递归处理嵌套的多部分内容
|
||||
attachments.addAll(extractAttachments(bodyPart));
|
||||
}
|
||||
}
|
||||
}
|
||||
return attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* 附件信息封装(原始文件名+输入流)
|
||||
*/
|
||||
public static class AttachmentInfo {
|
||||
private String originalFileName;
|
||||
private InputStream inputStream;
|
||||
|
||||
public AttachmentInfo(String originalFileName, InputStream inputStream) {
|
||||
this.originalFileName = originalFileName;
|
||||
this.inputStream = inputStream;
|
||||
}
|
||||
|
||||
// Getter
|
||||
public String getOriginalFileName() {
|
||||
return originalFileName;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return inputStream;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.mini.capi.utils;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.mail.BodyPart;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.Multipart;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MailUtil {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MailUtil.class);
|
||||
|
||||
/**
|
||||
* 获取邮件内容
|
||||
*/
|
||||
public static String getEmailContent(Message message) {
|
||||
try {
|
||||
Object content = message.getContent();
|
||||
|
||||
// 简单文本内容
|
||||
if (content instanceof String) {
|
||||
return (String) content;
|
||||
}
|
||||
// 复杂内容(多部分)
|
||||
else if (content instanceof Multipart) {
|
||||
Multipart multipart = (Multipart) content;
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
|
||||
// 遍历所有部分
|
||||
for (int i = 0; i < multipart.getCount(); i++) {
|
||||
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||
|
||||
// 忽略附件
|
||||
if (BodyPart.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition()) ||
|
||||
bodyPart.getFileName() != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取内容
|
||||
Object partContent = bodyPart.getContent();
|
||||
if (partContent instanceof String) {
|
||||
contentBuilder.append(partContent);
|
||||
} else if (partContent instanceof Multipart) {
|
||||
// 处理嵌套的多部分内容
|
||||
contentBuilder.append(getMultipartContent((Multipart) partContent));
|
||||
}
|
||||
}
|
||||
|
||||
return contentBuilder.toString();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("获取邮件内容失败", e);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理嵌套的多部分内容
|
||||
*/
|
||||
private static String getMultipartContent(Multipart multipart) throws MessagingException, IOException {
|
||||
StringBuilder contentBuilder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < multipart.getCount(); i++) {
|
||||
BodyPart bodyPart = multipart.getBodyPart(i);
|
||||
|
||||
// 忽略附件
|
||||
if (BodyPart.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition()) ||
|
||||
bodyPart.getFileName() != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Object partContent = bodyPart.getContent();
|
||||
if (partContent instanceof String) {
|
||||
contentBuilder.append(partContent);
|
||||
} else if (partContent instanceof Multipart) {
|
||||
contentBuilder.append(getMultipartContent((Multipart) partContent));
|
||||
}
|
||||
}
|
||||
|
||||
return contentBuilder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
98
src/main/java/com/mini/capi/utils/SqlUtils.java
Normal file
98
src/main/java/com/mini/capi/utils/SqlUtils.java
Normal file
@@ -0,0 +1,98 @@
|
||||
package com.mini.capi.utils;
|
||||
|
||||
import com.mini.capi.biz.domain.DataTableField;
|
||||
import com.mini.capi.biz.domain.DataTableInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SqlUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 生成CREATE TABLE语句
|
||||
*
|
||||
* @param tableInfo 数据表基础信息
|
||||
* @param fieldList 字段列表
|
||||
* @return CREATE TABLE语句
|
||||
*/
|
||||
public static String CreateTableSql(DataTableInfo tableInfo, List<DataTableField> fieldList) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 表定义开始
|
||||
sb.append("CREATE TABLE ").append(tableInfo.getTableName()).append(" (\n");
|
||||
// 拼接字段定义
|
||||
for (int i = 0; i < fieldList.size(); i++) {
|
||||
DataTableField field = fieldList.get(i);
|
||||
sb.append(" ").append(field.getFieldName()).append(" ");
|
||||
// 处理字段类型和长度
|
||||
if (field.getFieldLength() != null && field.getFieldLength() > 0) {
|
||||
sb.append(field.getFieldType()).append("(").append(field.getFieldLength()).append(") ");
|
||||
} else {
|
||||
sb.append(field.getFieldType()).append(" ");
|
||||
}
|
||||
|
||||
// 处理主键(简单判断:fieldId为主键字段)
|
||||
if ("field_id".equals(field.getFieldName()) || "table_id".equals(field.getFieldName())) {
|
||||
sb.append("NOT NULL AUTO_INCREMENT ");
|
||||
} else {
|
||||
sb.append("DEFAULT NULL ");
|
||||
}
|
||||
|
||||
// 字段注释
|
||||
if (field.getFieldRemark() != null && !field.getFieldRemark().isEmpty()) {
|
||||
sb.append("COMMENT '").append(field.getFieldRemark()).append("'");
|
||||
}
|
||||
|
||||
// 最后一个字段不加逗号
|
||||
if (i != fieldList.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
// 表引擎和字符集
|
||||
sb.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ");
|
||||
// 表注释
|
||||
if (tableInfo.getTableComment() != null && !tableInfo.getTableComment().isEmpty()) {
|
||||
sb.append("COMMENT\t'").append(tableInfo.getTableComment()).append("'");
|
||||
}
|
||||
sb.append(";");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成带注释的SELECT语句
|
||||
*
|
||||
* @param tableInfo 数据表基础信息
|
||||
* @param fieldList 字段列表
|
||||
* @return 带注释的SELECT语句
|
||||
*/
|
||||
public static String SelectSqlComments(DataTableInfo tableInfo, List<DataTableField> fieldList) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 表注释
|
||||
sb.append("-- 表名:").append(tableInfo.getTableName()).append("\n");
|
||||
sb.append("-- 描述:").append(tableInfo.getTableComment() != null ? tableInfo.getTableComment() : "无").append("\n");
|
||||
sb.append("-- 数据来源:").append(tableInfo.getDataSource() != null ? tableInfo.getDataSource() : "未知").append("\n");
|
||||
// SELECT语句开始
|
||||
sb.append("SELECT\n");
|
||||
|
||||
// 拼接字段(带注释)
|
||||
for (int i = 0; i < fieldList.size(); i++) {
|
||||
DataTableField field = fieldList.get(i);
|
||||
sb.append(" ").append(field.getFieldName()).append("");
|
||||
// 字段注释
|
||||
if (field.getFieldRemark() != null && !field.getFieldRemark().isEmpty()) {
|
||||
sb.append(" -- ").append(field.getFieldRemark());
|
||||
}
|
||||
// 最后一个字段不加逗号
|
||||
if (i != fieldList.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
// 表名
|
||||
sb.append("FROM ").append(tableInfo.getTableName()).append(";");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
183
src/main/java/com/mini/capi/utils/SystemInfoUtil.java
Normal file
183
src/main/java/com/mini/capi/utils/SystemInfoUtil.java
Normal file
@@ -0,0 +1,183 @@
|
||||
package com.mini.capi.utils;
|
||||
|
||||
|
||||
import com.jcraft.jsch.ChannelExec;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.Session;
|
||||
import com.mini.capi.model.info.CpuInfo;
|
||||
import com.mini.capi.model.info.DiskInfo;
|
||||
import com.mini.capi.model.info.ServerInfo;
|
||||
import lombok.Data;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class SystemInfoUtil {
|
||||
|
||||
|
||||
// SSH 连接超时时间(毫秒)
|
||||
private static final int SSH_TIMEOUT = 5000;
|
||||
|
||||
|
||||
/**
|
||||
* 建立SSH连接
|
||||
*/
|
||||
private static Session getSshSession(String host, int port, String username, String password) throws Exception {
|
||||
JSch jsch = new JSch();
|
||||
Session session = jsch.getSession(username, host, port);
|
||||
session.setPassword(password);
|
||||
|
||||
Properties config = new Properties();
|
||||
config.put("StrictHostKeyChecking", "no");
|
||||
session.setConfig(config);
|
||||
|
||||
session.setTimeout(SSH_TIMEOUT);
|
||||
session.connect();
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行SSH命令并返回结果
|
||||
*/
|
||||
private static String executeCommand(Session session, String command) throws Exception {
|
||||
ChannelExec channel = (ChannelExec) session.openChannel("exec");
|
||||
channel.setCommand(command);
|
||||
|
||||
InputStream in = channel.getInputStream();
|
||||
channel.connect();
|
||||
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(in, StandardCharsets.UTF_8)
|
||||
);
|
||||
StringBuilder result = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
result.append(line).append("\n");
|
||||
}
|
||||
|
||||
reader.close();
|
||||
channel.disconnect();
|
||||
return result.toString().trim();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取服务器基础信息
|
||||
*/
|
||||
public static ServerInfo getServerBasicInfo(String host, int port, String username, String password, String ipAddress) throws Exception {
|
||||
ServerInfo info = new ServerInfo();
|
||||
Session session = null;
|
||||
try {
|
||||
session = getSshSession(host, port, username, password);
|
||||
|
||||
// 主机运行时间
|
||||
String uptimeResult = executeCommand(session, "uptime");
|
||||
if (StringUtils.hasText(uptimeResult)) {
|
||||
String[] parts = uptimeResult.split("up ");
|
||||
if (parts.length > 1) {
|
||||
info.setUptime(parts[1].split(", ")[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// 操作系统
|
||||
String osResult = executeCommand(session, "cat /etc/os-release | grep PRETTY_NAME | cut -d'=' -f2 | tr -d '\"'");
|
||||
info.setOs(osResult.isEmpty() ? "Unknown" : osResult);
|
||||
|
||||
// 内核版本
|
||||
info.setKernelVersion(executeCommand(session, "uname -r"));
|
||||
|
||||
// 主机名
|
||||
info.setHostname(executeCommand(session, "hostname"));
|
||||
|
||||
// IP地址(传入参数)
|
||||
info.setIpAddress(ipAddress);
|
||||
|
||||
// CPU型号
|
||||
String cpuResult = executeCommand(session, "cat /proc/cpuinfo | grep 'model name' | head -n 1 | cut -d: -f2 | sed -e 's/^ *//'");
|
||||
info.setCpuModel(cpuResult.isEmpty() ? "Unknown" : cpuResult);
|
||||
|
||||
// 内存总量
|
||||
String memResult = executeCommand(session, "free -h | grep Mem | awk '{print $2}'");
|
||||
info.setMemoryTotal(memResult.isEmpty() ? "Unknown" : memResult);
|
||||
|
||||
} finally {
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取CPU和内存使用率
|
||||
*/
|
||||
public static CpuInfo getCpuMemUsage(String host, int port, String username, String password) throws Exception {
|
||||
CpuInfo usage = new CpuInfo();
|
||||
Session session = null;
|
||||
try {
|
||||
session = getSshSession(host, port, username, password);
|
||||
|
||||
// CPU使用率
|
||||
String cpuCommand = "top -bn2 | grep '%Cpu' | tail -n1 | awk '{print 100 - $8}'";
|
||||
String cpuResult = executeCommand(session, cpuCommand);
|
||||
if (StringUtils.hasText(cpuResult)) {
|
||||
usage.setCpuUsage(Double.parseDouble(cpuResult));
|
||||
}
|
||||
// 内存使用率
|
||||
String memCommand = "free | grep Mem | awk '{print $3/$2 * 100}'";
|
||||
String memResult = executeCommand(session, memCommand);
|
||||
if (StringUtils.hasText(memResult)) {
|
||||
usage.setMemoryUsage(Double.parseDouble(memResult));
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有磁盘信息
|
||||
*/
|
||||
public static List<DiskInfo> getDiskInfos(String host, int port, String username, String password) throws Exception {
|
||||
List<DiskInfo> diskInfos = new ArrayList<>();
|
||||
Session session = null;
|
||||
try {
|
||||
session = getSshSession(host, port, username, password);
|
||||
// 关键修改:强制英文输出 + 调整字段索引
|
||||
String diskCommand = "LANG=en_US.UTF-8 df -h | awk '{print $1, $6, $2, $3, $5}'";
|
||||
String diskResult = executeCommand(session, diskCommand);
|
||||
|
||||
if (StringUtils.hasText(diskResult)) {
|
||||
String[] lines = diskResult.split("\n");
|
||||
for (String line : lines) {
|
||||
// 过滤空行和标题行
|
||||
if (line.trim().isEmpty() || line.contains("Filesystem")) {
|
||||
continue;
|
||||
}
|
||||
// 按任意空格分割(兼容多个空格)
|
||||
String[] parts = line.trim().split("\\s+");
|
||||
if (parts.length >= 5) {
|
||||
String usageStr = parts[4].replace("%", "").trim();
|
||||
DiskInfo diskInfo = new DiskInfo(parts[0], parts[1], parts[2], parts[3], Double.parseDouble(usageStr));
|
||||
diskInfos.add(diskInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (session != null && session.isConnected()) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
return diskInfos;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -46,4 +46,39 @@ public class vDate {
|
||||
return getNow();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getRunTimes(long totalSeconds) {
|
||||
if (totalSeconds < 0) {
|
||||
throw new IllegalArgumentException("输入的秒数不能为负数");
|
||||
}
|
||||
// 定义时间单位换算关系
|
||||
long secondsPerMinute = 60;
|
||||
long secondsPerHour = secondsPerMinute * 60; // 3600秒/小时
|
||||
long secondsPerDay = secondsPerHour * 24; // 86400秒/天
|
||||
|
||||
// 计算各单位数值
|
||||
long days = totalSeconds / secondsPerDay;
|
||||
long remainingSecondsAfterDays = totalSeconds % secondsPerDay;
|
||||
|
||||
long hours = remainingSecondsAfterDays / secondsPerHour;
|
||||
long remainingSecondsAfterHours = remainingSecondsAfterDays % secondsPerHour;
|
||||
|
||||
long minutes = remainingSecondsAfterHours / secondsPerMinute;
|
||||
long seconds = remainingSecondsAfterHours % secondsPerMinute;
|
||||
// 拼接结果字符串
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (days > 0) {
|
||||
result.append(days).append("天");
|
||||
}
|
||||
if (hours > 0) {
|
||||
result.append(hours).append("小时");
|
||||
}
|
||||
if (minutes > 0) {
|
||||
result.append(minutes).append("分");
|
||||
}
|
||||
// 确保至少显示秒数(即使其他单位都为0)
|
||||
result.append(seconds).append("秒");
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
23
src/main/resources/mapper/BizDeviceInfoMapper.xml
Normal file
23
src/main/resources/mapper/BizDeviceInfoMapper.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.BizDeviceInfoMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.BizDeviceInfo">
|
||||
<id column="id" property="id" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="device" property="device" />
|
||||
<result column="mount_point" property="mountPoint" />
|
||||
<result column="total_size" property="totalSize" />
|
||||
<result column="used_size" property="usedSize" />
|
||||
<result column="usage_rate" property="usageRate" />
|
||||
<result column="last_online_time" property="lastOnlineTime" />
|
||||
<result column="host_id" property="hostId" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
create_time, id, device, mount_point, total_size, used_size, usage_rate, last_online_time, host_id
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
22
src/main/resources/mapper/BizFileFoldersMapper.xml
Normal file
22
src/main/resources/mapper/BizFileFoldersMapper.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.BizFileFoldersMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.BizFileFolders">
|
||||
<id column="folder_id" property="folderId" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="folder_name" property="folderName" />
|
||||
<result column="parent_id" property="parentId" />
|
||||
<result column="creator_id" property="creatorId" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
<result column="is_deleted" property="isDeleted" />
|
||||
<result column="description" property="description" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
create_time, folder_id, folder_name, parent_id, creator_id, update_time, is_deleted, description
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
26
src/main/resources/mapper/BizFilesMapper.xml
Normal file
26
src/main/resources/mapper/BizFilesMapper.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.BizFilesMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.BizFiles">
|
||||
<id column="file_id" property="fileId" />
|
||||
<result column="file_name" property="fileName" />
|
||||
<result column="folder_id" property="folderId" />
|
||||
<result column="file_size" property="fileSize" />
|
||||
<result column="file_type" property="fileType" />
|
||||
<result column="file_path" property="filePath" />
|
||||
<result column="creator_id" property="creatorId" />
|
||||
<result column="upload_time" property="uploadTime" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
<result column="is_deleted" property="isDeleted" />
|
||||
<result column="version" property="version" />
|
||||
<result column="md5" property="md5" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
file_id, file_name, folder_id, file_size, file_type, file_path, creator_id, upload_time, update_time, is_deleted, version, md5
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
19
src/main/resources/mapper/BizHomeUserMapper.xml
Normal file
19
src/main/resources/mapper/BizHomeUserMapper.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.BizHomeUserMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.BizHomeUser">
|
||||
<id column="user_id" property="userId" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="user_name" property="userName" />
|
||||
<result column="password" property="password" />
|
||||
<result column="uname" property="uname" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
create_time, user_id, user_name, password, uname
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
27
src/main/resources/mapper/BizQuickLoginMapper.xml
Normal file
27
src/main/resources/mapper/BizQuickLoginMapper.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.BizQuickLoginMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.BizQuickLogin">
|
||||
<id column="id" property="id" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="system_name" property="systemName" />
|
||||
<result column="homepage_url" property="homepageUrl" />
|
||||
<result column="icon_class" property="iconClass" />
|
||||
<result column="icon_color" property="iconColor" />
|
||||
<result column="sort_order" property="sortOrder" />
|
||||
<result column="is_enabled" property="isEnabled" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
<result column="f_tenant_id" property="fTenantId" />
|
||||
<result column="f_flow_id" property="fFlowId" />
|
||||
<result column="f_flow_task_id" property="fFlowTaskId" />
|
||||
<result column="f_flow_state" property="fFlowState" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
create_time, id, system_name, homepage_url, icon_class, icon_color, sort_order, is_enabled, update_time, f_tenant_id, f_flow_id, f_flow_task_id, f_flow_state
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
27
src/main/resources/mapper/BizServerInfoMapper.xml
Normal file
27
src/main/resources/mapper/BizServerInfoMapper.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.BizServerInfoMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.BizServerInfo">
|
||||
<id column="id" property="id" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="uptime" property="uptime" />
|
||||
<result column="os" property="os" />
|
||||
<result column="kernel_version" property="kernelVersion" />
|
||||
<result column="hostname" property="hostname" />
|
||||
<result column="ip_address" property="ipAddress" />
|
||||
<result column="cpu_model" property="cpuModel" />
|
||||
<result column="memory_total" property="memoryTotal" />
|
||||
<result column="cpu_usage" property="cpuUsage" />
|
||||
<result column="memory_usage" property="memoryUsage" />
|
||||
<result column="last_online_time" property="lastOnlineTime" />
|
||||
<result column="host_id" property="hostId" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
create_time, id, uptime, os, kernel_version, hostname, ip_address, cpu_model, memory_total, cpu_usage, memory_usage, last_online_time, host_id
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
26
src/main/resources/mapper/BizSubTaskMapper.xml
Normal file
26
src/main/resources/mapper/BizSubTaskMapper.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.BizSubTaskMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.BizSubTask">
|
||||
<id column="sub_task_id" property="subTaskId" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="parent_task_id" property="parentTaskId" />
|
||||
<result column="sub_task_name" property="subTaskName" />
|
||||
<result column="priority" property="priority" />
|
||||
<result column="ustatus" property="ustatus" />
|
||||
<result column="finish_time" property="finishTime" />
|
||||
<result column="remark" property="remark" />
|
||||
<result column="f_tenant_id" property="fTenantId" />
|
||||
<result column="f_flow_id" property="fFlowId" />
|
||||
<result column="f_flow_task_id" property="fFlowTaskId" />
|
||||
<result column="f_flow_state" property="fFlowState" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
create_time, sub_task_id, parent_task_id, sub_task_name, priority, ustatus, finish_time, remark, f_tenant_id, f_flow_id, f_flow_task_id, f_flow_state
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
30
src/main/resources/mapper/BizTodoTaskViewMapper.xml
Normal file
30
src/main/resources/mapper/BizTodoTaskViewMapper.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.BizTodoTaskViewMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.BizTodoTaskView">
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="task_id" property="taskId" />
|
||||
<result column="task_name" property="taskName" />
|
||||
<result column="task_desc" property="taskDesc" />
|
||||
<result column="deadline" property="deadline" />
|
||||
<result column="todo_user" property="todoUser" />
|
||||
<result column="login_user" property="loginUser" />
|
||||
<result column="f_tenant_id" property="fTenantId" />
|
||||
<result column="f_flow_id" property="fFlowId" />
|
||||
<result column="f_flow_task_id" property="fFlowTaskId" />
|
||||
<result column="f_flow_state" property="fFlowState" />
|
||||
<result column="sub_task_id" property="subTaskId" />
|
||||
<result column="sub_task_name" property="subTaskName" />
|
||||
<result column="priority" property="priority" />
|
||||
<result column="ustatus" property="ustatus" />
|
||||
<result column="finish_time" property="finishTime" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
create_time, task_id, task_name, task_desc, deadline, todo_user, login_user, f_tenant_id, f_flow_id, f_flow_task_id, f_flow_state, sub_task_id, sub_task_name, priority, ustatus, finish_time
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
26
src/main/resources/mapper/DataTableFieldMapper.xml
Normal file
26
src/main/resources/mapper/DataTableFieldMapper.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.DataTableFieldMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.DataTableField">
|
||||
<id column="field_id" property="fieldId" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="table_id" property="tableId" />
|
||||
<result column="field_order" property="fieldOrder" />
|
||||
<result column="field_type" property="fieldType" />
|
||||
<result column="field_name" property="fieldName" />
|
||||
<result column="field_length" property="fieldLength" />
|
||||
<result column="field_remark" property="fieldRemark" />
|
||||
<result column="f_tenant_id" property="fTenantId" />
|
||||
<result column="f_flow_id" property="fFlowId" />
|
||||
<result column="f_flow_task_id" property="fFlowTaskId" />
|
||||
<result column="f_flow_state" property="fFlowState" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
create_time, field_id, table_id, field_order, field_type, field_name, field_length, field_remark, f_tenant_id, f_flow_id, f_flow_task_id, f_flow_state
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
28
src/main/resources/mapper/DataTableInfoMapper.xml
Normal file
28
src/main/resources/mapper/DataTableInfoMapper.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.mini.capi.biz.mapper.DataTableInfoMapper">
|
||||
|
||||
<!-- 通用查询映射结果 -->
|
||||
<resultMap id="BaseResultMap" type="com.mini.capi.biz.domain.DataTableInfo">
|
||||
<id column="table_id" property="tableId" />
|
||||
<result column="create_time" property="createTime" />
|
||||
<result column="table_name" property="tableName" />
|
||||
<result column="table_comment" property="tableComment" />
|
||||
<result column="table_size" property="tableSize" />
|
||||
<result column="data_source" property="dataSource" />
|
||||
<result column="creator" property="creator" />
|
||||
<result column="data_rows" property="dataRows" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
<result column="f_tenant_id" property="fTenantId" />
|
||||
<result column="f_flow_id" property="fFlowId" />
|
||||
<result column="f_flow_task_id" property="fFlowTaskId" />
|
||||
<result column="f_flow_state" property="fFlowState" />
|
||||
<result column="remarks" property="remarks" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
create_time, table_id, table_name, table_comment, table_size, data_source, creator, data_rows, update_time, f_tenant_id, f_flow_id, f_flow_task_id, f_flow_state, remarks
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
BIN
src/main/resources/static/images/user.jpg
Normal file
BIN
src/main/resources/static/images/user.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
357
src/main/resources/templates/data.html
Normal file
357
src/main/resources/templates/data.html
Normal file
@@ -0,0 +1,357 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>数据展示平台</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
|
||||
<!-- 配置Tailwind自定义颜色 -->
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
lightBlue: '#e6f0ff',
|
||||
cardBlue: '#f0f7ff',
|
||||
borderBlue: '#bfd8ff',
|
||||
btnBlue: '#165dff',
|
||||
statBlue: '#dceaff'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(156, 163, 175, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
@apply bg-statBlue rounded-lg p-4 border border-borderBlue hover:shadow-sm transition duration-200;
|
||||
}
|
||||
|
||||
.detail-panel {
|
||||
@apply fixed top-0 right-0 h-full w-0 bg-white shadow-lg z-50 transition-all duration-300 ease-in-out overflow-hidden;
|
||||
}
|
||||
|
||||
/* 修改为70%宽度 */
|
||||
.detail-panel.open {
|
||||
@apply w-[70%];
|
||||
}
|
||||
|
||||
.overlay {
|
||||
@apply fixed inset-0 bg-black bg-opacity-20 z-40 hidden transition-opacity duration-300;
|
||||
}
|
||||
|
||||
.overlay.show {
|
||||
@apply block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-lightBlue min-h-screen flex flex-col">
|
||||
<!-- 主容器 -->
|
||||
<div class="flex-1 flex w-full mb-4 px-4 md:px-8 lg:px-16">
|
||||
<!-- 左侧基本信息 (25%) -->
|
||||
<div class="w-1/4 p-4 bg-white rounded-lg shadow-sm mr-4 h-[calc(100vh-3rem)] overflow-y-auto scrollbar-thin">
|
||||
<h2 class="text-xl font-bold text-gray-800 mb-6 pb-2 border-b border-gray-200">基本信息</h2>
|
||||
|
||||
<div class="space-y-5">
|
||||
|
||||
<!-- 数据统计卡片 -->
|
||||
<div class="stat-card">
|
||||
<div class="flex items-center mb-3">
|
||||
<div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center mr-3">
|
||||
<i class="fa fa-database text-btnBlue"></i>
|
||||
</div>
|
||||
<h3 class="text-sm font-semibold text-gray-500">数据统计</h3>
|
||||
</div>
|
||||
<div class="space-y-3 pl-11">
|
||||
<div class="flex justify-between items-end">
|
||||
<span class="text-gray-500 text-sm">总数据表</span>
|
||||
<span class="text-xl font-bold text-gray-800" th:text="${tables.size()}"></span>
|
||||
</div>
|
||||
<div class="w-full bg-white h-1.5 rounded-full overflow-hidden">
|
||||
<div class="bg-btnBlue h-full rounded-full" style="width: 75%"></div>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">张</div>
|
||||
|
||||
<div class="flex justify-between items-end">
|
||||
<span class="text-gray-500 text-sm">总数据量</span>
|
||||
<span class="text-xl font-bold text-gray-800">348.2</span>
|
||||
</div>
|
||||
<div class="w-full bg-white h-1.5 rounded-full overflow-hidden">
|
||||
<div class="bg-btnBlue h-full rounded-full" style="width: 60%"></div>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">GB</div>
|
||||
|
||||
<div class="flex justify-between items-end">
|
||||
<span class="text-gray-500 text-sm">今日新增</span>
|
||||
<span class="text-xl font-bold text-gray-800">12</span>
|
||||
</div>
|
||||
<div class="w-full bg-white h-1.5 rounded-full overflow-hidden">
|
||||
<div class="bg-green-500 h-full rounded-full" style="width: 30%"></div>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">张表</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 来源系统卡片 -->
|
||||
<div class="stat-card">
|
||||
<div class="flex items-center mb-3">
|
||||
<div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center mr-3">
|
||||
<i class="fa fa-sitemap text-btnBlue"></i>
|
||||
</div>
|
||||
<h3 class="text-sm font-semibold text-gray-500">来源系统</h3>
|
||||
</div>
|
||||
<div class="pl-11">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full flex items-center">
|
||||
<i class="fa fa-building-o mr-1"></i>ERP系统
|
||||
</span>
|
||||
<span class="px-2 py-1 bg-yellow-100 text-yellow-800 text-xs rounded-full flex items-center">
|
||||
<i class="fa fa-archive mr-1"></i>数据仓库
|
||||
</span>
|
||||
<span class="px-2 py-1 bg-red-100 text-red-800 text-xs rounded-full flex items-center">
|
||||
<i class="fa fa-cubes mr-1"></i>业务系统
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 中间内容区域 -->
|
||||
<div class="w-full flex flex-col h-[calc(100vh-3rem)]">
|
||||
<!-- 查询区域 -->
|
||||
<div class="bg-white p-4 rounded-lg shadow-sm mb-4">
|
||||
<div class="flex flex-col sm:flex-row gap-3">
|
||||
<div class="relative flex-1">
|
||||
<input
|
||||
type="text"
|
||||
id="searchInput"
|
||||
placeholder="请输入表名称或描述进行查询..."
|
||||
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||
>
|
||||
<i class="fa fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button id="searchBtn" class="bg-btnBlue hover:bg-blue-700 text-white px-4 py-2 rounded-lg flex items-center gap-2 transition duration-200">
|
||||
<i class="fa fa-search"></i>
|
||||
<span>查询</span>
|
||||
</button>
|
||||
<button id="resetBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded-lg flex items-center gap-2 transition duration-200">
|
||||
<i class="fa fa-refresh"></i>
|
||||
<span>重置</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sticky top-0 z-10 flex justify-between items-center mb-4 bg-cardBlue p-3 rounded-lg">
|
||||
<h2 class="text-xl font-bold text-gray-800">数据列表</h2>
|
||||
<p class="text-sm text-gray-500">共 <span id="totalCount" th:text="${tables.size()}"></span> 条数据</p>
|
||||
</div>
|
||||
|
||||
<!-- 卡片列表区域 -->
|
||||
<div class="flex-1 bg-white rounded-lg shadow-sm p-4 overflow-y-auto scrollbar-thin">
|
||||
<!-- 卡片网格 - 每行3个卡片 -->
|
||||
<div id="tableCardContainer" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- 卡片1 -->
|
||||
<div class="border border-borderBlue bg-cardBlue rounded-lg p-4 hover:shadow-md transition duration-200 table-card"
|
||||
th:each="table : ${tables}">
|
||||
<div class="mb-2">
|
||||
<div class="flex justify-between items-start">
|
||||
<h3 class="font-semibold text-gray-800 table-name" th:text="${table.getTableName()}"></h3>
|
||||
<span class="text-xs bg-blue-100 text-blue-800 px-2 py-0.5 rounded"
|
||||
th:text="${table.getDataSource()}"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 text-sm text-gray-600 table-desc" th:text="${table.getTableComment()}"></div>
|
||||
|
||||
<div class="mb-3 text-xs text-gray-500 space-y-1">
|
||||
<div class="flex justify-between">
|
||||
<span>大小:<span th:text="${table.getTableSize()}"></span></span>
|
||||
<span>创建时间:<span th:text="${table.getCreateTime()}"></span></span>
|
||||
</div>
|
||||
<div>
|
||||
<span>创建人:<span th:text="${table.getCreator()}"></span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<button class="text-btnBlue hover:text-blue-700 text-sm font-medium flex items-center gap-1 transition duration-200 detail-btn"
|
||||
th:data-tableId="${table.getTableId()}">
|
||||
<span>详情</span>
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 无数据提示 -->
|
||||
<div id="emptyTip" class="hidden flex flex-col items-center justify-center h-40 text-gray-500">
|
||||
<i class="fa fa-search-minus text-4xl mb-2"></i>
|
||||
<span>未找到匹配的数据表</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧详情面板 - 已设置为70%宽度 -->
|
||||
<div class="detail-panel" id="detailPanel">
|
||||
<div class="p-4 border-b border-gray-200 flex justify-between items-center sticky top-0 bg-white z-10">
|
||||
<h3 class="text-lg font-semibold text-gray-800">数据详情</h3>
|
||||
<button id="closeDetailBtn"
|
||||
class="text-gray-500 hover:text-gray-700 transition-colors p-2 rounded-full hover:bg-gray-100">
|
||||
<i class="fa fa-times text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div style="height: calc(100% - 64px); overflow-y: auto;">
|
||||
<!-- 加载中动画 -->
|
||||
<div id="loadingMask" class="flex justify-center items-center h-full">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-btnBlue"></div>
|
||||
<span class="ml-3 text-gray-600">加载中...</span>
|
||||
</div>
|
||||
<!-- 详情内容 -->
|
||||
<iframe id="detailFrame" width="100%" height="100%" frameborder="0" style="min-height: 600px;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 背景遮罩 -->
|
||||
<div class="overlay" id="overlay"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// 核心元素获取
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const searchBtn = document.getElementById('searchBtn');
|
||||
const resetBtn = document.getElementById('resetBtn');
|
||||
const tableCards = document.querySelectorAll('.table-card');
|
||||
const tableCardContainer = document.getElementById('tableCardContainer');
|
||||
const emptyTip = document.getElementById('emptyTip');
|
||||
const totalCountEl = document.getElementById('totalCount');
|
||||
const originalTotal = totalCountEl.textContent;
|
||||
|
||||
// 详情面板相关逻辑
|
||||
const detailPanel = document.getElementById('detailPanel');
|
||||
const overlay = document.getElementById('overlay');
|
||||
const closeDetailBtn = document.getElementById('closeDetailBtn');
|
||||
const detailFrame = document.getElementById('detailFrame');
|
||||
const loadingMask = document.getElementById('loadingMask');
|
||||
const detailBtns = document.querySelectorAll('.detail-btn');
|
||||
|
||||
// 搜索功能实现
|
||||
function handleSearch() {
|
||||
const searchVal = searchInput.value.trim().toLowerCase();
|
||||
let matchCount = 0;
|
||||
|
||||
tableCards.forEach(card => {
|
||||
const tableName = card.querySelector('.table-name').textContent.toLowerCase();
|
||||
const tableDesc = card.querySelector('.table-desc').textContent.toLowerCase();
|
||||
|
||||
// 匹配表名或描述
|
||||
const isMatch = tableName.includes(searchVal) || tableDesc.includes(searchVal);
|
||||
|
||||
if (isMatch) {
|
||||
card.style.display = '';
|
||||
matchCount++;
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// 更新计数和空数据提示
|
||||
totalCountEl.textContent = matchCount;
|
||||
emptyTip.style.display = matchCount === 0 ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
// 重置功能实现
|
||||
function handleReset() {
|
||||
searchInput.value = '';
|
||||
tableCards.forEach(card => {
|
||||
card.style.display = '';
|
||||
});
|
||||
totalCountEl.textContent = originalTotal;
|
||||
emptyTip.style.display = 'none';
|
||||
}
|
||||
|
||||
// 绑定搜索事件
|
||||
searchBtn.addEventListener('click', handleSearch);
|
||||
|
||||
// 输入框回车触发搜索
|
||||
searchInput.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
|
||||
// 绑定重置事件
|
||||
resetBtn.addEventListener('click', handleReset);
|
||||
|
||||
// 打开详情面板
|
||||
detailBtns.forEach(btn => {
|
||||
btn.addEventListener('click', function () {
|
||||
const tableId = this.getAttribute('data-tableId');
|
||||
|
||||
// 显示加载状态
|
||||
loadingMask.style.display = 'flex';
|
||||
|
||||
// 加载详情内容
|
||||
detailFrame.src = `getFieldDetail?tableId=${tableId}`;
|
||||
|
||||
// 当iframe加载完成后隐藏加载状态
|
||||
detailFrame.onload = function () {
|
||||
loadingMask.style.display = 'none';
|
||||
};
|
||||
|
||||
// 显示详情面板和遮罩
|
||||
detailPanel.classList.add('open');
|
||||
overlay.classList.add('show');
|
||||
|
||||
// 防止背景滚动
|
||||
document.body.style.overflow = 'hidden';
|
||||
});
|
||||
});
|
||||
|
||||
// 关闭详情面板
|
||||
function closeDetail() {
|
||||
detailPanel.classList.remove('open');
|
||||
overlay.classList.remove('show');
|
||||
detailFrame.src = '';
|
||||
|
||||
// 恢复背景滚动
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
closeDetailBtn.addEventListener('click', closeDetail);
|
||||
overlay.addEventListener('click', closeDetail);
|
||||
|
||||
// 按ESC键关闭详情面板
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && detailPanel.classList.contains('open')) {
|
||||
closeDetail();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
313
src/main/resources/templates/field.html
Normal file
313
src/main/resources/templates/field.html
Normal file
@@ -0,0 +1,313 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>数据信息展示</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#e3f2fd',
|
||||
secondary: '#bbdefb',
|
||||
accent: '#4a90e2', // 加深了主色调,提高对比度
|
||||
card: '#ffffff',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(74, 144, 226, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.card-hover {
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.card-hover:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 表格滚动容器样式 */
|
||||
.table-container {
|
||||
max-height: calc(100vh - 180px);
|
||||
overflow-y: auto;
|
||||
-ms-overflow-style: thin;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.table-container::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.table-container::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(74, 144, 226, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* 表头样式优化 - 提高可读性 */
|
||||
.sticky-header th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background-color: #4a90e2; /* 更深的蓝色背景 */
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); /* 添加文字阴影增强可读性 */
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); /* 底部阴影区分表头和内容 */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-primary min-h-screen m-0">
|
||||
<div class="flex h-screen pb-15">
|
||||
<!-- 左侧基本信息区域 - 卡片式布局 -->
|
||||
<div class="w-1/4 bg-secondary p-6 overflow-y-auto scrollbar-thin">
|
||||
<h2 class="text-xl font-bold mb-6 text-gray-800 border-b-2 border-accent pb-2">基本信息</h2>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<!-- 数据表名称卡片 -->
|
||||
<div class="bg-card rounded-lg p-4 shadow-md card-hover">
|
||||
<h3 class="font-semibold text-gray-700 flex items-center">
|
||||
<i class="fa fa-database mr-2 text-accent"></i>数据表名称
|
||||
</h3>
|
||||
<p class="mt-2 text-gray-600"><span th:text="${tableInfo.getTableName()}"></span></p>
|
||||
</div>
|
||||
|
||||
<!-- 创建时间卡片 -->
|
||||
<div class="bg-card rounded-lg p-4 shadow-md card-hover">
|
||||
<h3 class="font-semibold text-gray-700 flex items-center">
|
||||
<i class="fa fa-calendar-plus-o mr-2 text-accent"></i>创建时间
|
||||
</h3>
|
||||
<p class="mt-2 text-gray-600">
|
||||
<span th:text="${tableInfo.getCreateTime()}"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 更新时间卡片 -->
|
||||
<div class="bg-card rounded-lg p-4 shadow-md card-hover">
|
||||
<h3 class="font-semibold text-gray-700 flex items-center">
|
||||
<i class="fa fa-calendar-check-o mr-2 text-accent"></i>更新时间
|
||||
</h3>
|
||||
<p class="mt-2 text-gray-600">
|
||||
<span th:text="${tableInfo.getUpdateTime()}"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 数据记录数卡片 -->
|
||||
<div class="bg-card rounded-lg p-4 shadow-md card-hover">
|
||||
<h3 class="font-semibold text-gray-700 flex items-center">
|
||||
<i class="fa fa-list-ol mr-2 text-accent"></i>数据记录数
|
||||
</h3>
|
||||
<p class="mt-2 text-gray-600">
|
||||
<span th:text="${tableInfo.getDataRows()}"></span> 条
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 存储引擎卡片 -->
|
||||
<div class="bg-card rounded-lg p-4 shadow-md card-hover">
|
||||
<h3 class="font-semibold text-gray-700 flex items-center">
|
||||
<i class="fa fa-cogs mr-2 text-accent"></i>存储引擎
|
||||
</h3>
|
||||
<p class="mt-2 text-gray-600">InnoDB</p>
|
||||
</div>
|
||||
|
||||
<!-- 字符集卡片 -->
|
||||
<div class="bg-card rounded-lg p-4 shadow-md card-hover">
|
||||
<h3 class="font-semibold text-gray-700 flex items-center">
|
||||
<i class="fa fa-font mr-2 text-accent"></i>字符集
|
||||
</h3>
|
||||
<p class="mt-2 text-gray-600">utf8mb4</p>
|
||||
</div>
|
||||
|
||||
<!-- 表描述卡片 -->
|
||||
<div class="bg-card rounded-lg p-4 shadow-md card-hover">
|
||||
<h3 class="font-semibold text-gray-700 flex items-center">
|
||||
<i class="fa fa-file-text-o mr-2 text-accent"></i>表描述
|
||||
</h3>
|
||||
<p class="mt-2 text-gray-600">
|
||||
<span th:text="${tableInfo.getTableComment()}"></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容区域 -->
|
||||
<div class="w-3/4 p-6 overflow-y-auto scrollbar-thin">
|
||||
<!-- 切换标签 -->
|
||||
<div class="flex border-b-2 border-accent mb-6">
|
||||
<button id="fieldBtn" class="tab-btn active px-6 py-3 text-accent font-medium flex items-center gap-2"
|
||||
onclick="showTab('fieldInfo')">
|
||||
<i class="fa fa-table"></i>
|
||||
<span>字段信息</span>
|
||||
</button>
|
||||
<button id="queryBtn" class="tab-btn px-6 py-3 text-gray-600 font-medium flex items-center gap-2"
|
||||
onclick="showTab('queryInfo')">
|
||||
<i class="fa fa-search"></i>
|
||||
<span>查询信息</span>
|
||||
</button>
|
||||
<button id="createBtn" class="tab-btn px-6 py-3 text-gray-600 font-medium flex items-center gap-2"
|
||||
onclick="showTab('createInfo')">
|
||||
<i class="fa fa-code"></i>
|
||||
<span>创表信息</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 提示消息容器 -->
|
||||
<div id="notification"
|
||||
class="fixed top-4 right-4 px-4 py-3 rounded-lg shadow-lg transform translate-x-full transition-transform duration-300 flex items-center gap-2 z-50"></div>
|
||||
|
||||
<!-- 字段信息 - 优化表格滚动和表头可读性 -->
|
||||
<div id="fieldInfo" class="tab-content block">
|
||||
<div class="bg-white rounded-lg shadow-sm overflow-hidden">
|
||||
<!-- 表格滚动容器 -->
|
||||
<div class="table-container">
|
||||
<table class="min-w-full table-fixed">
|
||||
<thead class="sticky-header">
|
||||
<tr>
|
||||
<!-- 按需求设置固定列宽,剩余宽度分配给名称和说明 -->
|
||||
<th class="py-3 px-4 text-left w-[120px]">字段序号</th>
|
||||
<th class="py-3 px-4 text-left w-[220px]">字段名称</th>
|
||||
<th class="py-3 px-4 text-left w-[120px]">字段类型</th>
|
||||
<th class="py-3 px-4 text-left w-[120px]">字段长度</th>
|
||||
<th class="py-3 px-4 text-left grow">字段说明</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200" th:each="field : ${fields}">
|
||||
<!-- 数据行 -->
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="py-3 px-4 w-[120px]" th:text="${field.getFieldOrder()}"></td>
|
||||
<td class="py-3 px-4 w-[220px]" th:text="${field.getFieldName()}"></td>
|
||||
<td class="py-3 px-4 w-[120px]" th:text="${field.getFieldType()}"></td>
|
||||
<td class="py-3 px-4 w-[120px]" th:text="${field.getFieldLength()}"></td>
|
||||
<td class="py-3 px-4 grow" th:text="${field.getFieldRemark()}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 查询信息 -->
|
||||
<div id="queryInfo" class="tab-content hidden">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<h3 class="text-lg font-semibold text-gray-800">SELECT 语句</h3>
|
||||
<button onclick="copyToClipboard('queryText')"
|
||||
class="bg-accent hover:bg-blue-600 text-white px-4 py-2 rounded flex items-center gap-1 transition-colors">
|
||||
<i class="fa fa-copy"></i>
|
||||
<span>复制</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="bg-gray-800 text-gray-100 p-4 rounded-lg font-mono text-sm h-[calc(100vh-180px)] overflow-auto scrollbar-thin">
|
||||
<pre id="queryText" th:text="${selectSql}"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 创表信息 -->
|
||||
<div id="createInfo" class="tab-content hidden">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<h3 class="text-lg font-semibold text-gray-800">CREATE 语句</h3>
|
||||
<button onclick="copyToClipboard('createText')"
|
||||
class="bg-accent hover:bg-blue-600 text-white px-4 py-2 rounded flex items-center gap-1 transition-colors">
|
||||
<i class="fa fa-copy"></i>
|
||||
<span>复制</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="bg-gray-800 text-gray-100 p-4 rounded-lg font-mono text-sm h-[calc(100vh-180px)] overflow-auto scrollbar-thin">
|
||||
<pre id="createText" th:text="${createSql}"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 切换标签显示
|
||||
function showTab(tabId) {
|
||||
document.querySelectorAll('.tab-content').forEach(tab => {
|
||||
tab.classList.add('hidden');
|
||||
});
|
||||
document.getElementById(tabId).classList.remove('hidden');
|
||||
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||||
btn.classList.remove('text-accent');
|
||||
btn.classList.add('text-gray-600');
|
||||
});
|
||||
|
||||
if (tabId === 'fieldInfo') {
|
||||
document.getElementById('fieldBtn').classList.remove('text-gray-600');
|
||||
document.getElementById('fieldBtn').classList.add('text-accent');
|
||||
} else if (tabId === 'queryInfo') {
|
||||
document.getElementById('queryBtn').classList.remove('text-gray-600');
|
||||
document.getElementById('queryBtn').classList.add('text-accent');
|
||||
} else if (tabId === 'createInfo') {
|
||||
document.getElementById('createBtn').classList.remove('text-gray-600');
|
||||
document.getElementById('createBtn').classList.add('text-accent');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示通知消息
|
||||
function showNotification(message, isSuccess) {
|
||||
const notification = document.getElementById('notification');
|
||||
notification.textContent = '';
|
||||
|
||||
notification.className = 'fixed top-4 right-4 px-4 py-3 rounded-lg shadow-lg transform translate-x-full transition-transform duration-300 flex items-center gap-2 z-50';
|
||||
notification.classList.add(isSuccess ? 'bg-green-500' : 'bg-red-500');
|
||||
notification.classList.add('text-white');
|
||||
|
||||
const icon = document.createElement('i');
|
||||
icon.className = isSuccess ? 'fa fa-check-circle' : 'fa fa-exclamation-circle';
|
||||
notification.appendChild(icon);
|
||||
|
||||
const text = document.createTextNode(message);
|
||||
notification.appendChild(text);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.classList.remove('translate-x-full');
|
||||
}, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.classList.add('translate-x-full');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 复制文本到剪贴板并显示提示
|
||||
function copyToClipboard(elementId) {
|
||||
const text = document.getElementById(elementId).textContent;
|
||||
navigator.clipboard.writeText(text)
|
||||
.then(() => {
|
||||
showNotification('复制成功!', true);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('复制失败: ', err);
|
||||
showNotification('复制失败,请手动复制', false);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1035
src/main/resources/templates/file.html
Normal file
1035
src/main/resources/templates/file.html
Normal file
File diff suppressed because it is too large
Load Diff
348
src/main/resources/templates/icon.html
Normal file
348
src/main/resources/templates/icon.html
Normal file
@@ -0,0 +1,348 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>超大容量快捷图标库(300+)</title>
|
||||
<!-- 引入外部资源 -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
|
||||
|
||||
<!-- Tailwind 配置 -->
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#165DFF',
|
||||
secondary: '#36CFC9',
|
||||
success: '#52C41A',
|
||||
warning: '#FAAD14',
|
||||
danger: '#FF4D4F',
|
||||
purple: '#722ED1',
|
||||
blue: '#1890FF',
|
||||
green: '#00B42A',
|
||||
yellow: '#FF7D00',
|
||||
indigo: '#4096ff',
|
||||
pink: '#f5222d',
|
||||
cyan: '#13c2c2',
|
||||
orange: '#fa8c16',
|
||||
dark: '#1D2129',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 自定义工具类 -->
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.icon-card {
|
||||
@apply flex flex-col items-center p-4 rounded-xl border border-gray-200 hover:border-primary hover:shadow-md transition-all duration-300 cursor-pointer;
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
@apply w-14 h-14 rounded-full flex items-center justify-center mb-3 text-xl;
|
||||
}
|
||||
|
||||
.icon-search-highlight {
|
||||
@apply bg-primary/5 border-primary/30;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 p-4 md:p-6">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<!-- 头部与搜索区 -->
|
||||
<div class="mb-8">
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-dark mb-2">快捷图标库(300+图标)</h1>
|
||||
<p class="text-gray-600 mb-6">海量功能图标与名称组合,支持搜索和筛选</p>
|
||||
|
||||
<!-- 搜索与筛选区 -->
|
||||
<div class="flex flex-col sm:flex-row gap-4 mb-6">
|
||||
<div class="relative flex-1">
|
||||
<input
|
||||
type="text"
|
||||
id="iconSearch"
|
||||
placeholder="搜索图标名称或类名(如:数据库、fa-database)..."
|
||||
class="w-full py-3 pl-10 pr-4 rounded-lg border border-gray-200 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-all"
|
||||
>
|
||||
<i class="fa fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<select id="colorFilter"
|
||||
class="py-3 px-4 rounded-lg border border-gray-200 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary">
|
||||
<option value="all">所有颜色</option>
|
||||
<option value="primary"> primary</option>
|
||||
<option value="secondary"> secondary</option>
|
||||
<option value="success"> success</option>
|
||||
<option value="warning"> warning</option>
|
||||
<option value="danger"> danger</option>
|
||||
<option value="purple"> purple</option>
|
||||
</select>
|
||||
|
||||
<button id="resetFilter" class="py-3 px-4 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors">
|
||||
<i class="fa fa-refresh mr-1"></i> 重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计信息 -->
|
||||
<div class="text-sm text-gray-500 mb-2">
|
||||
<span id="iconCount">0</span> 个图标(显示 <span id="visibleCount">0</span> 个)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图标网格容器 -->
|
||||
<div id="iconGrid"
|
||||
class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4 md:gap-6">
|
||||
<!-- 图标将通过JavaScript动态生成 -->
|
||||
</div>
|
||||
|
||||
<!-- 使用说明 -->
|
||||
<div class="mt-10 p-4 bg-white rounded-lg border border-gray-200">
|
||||
<h3 class="font-bold mb-3">使用说明</h3>
|
||||
<ul class="list-disc pl-5 text-gray-600 space-y-2 text-sm">
|
||||
<li>每个图标下方显示了对应的Font Awesome类名(如fa-database)</li>
|
||||
<li>点击图标可复制其HTML代码到剪贴板</li>
|
||||
<li>支持按名称、类名搜索和按颜色筛选</li>
|
||||
<li>如需调整颜色,可修改容器的bg-*和text-*类(如bg-primary/10和text-primary)</li>
|
||||
<li>Font Awesome官方文档:<a href="https://fontawesome.com/v4/icons/" class="text-primary hover:underline"
|
||||
target="_blank">https://fontawesome.com/v4/icons/</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 复制成功提示 -->
|
||||
<div id="copyToast"
|
||||
class="fixed bottom-6 right-6 bg-success text-white px-4 py-2 rounded-lg shadow-lg opacity-0 transition-opacity duration-300 flex items-center">
|
||||
<i class="fa fa-check-circle mr-2"></i> 已复制到剪贴板
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 颜色池 - 用于随机分配图标颜色
|
||||
const colorPool = [
|
||||
'primary', 'secondary', 'success', 'warning', 'danger',
|
||||
'purple', 'blue', 'green', 'yellow', 'indigo', 'pink', 'cyan', 'orange'
|
||||
];
|
||||
|
||||
// 基础图标数据(50个示例,实际使用时可扩展到300+)
|
||||
const baseIcons = [
|
||||
{name: '数据库管理', icon: 'fa-database'},
|
||||
{name: '性能监控', icon: 'fa-line-chart'},
|
||||
{name: '用户管理', icon: 'fa-user-circle'},
|
||||
{name: '安全中心', icon: 'fa-shield'},
|
||||
{name: '日志审计', icon: 'fa-file-text'},
|
||||
{name: '系统设置', icon: 'fa-cog'},
|
||||
{name: '备份恢复', icon: 'fa-cloud-upload'},
|
||||
{name: 'API接口', icon: 'fa-plug'},
|
||||
{name: '告警中心', icon: 'fa-bell'},
|
||||
{name: '网络配置', icon: 'fa-wifi'},
|
||||
{name: '存储管理', icon: 'fa-hdd-o'},
|
||||
{name: '任务调度', icon: 'fa-calendar-check-o'},
|
||||
{name: '消息中心', icon: 'fa-envelope'},
|
||||
{name: '报表分析', icon: 'fa-bar-chart'},
|
||||
{name: '帮助中心', icon: 'fa-question-circle'},
|
||||
{name: '服务器管理', icon: 'fa-server'},
|
||||
{name: '代码仓库', icon: 'fa-code'},
|
||||
{name: '项目管理', icon: 'fa-tasks'},
|
||||
{name: '团队管理', icon: 'fa-users'},
|
||||
{name: '密钥管理', icon: 'fa-key'},
|
||||
{name: '流量分析', icon: 'fa-area-chart'},
|
||||
{name: '访问控制', icon: 'fa-ban'},
|
||||
{name: '风险检测', icon: 'fa-exclamation-triangle'},
|
||||
{name: '国际化设置', icon: 'fa-language'},
|
||||
{name: '资源下载', icon: 'fa-download'},
|
||||
{name: '文件上传', icon: 'fa-upload'},
|
||||
{name: '定时任务', icon: 'fa-clock-o'},
|
||||
{name: '数据地图', icon: 'fa-map'},
|
||||
{name: '文档中心', icon: 'fa-book'},
|
||||
{name: '收藏夹', icon: 'fa-star'},
|
||||
{name: '邮件系统', icon: 'fa-envelope-o'},
|
||||
{name: '即时通讯', icon: 'fa-comments'},
|
||||
{name: '视频会议', icon: 'fa-video-camera'},
|
||||
{name: '语音通话', icon: 'fa-phone'},
|
||||
{name: '数据同步', icon: 'fa-exchange'},
|
||||
{name: '缓存清理', icon: 'fa-trash'},
|
||||
{name: '病毒扫描', icon: 'fa-bug'},
|
||||
{name: '防火墙', icon: 'fa-shield'},
|
||||
{name: '负载均衡', icon: 'fa-balance-scale'},
|
||||
{name: '容器管理', icon: 'fa-cubes'},
|
||||
{name: '虚拟机', icon: 'fa-desktop'},
|
||||
{name: '云存储', icon: 'fa-cloud'},
|
||||
{name: 'CDN加速', icon: 'fa-bolt'},
|
||||
{name: 'DNS配置', icon: 'fa-sitemap'},
|
||||
{name: 'SSL证书', icon: 'fa-lock'},
|
||||
{name: 'API文档', icon: 'fa-file-code-o'},
|
||||
{name: '错误跟踪', icon: 'fa-bug'},
|
||||
{name: '性能分析', icon: 'fa-tachometer'},
|
||||
{name: '代码审查', icon: 'fa-search'}
|
||||
];
|
||||
|
||||
// 扩展到300个图标(通过组合基础图标+功能前缀)
|
||||
function generate300Icons() {
|
||||
const prefixes = [
|
||||
'用户', '系统', '数据', '安全', '网络', '存储', '应用',
|
||||
'服务', '设备', '终端', '日志', '监控', '分析', '报表',
|
||||
'配置', '管理', '运维', '开发', '测试', '生产', '备份',
|
||||
'恢复', '同步', '迁移', '部署', '发布', '更新', '升级',
|
||||
'降级', '回滚', '审计', '统计', '查询', '过滤', '导出',
|
||||
'导入', '打印', '分享', '协作', '权限', '角色', '部门'
|
||||
];
|
||||
|
||||
const icons = [...baseIcons];
|
||||
let currentLength = baseIcons.length;
|
||||
|
||||
// 循环生成直到达到300个
|
||||
while (currentLength < 300) {
|
||||
// 随机组合前缀和基础图标
|
||||
const randomPrefix = prefixes[Math.floor(Math.random() * prefixes.length)];
|
||||
const randomBase = baseIcons[Math.floor(Math.random() * baseIcons.length)];
|
||||
|
||||
// 避免重复名称
|
||||
const newName = `${randomPrefix}${randomBase.name}`;
|
||||
if (!icons.some(icon => icon.name === newName)) {
|
||||
icons.push({
|
||||
name: newName,
|
||||
icon: randomBase.icon // 复用基础图标
|
||||
});
|
||||
currentLength++;
|
||||
}
|
||||
}
|
||||
|
||||
return icons;
|
||||
}
|
||||
|
||||
// 生成所有图标
|
||||
const allIcons = generate300Icons();
|
||||
const iconGrid = document.getElementById('iconGrid');
|
||||
const iconCountEl = document.getElementById('iconCount');
|
||||
const visibleCountEl = document.getElementById('visibleCount');
|
||||
const copyToast = document.getElementById('copyToast');
|
||||
|
||||
// 初始化图标网格
|
||||
function renderIcons(iconsToRender = allIcons) {
|
||||
// 清空网格
|
||||
iconGrid.innerHTML = '';
|
||||
|
||||
// 渲染图标
|
||||
iconsToRender.forEach(icon => {
|
||||
// 随机分配颜色(也可根据需要固定颜色)
|
||||
const color = colorPool[Math.floor(Math.random() * colorPool.length)];
|
||||
|
||||
const iconCard = document.createElement('div');
|
||||
iconCard.className = 'icon-card';
|
||||
iconCard.dataset.name = icon.name;
|
||||
iconCard.dataset.icon = icon.icon;
|
||||
iconCard.dataset.color = color;
|
||||
|
||||
iconCard.innerHTML = `
|
||||
<div class="icon-container bg-${color}/10 text-${color}">
|
||||
<i class="fa ${icon.icon}"></i>
|
||||
</div>
|
||||
<span class="font-medium text-center">${icon.name}</span>
|
||||
<div class="text-xs text-gray-500 mt-1">${icon.icon}</div>
|
||||
`;
|
||||
|
||||
// 点击复制功能
|
||||
iconCard.addEventListener('click', () => {
|
||||
const html = `
|
||||
<div class="icon-card">
|
||||
<div class="icon-container bg-${color}/10 text-${color}">
|
||||
<i class="fa ${icon.icon}"></i>
|
||||
</div>
|
||||
<span class="font-medium">${icon.name}</span>
|
||||
<div class="text-xs text-gray-500 mt-2">${icon.icon}</div>
|
||||
</div>
|
||||
`.trim();
|
||||
|
||||
navigator.clipboard.writeText(html).then(() => {
|
||||
// 显示复制成功提示
|
||||
copyToast.style.opacity = '1';
|
||||
setTimeout(() => {
|
||||
copyToast.style.opacity = '0';
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
iconGrid.appendChild(iconCard);
|
||||
});
|
||||
|
||||
// 更新统计
|
||||
iconCountEl.textContent = allIcons.length;
|
||||
visibleCountEl.textContent = iconsToRender.length;
|
||||
}
|
||||
|
||||
// 初始化页面
|
||||
renderIcons();
|
||||
|
||||
// 搜索功能
|
||||
document.getElementById('iconSearch').addEventListener('input', (e) => {
|
||||
const searchTerm = e.target.value.toLowerCase().trim();
|
||||
const colorFilter = document.getElementById('colorFilter').value;
|
||||
|
||||
if (!searchTerm && colorFilter === 'all') {
|
||||
renderIcons();
|
||||
return;
|
||||
}
|
||||
|
||||
// 筛选图标
|
||||
const filtered = allIcons.filter(icon => {
|
||||
const matchesSearch =
|
||||
icon.name.toLowerCase().includes(searchTerm) ||
|
||||
icon.icon.toLowerCase().includes(searchTerm);
|
||||
|
||||
const matchesColor = colorFilter === 'all' || true; // 颜色筛选在渲染时处理
|
||||
|
||||
return matchesSearch && matchesColor;
|
||||
});
|
||||
|
||||
renderIcons(filtered);
|
||||
|
||||
// 高亮搜索结果
|
||||
if (searchTerm) {
|
||||
document.querySelectorAll('.icon-card').forEach(card => {
|
||||
const nameMatch = card.dataset.name.toLowerCase().includes(searchTerm);
|
||||
const iconMatch = card.dataset.icon.toLowerCase().includes(searchTerm);
|
||||
|
||||
if (nameMatch || iconMatch) {
|
||||
card.classList.add('icon-search-highlight');
|
||||
} else {
|
||||
card.classList.remove('icon-search-highlight');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 应用颜色筛选
|
||||
if (colorFilter !== 'all') {
|
||||
document.querySelectorAll('.icon-card').forEach(card => {
|
||||
if (card.dataset.color === colorFilter) {
|
||||
card.style.display = '';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// 更新可见计数
|
||||
const visibleCount = document.querySelectorAll(`.icon-card[data-color="${colorFilter}"]`).length;
|
||||
visibleCountEl.textContent = visibleCount;
|
||||
}
|
||||
});
|
||||
|
||||
// 颜色筛选
|
||||
document.getElementById('colorFilter').addEventListener('change', (e) => {
|
||||
// 触发搜索事件以应用筛选
|
||||
document.getElementById('iconSearch').dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
// 重置筛选
|
||||
document.getElementById('resetFilter').addEventListener('click', () => {
|
||||
document.getElementById('iconSearch').value = '';
|
||||
document.getElementById('colorFilter').value = 'all';
|
||||
renderIcons();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
712
src/main/resources/templates/index.html
Normal file
712
src/main/resources/templates/index.html
Normal file
@@ -0,0 +1,712 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>系统首页</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#E3F2FD',
|
||||
secondary: '#BBDEFB',
|
||||
accent: '#64B5F6',
|
||||
dark: '#1976D2',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(100, 181, 246, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.status-online {
|
||||
@apply bg-green-500;
|
||||
}
|
||||
|
||||
.status-offline {
|
||||
@apply bg-red-500;
|
||||
}
|
||||
|
||||
.status-warning {
|
||||
@apply bg-yellow-500;
|
||||
}
|
||||
|
||||
/* 自定义提示弹窗样式 */
|
||||
.custom-toast {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
padding: 12px 20px;
|
||||
border-radius: 6px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
|
||||
z-index: 9999;
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.toast-success {
|
||||
background-color: #10B981; /* 成功绿色 */
|
||||
}
|
||||
|
||||
.toast-error {
|
||||
background-color: #EF4444; /* 错误红色 */
|
||||
}
|
||||
|
||||
.toast-show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-primary h-screen overflow-hidden flex flex-col">
|
||||
<!-- 顶部区域 -->
|
||||
<header class="bg-primary border-b border-accent/30 py-3 px-6 flex items-center justify-between shadow-sm">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 bg-dark rounded-lg flex items-center justify-center text-white">
|
||||
<i class="fa fa-cogs text-xl"></i>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold text-dark"><a th:href="@{/biz/index}">智慧门户</a></h1>
|
||||
</div>
|
||||
|
||||
<!-- 中间模块导航 -->
|
||||
<nav class="flex items-center space-x-8">
|
||||
<a href="javascript:loadContent('dataMap')"
|
||||
class="text-dark hover:text-accent transition-colors duration-200 flex items-center cursor-pointer">
|
||||
<i class="fa fa-map-o mr-2"></i>
|
||||
<span>数据地图</span>
|
||||
</a>
|
||||
<a href="javascript:loadContent('dataDoc')"
|
||||
class="text-dark hover:text-accent transition-colors duration-200 flex items-center cursor-pointer">
|
||||
<i class="fa fa-book mr-2"></i>
|
||||
<span>文档中心</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<!-- 右侧账号信息 -->
|
||||
<div class="relative">
|
||||
<button id="userMenuBtn" class="flex items-center space-x-2 focus:outline-none">
|
||||
<img th:src="@{/images/user.jpg}" alt="用户头像"
|
||||
class="w-8 h-8 rounded-full border-2 border-accent">
|
||||
<span class="text-dark font-medium" th:text="${uname}"></span>
|
||||
<i class="fa fa-chevron-down text-xs text-dark"></i>
|
||||
</button>
|
||||
|
||||
<div id="userMenu" class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 hidden z-10">
|
||||
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary transition-colors duration-200">
|
||||
<i class="fa fa-user mr-2"></i>个人信息
|
||||
</a>
|
||||
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-primary transition-colors duration-200">
|
||||
<i class="fa fa-cog mr-2"></i>系统设置
|
||||
</a>
|
||||
<div class="border-t border-gray-200 my-1"></div>
|
||||
<a href="javascript:void(0);" id="logoutLink"
|
||||
class="block px-4 py-2 text-sm text-red-600 hover:bg-red-50 transition-colors duration-200">
|
||||
<i class="fa fa-sign-out mr-2"></i>退出登录
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<main class="flex-1 bg-primary overflow-hidden flex flex-col">
|
||||
<!-- 状态栏 -->
|
||||
<div class="bg-secondary/50 px-6 py-2 flex justify-between items-center border-b border-accent/20">
|
||||
<div class="flex space-x-6">
|
||||
<div class="flex items-center text-dark">
|
||||
<i class="fa fa-clock-o mr-2"></i>
|
||||
<span id="systemUptime">系统运行时长: <span th:text="${times}"></span> </span>
|
||||
</div>
|
||||
<div class="flex items-center text-dark">
|
||||
<i class="fa fa-users mr-2"></i>
|
||||
<span>在线人数: <span id="onlineUsers">24</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-dark font-medium" id="currentTime"></div>
|
||||
</div>
|
||||
|
||||
<!-- 主题区域 -->
|
||||
<div class="flex-1 px-6 py-4 overflow-hidden mb-15">
|
||||
<!-- 内容切换核心容器 -->
|
||||
<div id="contentContainer" class="h-full bg-white rounded-lg shadow-sm overflow-hidden">
|
||||
<!-- 默认首页内容 -->
|
||||
<div id="defaultContent" class="h-full p-4 overflow-hidden">
|
||||
<div class="flex h-full space-x-4">
|
||||
<!-- 第一部分 (30%) -->
|
||||
<div class="w-[30%] bg-secondary/50 rounded-lg shadow-sm flex flex-col overflow-hidden">
|
||||
<!-- 日历 (50%) -->
|
||||
<div class="h-[50%] p-4 border-b border-accent/20 overflow-hidden">
|
||||
<h2 class="text-dark font-semibold mb-3 flex items-center">
|
||||
<i class="fa fa-calendar mr-2"></i>日历
|
||||
</h2>
|
||||
<div id="calendar" class="bg-white/70 rounded-lg p-3 h-[calc(100%-30px)] flex flex-col">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<button class="text-dark hover:text-accent"><i class="fa fa-chevron-left"></i>
|
||||
</button>
|
||||
<h3 class="font-medium text-dark">2025年11月</h3>
|
||||
<button class="text-dark hover:text-accent"><i class="fa fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-7 gap-1 text-center text-xs text-gray-600 mb-1">
|
||||
<div>日</div>
|
||||
<div>一</div>
|
||||
<div>二</div>
|
||||
<div>三</div>
|
||||
<div>四</div>
|
||||
<div>五</div>
|
||||
<div>六</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-7 gap-1 text-center flex-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主机列表 (50%) -->
|
||||
<div class="h-[50%] p-4 overflow-hidden">
|
||||
<h2 class="text-dark font-semibold mb-3 flex items-center">
|
||||
<i class="fa fa-server mr-2"></i>主机列表
|
||||
</h2>
|
||||
<div class="bg-white/70 rounded-lg p-3 h-[calc(100%-30px)] overflow-y-auto scrollbar-thin">
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between p-2 border-b border-gray-100"
|
||||
th:each="host : ${hosts}">
|
||||
<div>
|
||||
<div class="font-medium text-dark">
|
||||
<a class="text-primary" style="cursor: pointer;"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#serverDetailModal"
|
||||
th:data-hostid="${host.getHostId()}">
|
||||
<span th:text="${host.getHostname()}">主机名</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500"><p>主机地址:<span
|
||||
th:text="${host.getIpAddress()}"></span></p>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500"><p>在线时间:<span
|
||||
th:text="${host.getLastOnlineTime()}"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<span class="w-2 h-2 rounded-full status-online mr-2"></span>
|
||||
<span class="text-sm text-green-600"
|
||||
th:text="${host.getUstatus() == '1' ? '运行中' : '已离线' }"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第二部分 (45%) -->
|
||||
<div class="w-[45%] bg-secondary/50 rounded-lg shadow-sm flex flex-col overflow-hidden">
|
||||
<div class="h-full p-4 overflow-hidden">
|
||||
<div class="flex border-b border-accent/20 pb-2 mb-3">
|
||||
<button id="todoBtn" class="px-4 py-1 bg-accent text-white rounded-md mr-2 font-medium">
|
||||
<i class="fa fa-tasks mr-1"></i>待办信息
|
||||
</button>
|
||||
<button id="notificationBtn"
|
||||
class="px-4 py-1 bg-white/50 text-dark hover:bg-accent/30 rounded-md font-medium transition-colors duration-200">
|
||||
<i class="fa fa-bell mr-1"></i>通知信息
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 待办信息内容 -->
|
||||
<div id="todoContent"
|
||||
class="bg-white/70 rounded-lg p-3 h-[calc(100%-50px)] overflow-y-auto scrollbar-thin">
|
||||
<div class="space-y-4">
|
||||
<div class="p-3 border-l-4 border-accent bg-primary/50 rounded-r"
|
||||
th:each="todo : ${todoViews}" th:data-task-id="${todo.getSubTaskId()}">
|
||||
<div class="flex justify-between items-start">
|
||||
<h3 class="font-medium text-dark" th:text="${todo.getTaskName()}"></h3>
|
||||
<span class="text-xs bg-red-100 text-red-600 px-2 py-0.5 rounded"
|
||||
th:text="${todo.getPriority()}"></span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 mt-1" th:text="${todo.getTaskDesc()}"></p>
|
||||
<div class="flex justify-between items-center mt-2">
|
||||
<span class="text-xs text-gray-500">截止日期:<span
|
||||
th:text="${todo.getDeadline()}"></span></span>
|
||||
<button class="text-xs text-accent hover:underline">处理</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 通知信息内容 (默认隐藏) -->
|
||||
<div id="notificationContent"
|
||||
class="bg-white/70 rounded-lg p-3 h-[calc(100%-50px)] overflow-y-auto scrollbar-thin hidden">
|
||||
<div class="space-y-4">
|
||||
<div class="p-3 border-b border-gray-100">
|
||||
<div class="flex justify-between items-start">
|
||||
<h3 class="font-medium text-dark">数据库备份成功</h3>
|
||||
<span class="text-xs text-gray-500">今天 08:30</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 mt-1">
|
||||
主数据库自动备份已完成,备份文件大小:2.4GB,存储路径:/backup/db/20251115/</p>
|
||||
</div>
|
||||
|
||||
<div class="p-3 border-b border-gray-100">
|
||||
<div class="flex justify-between items-start">
|
||||
<h3 class="font-medium text-dark">用户登录提醒</h3>
|
||||
<span class="text-xs text-gray-500">昨天 16:45</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 mt-1">您的账号在新设备(Windows 10, Chrome
|
||||
120)登录,IP地址:113.25.XX.XX,如非本人操作,请及时修改密码。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第三部分 (25%) -->
|
||||
<div class="w-[25%] bg-secondary/50 rounded-lg shadow-sm flex flex-col overflow-hidden">
|
||||
<!-- 快捷登录 -->
|
||||
<div class="h-full p-4 overflow-hidden">
|
||||
<h2 class="text-dark font-semibold mb-3 flex items-center">
|
||||
<i class="fa fa-rocket mr-2"></i>快捷登录
|
||||
</h2>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<div class="relative mb-3">
|
||||
<input type="text" id="quickSearch" placeholder="搜索快捷登录..."
|
||||
class="w-full pl-9 pr-3 py-2 bg-white/70 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-accent">
|
||||
<i class="fa fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
|
||||
</div>
|
||||
|
||||
<!-- 快捷登录图标列表 -->
|
||||
<div id="quickLinks"
|
||||
class="bg-white/70 rounded-lg p-4 h-[calc(100%-80px)] overflow-y-auto scrollbar-thin">
|
||||
<div class="grid grid-cols-4 gap-4">
|
||||
<!-- 快捷登录项 -->
|
||||
<a th:each="storage : ${storages}" th:href="${storage.getHomepageUrl()}"
|
||||
target="_blank"
|
||||
class="flex flex-col items-center p-3 hover:bg-primary rounded-lg transition-colors duration-200 group">
|
||||
<div class="w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center text-blue-600 group-hover:bg-blue-600 group-hover:text-white transition-colors duration-200">
|
||||
<i th:class="${storage.getIconClass()}"></i>
|
||||
</div>
|
||||
<span class="text-xs mt-2 text-center text-dark"
|
||||
th:text="${storage.getSystemName()}"></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容加载iframe(默认隐藏) -->
|
||||
<iframe id="contentFrame" class="w-full h-full border-0 hidden" src=""></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 服务器详情模态框 -->
|
||||
<div class="modal fade" id="serverDetailModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg modal-dialog-centered" style="width: 80%; max-width: 1400px;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">服务器详情</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-0" style="max-height: 60vh; height: 60vh; overflow: hidden;">
|
||||
<div style="height: 100%; overflow-y: auto; scrollbar-width: thin; position: relative;">
|
||||
<!-- 加载中动画 -->
|
||||
<div id="loadingMask"
|
||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.8); display: none; justify-content: center; align-items: center; z-index: 100;">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">加载中...</span>
|
||||
</div>
|
||||
</div>
|
||||
<iframe id="detailFrame" width="100%" height="100%" frameborder="0"
|
||||
style="min-height: 600px;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 退出确认弹窗 -->
|
||||
<div id="logoutModal"
|
||||
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 opacity-0 pointer-events-none transition-opacity duration-300">
|
||||
<div class="bg-white rounded-lg w-full max-w-md p-6 transform scale-95 transition-transform duration-300">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">温馨提示</h3>
|
||||
<p class="text-gray-600 mb-6">您确定要退出当前系统吗?</p>
|
||||
<div class="flex justify-end gap-3">
|
||||
<button id="cancelLogout"
|
||||
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors">
|
||||
取消
|
||||
</button>
|
||||
<button id="confirmLogout"
|
||||
class="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors">
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 处理意见弹窗 -->
|
||||
<div id="handleTaskModal"
|
||||
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50 opacity-0 pointer-events-none transition-opacity duration-300">
|
||||
<div class="bg-white rounded-lg w-full max-w-md p-6 transform scale-95 transition-transform duration-300">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">待办信息处理意见:</h3>
|
||||
<div class="mb-4">
|
||||
<textarea id="taskOpinion" rows="3"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-accent"
|
||||
placeholder="请输入处理意见..."></textarea>
|
||||
</div>
|
||||
<div class="flex justify-end gap-3">
|
||||
<button id="cancelHandleTask"
|
||||
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors">
|
||||
取消
|
||||
</button>
|
||||
<button id="confirmHandleTask"
|
||||
class="px-4 py-2 bg-accent text-white rounded-md hover:bg-dark transition-colors">
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 引入Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// 提示弹窗工具函数 - 新增
|
||||
function showToast(message, type = 'success') {
|
||||
// 创建弹窗元素
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `custom-toast toast-${type}`;
|
||||
toast.textContent = message;
|
||||
document.body.appendChild(toast);
|
||||
|
||||
// 显示弹窗
|
||||
setTimeout(() => toast.classList.add('toast-show'), 10);
|
||||
|
||||
// 3秒后隐藏并移除
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('toast-show');
|
||||
setTimeout(() => document.body.removeChild(toast), 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 动态日历渲染函数
|
||||
function renderCalendar(year, month) {
|
||||
const calendarContainer = document.querySelector('#calendar .grid:last-child');
|
||||
const monthTitle = document.querySelector('#calendar h3');
|
||||
|
||||
calendarContainer.innerHTML = '';
|
||||
monthTitle.textContent = `${year}年${month + 1}月`;
|
||||
|
||||
const firstDay = new Date(year, month, 1).getDay();
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
const lastDayOfPrevMonth = new Date(year, month, 0).getDate();
|
||||
|
||||
// 渲染上月剩余天数
|
||||
for (let i = firstDay - 1; i >= 0; i--) {
|
||||
const prevDay = document.createElement('div');
|
||||
prevDay.className = 'py-2 text-gray-400 text-sm';
|
||||
prevDay.textContent = lastDayOfPrevMonth - i;
|
||||
calendarContainer.appendChild(prevDay);
|
||||
}
|
||||
|
||||
// 渲染当月天数
|
||||
const today = new Date();
|
||||
const isCurrentMonth = today.getFullYear() === year && today.getMonth() === month;
|
||||
const currentDate = today.getDate();
|
||||
|
||||
for (let i = 1; i <= daysInMonth; i++) {
|
||||
const day = document.createElement('div');
|
||||
day.className = 'py-2 text-sm';
|
||||
if (isCurrentMonth && i === currentDate) {
|
||||
day.classList.add('bg-accent', 'text-white', 'rounded-full', 'font-medium');
|
||||
}
|
||||
day.textContent = i;
|
||||
calendarContainer.appendChild(day);
|
||||
}
|
||||
|
||||
// 渲染下月起始天数
|
||||
const totalDays = firstDay + daysInMonth;
|
||||
const nextMonthDays = totalDays % 7 !== 0 ? 7 - (totalDays % 7) : 0;
|
||||
for (let i = 1; i <= nextMonthDays; i++) {
|
||||
const nextDay = document.createElement('div');
|
||||
nextDay.className = 'py-2 text-gray-400 text-sm';
|
||||
nextDay.textContent = i;
|
||||
calendarContainer.appendChild(nextDay);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化日历
|
||||
let currentDate = new Date();
|
||||
let currentYear = currentDate.getFullYear();
|
||||
let currentMonth = currentDate.getMonth();
|
||||
renderCalendar(currentYear, currentMonth);
|
||||
|
||||
// 绑定月份切换事件
|
||||
document.querySelectorAll('#calendar button')[0].addEventListener('click', () => {
|
||||
currentMonth--;
|
||||
if (currentMonth < 0) {
|
||||
currentMonth = 11;
|
||||
currentYear--;
|
||||
}
|
||||
renderCalendar(currentYear, currentMonth);
|
||||
});
|
||||
|
||||
document.querySelectorAll('#calendar button')[1].addEventListener('click', () => {
|
||||
currentMonth++;
|
||||
if (currentMonth > 11) {
|
||||
currentMonth = 0;
|
||||
currentYear++;
|
||||
}
|
||||
renderCalendar(currentYear, currentMonth);
|
||||
});
|
||||
|
||||
// 内容加载函数
|
||||
function loadContent(pageUrl) {
|
||||
const defaultContent = document.getElementById('defaultContent');
|
||||
const contentFrame = document.getElementById('contentFrame');
|
||||
defaultContent.classList.add('hidden');
|
||||
contentFrame.classList.remove('hidden');
|
||||
contentFrame.src = pageUrl;
|
||||
}
|
||||
|
||||
// 返回首页函数
|
||||
function backToHome() {
|
||||
const defaultContent = document.getElementById('defaultContent');
|
||||
const contentFrame = document.getElementById('contentFrame');
|
||||
defaultContent.classList.remove('hidden');
|
||||
contentFrame.classList.add('hidden');
|
||||
contentFrame.src = '';
|
||||
}
|
||||
|
||||
// 用户菜单切换
|
||||
const userMenuBtn = document.getElementById('userMenuBtn');
|
||||
const userMenu = document.getElementById('userMenu');
|
||||
userMenuBtn.addEventListener('click', () => userMenu.classList.toggle('hidden'));
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!userMenuBtn.contains(e.target) && !userMenu.contains(e.target)) {
|
||||
userMenu.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// 待办和通知切换
|
||||
const todoBtn = document.getElementById('todoBtn');
|
||||
const notificationBtn = document.getElementById('notificationBtn');
|
||||
const todoContent = document.getElementById('todoContent');
|
||||
const notificationContent = document.getElementById('notificationContent');
|
||||
|
||||
todoBtn.addEventListener('click', () => {
|
||||
todoBtn.classList.remove('bg-white/50', 'text-dark');
|
||||
todoBtn.classList.add('bg-accent', 'text-white');
|
||||
notificationBtn.classList.remove('bg-accent', 'text-white');
|
||||
notificationBtn.classList.add('bg-white/50', 'text-dark', 'hover:bg-accent/30');
|
||||
todoContent.classList.remove('hidden');
|
||||
notificationContent.classList.add('hidden');
|
||||
});
|
||||
|
||||
notificationBtn.addEventListener('click', () => {
|
||||
notificationBtn.classList.remove('bg-white/50', 'text-dark', 'hover:bg-accent/30');
|
||||
notificationBtn.classList.add('bg-accent', 'text-white');
|
||||
todoBtn.classList.remove('bg-accent', 'text-white');
|
||||
todoBtn.classList.add('bg-white/50', 'text-dark');
|
||||
notificationContent.classList.remove('hidden');
|
||||
todoContent.classList.add('hidden');
|
||||
});
|
||||
|
||||
// 快捷登录搜索功能
|
||||
const quickSearch = document.getElementById('quickSearch');
|
||||
const quickLinks = document.querySelectorAll('#quickLinks a');
|
||||
quickSearch.addEventListener('input', (e) => {
|
||||
const searchTerm = e.target.value.toLowerCase();
|
||||
quickLinks.forEach(link => {
|
||||
const text = link.querySelector('span').textContent.toLowerCase();
|
||||
link.style.display = text.includes(searchTerm) ? 'flex' : 'none';
|
||||
});
|
||||
});
|
||||
|
||||
// 更新当前时间
|
||||
function updateCurrentTime() {
|
||||
const now = new Date();
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'long',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
};
|
||||
document.getElementById('currentTime').textContent = now.toLocaleString('zh-CN', options);
|
||||
}
|
||||
|
||||
updateCurrentTime();
|
||||
setInterval(updateCurrentTime, 1000);
|
||||
|
||||
// 服务器详情模态框
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const modal = new bootstrap.Modal(document.getElementById('serverDetailModal'));
|
||||
const detailFrame = document.getElementById('detailFrame');
|
||||
document.getElementById('serverDetailModal').addEventListener('show.bs.modal', function (e) {
|
||||
const hostId = e.relatedTarget.getAttribute('data-hostid');
|
||||
detailFrame.src = `getServerInfo?hostId=${hostId}`;
|
||||
});
|
||||
document.getElementById('serverDetailModal').addEventListener('hide.bs.modal', function () {
|
||||
detailFrame.src = '';
|
||||
});
|
||||
});
|
||||
|
||||
// 服务器详情模态框加载动画
|
||||
const detailModal = document.getElementById('serverDetailModal');
|
||||
const loadingMask = document.getElementById('loadingMask');
|
||||
const detailFrame = document.getElementById('detailFrame');
|
||||
detailModal.addEventListener('show.bs.modal', function (e) {
|
||||
loadingMask.style.display = 'flex';
|
||||
const targetUrl = e.relatedTarget.dataset.url;
|
||||
detailFrame.src = targetUrl;
|
||||
});
|
||||
detailFrame.addEventListener('load', function () {
|
||||
loadingMask.style.display = 'none';
|
||||
});
|
||||
detailModal.addEventListener('hide.bs.modal', function () {
|
||||
detailFrame.src = '';
|
||||
loadingMask.style.display = 'none';
|
||||
});
|
||||
|
||||
// 退出登录功能
|
||||
const logoutLink = document.getElementById('logoutLink');
|
||||
const logoutModal = document.getElementById('logoutModal');
|
||||
const cancelLogout = document.getElementById('cancelLogout');
|
||||
const confirmLogout = document.getElementById('confirmLogout');
|
||||
|
||||
function showModal() {
|
||||
logoutModal.classList.remove('opacity-0', 'pointer-events-none');
|
||||
logoutModal.querySelector('div').classList.remove('scale-95');
|
||||
logoutModal.querySelector('div').classList.add('scale-100');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function hideModal() {
|
||||
logoutModal.classList.add('opacity-0', 'pointer-events-none');
|
||||
logoutModal.querySelector('div').classList.remove('scale-100');
|
||||
logoutModal.querySelector('div').classList.add('scale-95');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
logoutLink.addEventListener('click', showModal);
|
||||
cancelLogout.addEventListener('click', hideModal);
|
||||
logoutModal.addEventListener('click', (e) => {
|
||||
if (e.target === logoutModal) hideModal();
|
||||
});
|
||||
|
||||
// 处理待办任务弹窗功能
|
||||
const handleButtons = document.querySelectorAll('#todoContent button.text-accent');
|
||||
const handleTaskModal = document.getElementById('handleTaskModal');
|
||||
const cancelHandleTask = document.getElementById('cancelHandleTask');
|
||||
const confirmHandleTask = document.getElementById('confirmHandleTask');
|
||||
const taskOpinion = document.getElementById('taskOpinion');
|
||||
let currentTaskId = null;
|
||||
|
||||
handleButtons.forEach(button => {
|
||||
button.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const taskItem = button.closest('.border-l-4');
|
||||
currentTaskId = taskItem.dataset.taskId || 'default-task-id';
|
||||
taskOpinion.value = '';
|
||||
showHandleModal();
|
||||
});
|
||||
});
|
||||
|
||||
function showHandleModal() {
|
||||
handleTaskModal.classList.remove('opacity-0', 'pointer-events-none');
|
||||
handleTaskModal.querySelector('div').classList.remove('scale-95');
|
||||
handleTaskModal.querySelector('div').classList.add('scale-100');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function hideHandleModal() {
|
||||
handleTaskModal.classList.add('opacity-0', 'pointer-events-none');
|
||||
handleTaskModal.querySelector('div').classList.remove('scale-100');
|
||||
handleTaskModal.querySelector('div').classList.add('scale-95');
|
||||
document.body.style.overflow = '';
|
||||
currentTaskId = null;
|
||||
}
|
||||
|
||||
cancelHandleTask.addEventListener('click', hideHandleModal);
|
||||
handleTaskModal.addEventListener('click', (e) => {
|
||||
if (e.target === handleTaskModal) hideHandleModal();
|
||||
});
|
||||
|
||||
// 确认处理(替换alert为弹窗)
|
||||
confirmHandleTask.addEventListener('click', () => {
|
||||
const opinion = taskOpinion.value.trim();
|
||||
if (!opinion) {
|
||||
showToast('请输入处理意见', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('finishTodoSub', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
taskId: currentTaskId,
|
||||
opinion: opinion
|
||||
})
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
showToast('处理成功');
|
||||
hideHandleModal();
|
||||
location.reload();
|
||||
} else {
|
||||
showToast('处理失败,请重试', 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('处理请求失败:', error);
|
||||
showToast('网络错误,处理失败', 'error');
|
||||
});
|
||||
});
|
||||
|
||||
// 确认退出(替换alert为弹窗)
|
||||
confirmLogout.addEventListener('click', () => {
|
||||
fetch('logout', {method: 'POST'})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
sessionStorage.removeItem('token');
|
||||
window.location.href = '/cApi/login';
|
||||
} else {
|
||||
showToast('退出失败,请重试', 'error'); // 替换alert
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('退出请求失败:', error);
|
||||
showToast('网络错误,退出失败', 'error'); // 替换alert
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -38,71 +38,31 @@
|
||||
}
|
||||
|
||||
.bg-glass {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
}
|
||||
|
||||
.bg-glass-hover {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.text-shadow {
|
||||
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.cube {
|
||||
transform-style: preserve-3d;
|
||||
animation: rotate 15s infinite linear;
|
||||
}
|
||||
|
||||
.float {
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse 4s infinite;
|
||||
text-shadow: 0 2px 15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.modal-backdrop {
|
||||
backdrop-filter: blur(5px);
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 动画定义 */
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotateX(0deg) rotateY(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(360deg) rotateY(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
100% {
|
||||
opacity: 0.6;
|
||||
transform: scale(1);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.input-focus-ring {
|
||||
box-shadow: 0 0 0 2px rgba(255, 209, 102, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
/* 动画优化 */
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
@@ -125,12 +85,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.geo-element {
|
||||
position: absolute;
|
||||
border-radius: 8px;
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
backdrop-filter: blur(5px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.error-modal {
|
||||
@@ -140,31 +104,33 @@
|
||||
.error-modal.hide {
|
||||
animation: fadeOut 0.3s ease-in forwards;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
animation: float 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.feature-card:nth-child(2) {
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
.feature-card:nth-child(3) {
|
||||
animation-delay: 2s;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="font-inter min-h-screen flex items-center justify-center p-4 md:p-0 overflow-hidden">
|
||||
<!-- 背景和装饰元素 -->
|
||||
<body class="font-inter min-h-screen flex items-center justify-center p-4 md:p-0 overflow-x-hidden">
|
||||
<!-- 背景优化 - 增加渐变叠加层让背景图更协调 -->
|
||||
<div class="fixed inset-0 bg-gradient-tech z-0"></div>
|
||||
<div class="geo-element w-32 h-32 top-1/4 left-1/6 float" style="animation-delay: 0s;"></div>
|
||||
<div class="geo-element w-16 h-16 bottom-1/3 right-1/5 float" style="animation-delay: 1s;"></div>
|
||||
<div class="geo-element w-24 h-24 top-2/3 left-1/4 float" style="animation-delay: 2s;"></div>
|
||||
<div class="geo-element w-20 h-20 bottom-1/4 right-1/3 float" style="animation-delay: 1.5s;"></div>
|
||||
<div class="fixed inset-0 z-0 opacity-30">
|
||||
<div class="absolute inset-0"
|
||||
style="background-image: radial-gradient(rgba(255,255,255,0.3) 1px, transparent 1px); background-size: 30px 30px;"></div>
|
||||
</div>
|
||||
<div class="fixed top-1/3 right-1/4 w-32 h-32 cube z-0">
|
||||
<div class="absolute inset-0 bg-primary/20 rounded-lg transform rotate-x-0 rotate-y-0"></div>
|
||||
<div class="absolute inset-0 bg-accent/20 rounded-lg transform rotate-x-90 rotate-y-0"></div>
|
||||
<div class="absolute inset-0 bg-secondary/20 rounded-lg transform rotate-x-0 rotate-y-90"></div>
|
||||
<!-- 科技感装饰元素 -->
|
||||
<div class="fixed inset-0 z-0 overflow-hidden opacity-20">
|
||||
<div class="absolute top-1/4 left-1/4 w-96 h-96 rounded-full bg-primary/30 blur-3xl"></div>
|
||||
<div class="absolute bottom-1/4 right-1/4 w-80 h-80 rounded-full bg-accent/20 blur-3xl"></div>
|
||||
</div>
|
||||
|
||||
<!-- 错误提示弹窗 (默认隐藏) -->
|
||||
<div id="errorModal" class="fixed inset-0 z-50 flex items-center justify-center hidden">
|
||||
<!-- 遮罩层 -->
|
||||
<div class="absolute inset-0 bg-darkBlue/70 modal-backdrop" id="modalBackdrop"></div>
|
||||
<!-- 弹窗内容 -->
|
||||
<div class="relative bg-white/10 backdrop-blur-xl border border-white/20 rounded-xl p-6 w-full max-w-md shadow-2xl error-modal">
|
||||
<div class="absolute inset-0 bg-darkBlue/80 modal-backdrop" id="modalBackdrop"></div>
|
||||
<div class="relative bg-white/15 backdrop-blur-xl border border-white/25 rounded-xl p-6 w-full max-w-md shadow-2xl error-modal transform transition-all">
|
||||
<div class="text-center mb-4">
|
||||
<div class="w-16 h-16 mx-auto bg-red-500/20 rounded-full flex items-center justify-center mb-4">
|
||||
<i class="fa fa-exclamation-triangle text-red-400 text-2xl"></i>
|
||||
@@ -173,27 +139,28 @@
|
||||
<p class="text-white/80" id="modalErrorMessage">账号或密码错误,请重新输入</p>
|
||||
</div>
|
||||
<button id="closeModal"
|
||||
class="w-full mt-4 bg-accent hover:bg-accent/90 text-darkBlue font-medium py-2.5 px-4 rounded-lg transition-all duration-300">
|
||||
class="w-full mt-4 bg-accent hover:bg-accent/90 text-darkBlue font-medium py-2.5 px-4 rounded-lg transition-all duration-300 shadow-lg shadow-accent/20 hover:shadow-accent/30">
|
||||
确定
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 登录卡片 -->
|
||||
<div class="relative w-full max-w-5xl bg-white/10 backdrop-blur-xl rounded-2xl shadow-2xl overflow-hidden flex flex-col md:flex-row z-10 border border-white/20">
|
||||
<!-- 左侧品牌区域 -->
|
||||
<div class="p-6 md:p-10 lg:p-12 flex-1 flex flex-col justify-between relative">
|
||||
<div class="absolute -top-10 -left-10 w-40 h-40 bg-accent/10 rounded-full blur-3xl"></div>
|
||||
<div class="absolute -bottom-20 -right-10 w-60 h-60 bg-primary/20 rounded-full blur-3xl"></div>
|
||||
<div class="relative">
|
||||
<p class="text-white/80 text-base md:text-lg max-w-md">
|
||||
一站式智能管理平台,融合前沿技术,为您提供高效、安全、便捷的服务体验
|
||||
<!-- 登录卡片 - 科技感背景图(协调不突兀) -->
|
||||
<div class="relative w-full max-w-5xl bg-white/10 backdrop-blur-xl rounded-2xl shadow-2xl overflow-hidden flex flex-col md:flex-row z-10 border border-white/20"
|
||||
style="background-image: url('images/login-brand.png'); background-size: cover; background-position: center; background-blend-mode: overlay;">
|
||||
<!-- 左侧品牌区域 - 半透明遮罩提升可读性 -->
|
||||
<div class="p-6 md:p-10 lg:p-12 flex-1 flex flex-col justify-between bg-darkBlue/40 backdrop-blur-sm">
|
||||
<div>
|
||||
<h2 class="text-[clamp(1.8rem,4vw,2.5rem)] font-bold text-white text-shadow mb-6">智慧门户</h2>
|
||||
<p class="text-white/90 text-base md:text-lg max-w-md leading-relaxed">
|
||||
一站式智能管理平台,融合前沿技术,为您提供高效、安全、便捷的服务体验,
|
||||
助力数字化转型,创造更大价值
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-10 md:mt-20 space-y-6 relative">
|
||||
<div class="bg-glass p-4 rounded-xl border border-white/10 hover:border-white/30 transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div class="mt-10 md:mt-20 space-y-6">
|
||||
<div class="bg-glass bg-glass-hover p-5 rounded-xl border border-white/10 hover:border-white/30 transition-all duration-300 feature-card">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-12 h-12 rounded-full bg-accent/20 flex items-center justify-center text-accent pulse">
|
||||
<div class="w-12 h-12 rounded-full bg-accent/20 flex items-center justify-center text-accent">
|
||||
<i class="fa fa-shield text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
@@ -202,9 +169,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-glass p-4 rounded-xl border border-white/10 hover:border-white/30 transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div class="bg-glass bg-glass-hover p-5 rounded-xl border border-white/10 hover:border-white/30 transition-all duration-300 feature-card">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-12 h-12 rounded-full bg-secondary/20 flex items-center justify-center text-secondary pulse">
|
||||
<div class="w-12 h-12 rounded-full bg-secondary/20 flex items-center justify-center text-secondary">
|
||||
<i class="fa fa-bolt text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
@@ -213,9 +180,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-glass p-4 rounded-xl border border-white/10 hover:border-white/30 transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div class="bg-glass bg-glass-hover p-5 rounded-xl border border-white/10 hover:border-white/30 transition-all duration-300 feature-card">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-12 h-12 rounded-full bg-primary/20 flex items-center justify-center text-primary pulse">
|
||||
<div class="w-12 h-12 rounded-full bg-primary/20 flex items-center justify-center text-primary">
|
||||
<i class="fa fa-cubes text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
@@ -225,20 +192,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-10 md:mt-16 text-center md:text-left text-white/60 text-sm relative">
|
||||
<p>© 2023 智慧门户 版权所有</p>
|
||||
<div class="mt-10 md:mt-16 text-center md:text-left text-white/60 text-sm">
|
||||
<p>© 2025 智慧门户 版权所有</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧登录表单区域 -->
|
||||
<div class="flex-1 p-6 md:p-10 lg:p-12 bg-white/10 backdrop-blur-xl border-l border-white/20 flex flex-col justify-center">
|
||||
<!-- 右侧登录表单区域 - 增强遮罩确保表单清晰 -->
|
||||
<div class="flex-1 p-6 md:p-10 lg:p-12 bg-darkBlue/60 backdrop-blur-xl border-l border-white/20 flex flex-col justify-center">
|
||||
<div class="max-w-md mx-auto w-full">
|
||||
<div class="text-center md:text-left mb-10 relative">
|
||||
<div class="absolute top-0 right-0 md:right-0">
|
||||
<img th:src="@{/images/login-brand.png}" alt="logo">
|
||||
</div>
|
||||
<h1 class="text-[clamp(2rem,5vw,3rem)] font-bold mb-4 text-white text-shadow">智慧门户</h1>
|
||||
<p class="text-white/70">欢迎登录系统</p>
|
||||
<div class="text-center md:text-left mb-10">
|
||||
<h1 class="text-[clamp(2rem,5vw,3rem)] font-bold mb-4 text-white text-shadow">账户登录</h1>
|
||||
<p class="text-white/70">欢迎回来,请输入您的账号信息</p>
|
||||
</div>
|
||||
|
||||
<form id="loginForm" class="space-y-6">
|
||||
@@ -250,7 +214,7 @@
|
||||
<i class="fa fa-user text-white/50"></i>
|
||||
</div>
|
||||
<input type="text" id="username" name="username"
|
||||
class="block w-full pl-12 pr-4 py-3 bg-white/10 border border-white/20 rounded-lg focus:ring-2 focus:ring-accent/50 focus:border-accent text-white placeholder-white/40 transition-all duration-300 outline-none"
|
||||
class="block w-full pl-12 pr-4 py-3 bg-white/10 border border-white/20 rounded-lg focus:border-accent focus:input-focus-ring text-white placeholder-white/40 transition-all duration-300 outline-none"
|
||||
placeholder="请输入账号" required>
|
||||
</div>
|
||||
</div>
|
||||
@@ -259,14 +223,13 @@
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="password" class="block text-sm font-medium text-white/80">密码</label>
|
||||
<a href="#" class="text-sm text-accent hover:text-accent/80 transition-colors">忘记密码?</a>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
||||
<i class="fa fa-lock text-white/50"></i>
|
||||
</div>
|
||||
<input type="password" id="password" name="password"
|
||||
class="block w-full pl-12 pr-12 py-3 bg-white/10 border border-white/20 rounded-lg focus:ring-2 focus:ring-accent/50 focus:border-accent text-white placeholder-white/40 transition-all duration-300 outline-none"
|
||||
class="block w-full pl-12 pr-12 py-3 bg-white/10 border border-white/20 rounded-lg focus:border-accent focus:input-focus-ring text-white placeholder-white/40 transition-all duration-300 outline-none"
|
||||
placeholder="请输入密码" required>
|
||||
<button type="button" id="togglePassword"
|
||||
class="absolute inset-y-0 right-0 pr-4 flex items-center text-white/50 hover:text-white transition-colors">
|
||||
@@ -286,7 +249,7 @@
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<button type="submit"
|
||||
class="w-full bg-accent hover:bg-accent/90 text-darkBlue font-medium py-3 px-4 rounded-lg transition-all duration-300 transform hover:scale-[1.02] active:scale-[0.98] focus:outline-none focus:ring-2 focus:ring-accent/50 focus:ring-offset-2 focus:ring-offset-white/10">
|
||||
class="w-full bg-accent hover:bg-accent/90 text-darkBlue font-medium py-3 px-4 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-accent/50 focus:ring-offset-2 focus:ring-offset-white/10 shadow-lg shadow-accent/20 hover:shadow-accent/30 transform hover:-translate-y-0.5 active:translate-y-0">
|
||||
登录
|
||||
</button>
|
||||
</form>
|
||||
@@ -295,6 +258,13 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
if (window.self !== window.top) {
|
||||
// 跳转到顶层窗口,强制取消嵌套
|
||||
window.top.location.href = window.self.location.href;
|
||||
}
|
||||
|
||||
|
||||
// 密码显示/隐藏切换
|
||||
const togglePassword = document.getElementById('togglePassword');
|
||||
const password = document.getElementById('password');
|
||||
@@ -302,13 +272,8 @@
|
||||
const type = password.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
password.setAttribute('type', type);
|
||||
const icon = togglePassword.querySelector('i');
|
||||
if (type === 'password') {
|
||||
icon.classList.remove('fa-eye');
|
||||
icon.classList.add('fa-eye-slash');
|
||||
} else {
|
||||
icon.classList.remove('fa-eye-slash');
|
||||
icon.classList.add('fa-eye');
|
||||
}
|
||||
icon.classList.toggle('fa-eye');
|
||||
icon.classList.toggle('fa-eye-slash');
|
||||
});
|
||||
|
||||
// 错误弹窗相关元素
|
||||
@@ -321,25 +286,23 @@
|
||||
function showErrorModal(message = '账号或密码错误,请重新输入') {
|
||||
modalErrorMessage.textContent = message;
|
||||
errorModal.classList.remove('hidden');
|
||||
// 阻止页面滚动
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
// 隐藏错误弹窗
|
||||
function hideErrorModal() {
|
||||
errorModal.querySelector('.error-modal').classList.add('hide');
|
||||
const modal = errorModal.querySelector('.error-modal');
|
||||
modal.classList.add('hide');
|
||||
setTimeout(() => {
|
||||
errorModal.classList.add('hidden');
|
||||
errorModal.querySelector('.error-modal').classList.remove('hide');
|
||||
// 恢复页面滚动
|
||||
modal.classList.remove('hide');
|
||||
document.body.style.overflow = '';
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// 关闭弹窗事件
|
||||
// 关闭弹窗事件绑定
|
||||
closeModal.addEventListener('click', hideErrorModal);
|
||||
modalBackdrop.addEventListener('click', hideErrorModal);
|
||||
// 按ESC键关闭弹窗
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && !errorModal.classList.contains('hidden')) {
|
||||
hideErrorModal();
|
||||
@@ -352,10 +315,16 @@
|
||||
e.preventDefault();
|
||||
|
||||
// 获取表单数据
|
||||
const username = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
const username = document.getElementById('username').value.trim();
|
||||
const password = document.getElementById('password').value.trim();
|
||||
const remember = document.getElementById('remember').checked;
|
||||
|
||||
// 简单表单验证
|
||||
if (!username || !password) {
|
||||
showErrorModal('账号和密码不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
// 模拟登录加载状态
|
||||
const submitButton = loginForm.querySelector('button[type="submit"]');
|
||||
const originalText = submitButton.innerHTML;
|
||||
@@ -363,34 +332,29 @@
|
||||
submitButton.innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i> 登录中...';
|
||||
|
||||
try {
|
||||
// 发送登录请求到后端
|
||||
const response = await fetch('userLogin', { // 替换为实际后端登录接口
|
||||
// 发送登录请求到后端(替换为实际接口地址)
|
||||
const response = await fetch('biz/userLogin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
remember
|
||||
}),
|
||||
body: JSON.stringify({username, password, remember}),
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
console.log(result)
|
||||
if (response.ok && result.success) {
|
||||
// 登录成功,跳转到首页
|
||||
window.location.href = '/index'; // 替换为实际首页路径
|
||||
// 登录成功,跳转到首页(替换为实际首页路径)
|
||||
window.location.href = 'biz/index';
|
||||
} else {
|
||||
// 登录失败,显示错误弹窗
|
||||
const errorMsg = result.message || '账号或密码错误,请重新输入';
|
||||
showErrorModal(errorMsg);
|
||||
// 登录失败,显示错误信息
|
||||
showErrorModal(result.message || '账号或密码错误,请重新输入');
|
||||
}
|
||||
} catch (error) {
|
||||
// 网络错误等异常情况
|
||||
// 网络错误处理
|
||||
console.error('登录请求失败:', error);
|
||||
showErrorModal('网络异常,请稍后重试');
|
||||
showErrorModal('网络异常,请检查网络连接后重试');
|
||||
} finally {
|
||||
// 恢复按钮状态
|
||||
submitButton.disabled = false;
|
||||
@@ -398,11 +362,11 @@
|
||||
}
|
||||
});
|
||||
|
||||
// 输入框交互效果
|
||||
// 输入框交互优化(聚焦时轻微上浮效果)
|
||||
const inputs = document.querySelectorAll('input');
|
||||
inputs.forEach(input => {
|
||||
input.addEventListener('focus', () => {
|
||||
input.parentElement.classList.add('scale-[1.02]');
|
||||
input.parentElement.classList.add('scale-[1.02]', 'transition-transform', 'duration-300');
|
||||
});
|
||||
input.addEventListener('blur', () => {
|
||||
input.parentElement.classList.remove('scale-[1.02]');
|
||||
|
||||
290
src/main/resources/templates/server.html
Normal file
290
src/main/resources/templates/server.html
Normal file
@@ -0,0 +1,290 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>系统监控仪表盘</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
|
||||
|
||||
<!-- 配置Tailwind自定义颜色和工具类 -->
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#e0f2fe',
|
||||
secondary: '#bae6fd',
|
||||
accent: '#38bdf8',
|
||||
dark: '#0f172a',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
|
||||
.card-shadow {
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(148, 163, 184, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-primary h-full overflow-hidden">
|
||||
<div class="flex h-[calc(100vh-15px)] px-4 gap-4">
|
||||
<!-- 左侧系统信息卡片区域 -->
|
||||
<div class="w-1/3 bg-white rounded-lg p-4 card-shadow flex flex-col gap-4 overflow-y-auto scrollbar-thin">
|
||||
<!-- 主机运行时间 -->
|
||||
<div class="p-3 bg-white rounded-lg border border-gray-100 card-shadow">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center">
|
||||
<i class="fa fa-clock-o text-accent text-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm text-gray-500">运行时长</h3>
|
||||
<p class="text-lg font-semibold text-dark" th:text="${info.getUptime()}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 系统信息 -->
|
||||
<div class="p-3 bg-white rounded-lg border border-gray-100 card-shadow">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center">
|
||||
<i class="fa fa-windows text-accent text-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm text-gray-500">操作系统</h3>
|
||||
<p class="text-lg font-semibold text-dark" th:text="${info.getOs()}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内核版本 -->
|
||||
<div class="p-3 bg-white rounded-lg border border-gray-100 card-shadow">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center">
|
||||
<i class="fa fa-cogs text-accent text-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm text-gray-500">内核版本</h3>
|
||||
<p class="text-lg font-semibold text-dark" th:text="${info.getKernelVersion()}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主机名 -->
|
||||
<div class="p-3 bg-white rounded-lg border border-gray-100 card-shadow">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center">
|
||||
<i class="fa fa-desktop text-accent text-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm text-gray-500">主机名称</h3>
|
||||
<p class="text-lg font-semibold text-dark" th:text="${info.getHostname()}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IP地址 -->
|
||||
<div class="p-3 bg-white rounded-lg border border-gray-100 card-shadow">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center">
|
||||
<i class="fa fa-ip-address text-accent text-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm text-gray-500">IP地址</h3>
|
||||
<p class="text-lg font-semibold text-dark" th:text="${info.getIpAddress()}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CPU型号 -->
|
||||
<div class="p-3 bg-white rounded-lg border border-gray-100 card-shadow">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center">
|
||||
<i class="fa fa-microchip text-accent text-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm text-gray-500">CPU型号</h3>
|
||||
<p class="text-lg font-semibold text-dark" th:text="${info.getCpuModel()}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内存总量 -->
|
||||
<div class="p-3 bg-white rounded-lg border border-gray-100 card-shadow">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center">
|
||||
<i class="fa fa-database text-accent text-lg"></i> <!-- 内存总量使用数据库图标,代表存储容量 -->
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm text-gray-500">内存总量</h3>
|
||||
<p class="text-lg font-semibold text-dark" th:text="${info.getMemoryTotal()}"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧区域 -->
|
||||
<div class="w-2/3 bg-white rounded-lg p-4 card-shadow flex flex-col gap-4">
|
||||
<!-- 上方CPU和内存卡片 -->
|
||||
<div class="flex gap-4">
|
||||
<!-- CPU使用率 -->
|
||||
<div class="flex-1 p-3 bg-white rounded-lg border border-gray-100 card-shadow flex flex-col items-center">
|
||||
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center mb-2">
|
||||
<i class="fa fa-microchip text-accent text-lg"></i>
|
||||
</div>
|
||||
<h3 class="text-sm text-gray-500 mb-2">CPU使用率</h3>
|
||||
<div class="relative w-32 h-32">
|
||||
<canvas id="cpuChart"></canvas>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<span class="text-xl font-bold text-dark" data-cpu-usage
|
||||
th:text="${info.getCpuUsage()}"></span>
|
||||
<p>%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内存使用率 -->
|
||||
<div class="flex-1 p-3 bg-white rounded-lg border border-gray-100 card-shadow flex flex-col items-center">
|
||||
<div class="w-10 h-10 rounded-full bg-secondary flex items-center justify-center mb-2">
|
||||
<i class="fa fa-area-chart text-accent text-lg"></i> <!-- 内存使用率使用面积图图标,代表资源监控 -->
|
||||
</div>
|
||||
<h3 class="text-sm text-gray-500 mb-2">内存使用率</h3>
|
||||
<div class="relative w-32 h-32">
|
||||
<canvas id="memoryChart"></canvas>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
<span class="text-xl font-bold text-dark" data-memory-usage
|
||||
th:text="${info.getMemoryUsage()}"></span>
|
||||
<p>%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下方磁盘使用率表格 -->
|
||||
<div class="flex-1 overflow-hidden flex flex-col">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<i class="fa fa-hdd-o text-accent"></i> <!-- 磁盘使用率添加硬盘图标 -->
|
||||
<h3 class="text-sm text-gray-500">磁盘使用率</h3>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto scrollbar-thin">
|
||||
<table class="min-w-full bg-white">
|
||||
<thead class="bg-gray-50 sticky top-0">
|
||||
<tr>
|
||||
<th class="py-2 px-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
设备
|
||||
</th>
|
||||
<th class="py-2 px-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
挂载点
|
||||
</th>
|
||||
<th class="py-2 px-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
总容量
|
||||
</th>
|
||||
<th class="py-2 px-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
已使用
|
||||
</th>
|
||||
<th class="py-2 px-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
使用率
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
<tr th:each=" disk : ${devices}">
|
||||
<td class="py-2 px-3 text-sm text-gray-900" th:text="${disk.getDevice()}"></td>
|
||||
<td class="py-2 px-3 text-sm text-gray-900" th:text="${disk.getMountPoint()}"></td>
|
||||
<td class="py-2 px-3 text-sm text-gray-900" th:text="${disk.getTotalSize()}"></td>
|
||||
<td class="py-2 px-3 text-sm text-gray-900" th:text="${disk.getUsedSize()}"></td>
|
||||
<td class="py-2 px-3">
|
||||
<div class="flex items-center">
|
||||
<div class="w-full bg-gray-200 rounded-full h-2.5">
|
||||
<div class="bg-accent h-2.5 rounded-full"
|
||||
th:style="'width: ' + ${disk.getUsageRate()} + '%;'"></div>
|
||||
</div>
|
||||
<span class="ml-2 text-sm font-medium text-gray-700"
|
||||
th:text="${disk.getUsageRate()}"></span>
|
||||
<p>%</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 配置环形进度图的通用选项
|
||||
const getChartOptions = (value) => {
|
||||
let color;
|
||||
if (value < 60) {
|
||||
color = '#38bdf8'; // 蓝色 - 正常
|
||||
} else if (value < 80) {
|
||||
color = '#eab308'; // 黄色 - 警告
|
||||
} else {
|
||||
color = '#ef4444'; // 红色 - 危险
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [value, 100 - value],
|
||||
backgroundColor: [color, '#f1f5f9'],
|
||||
borderWidth: 0,
|
||||
cutout: '80%'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
plugins: {
|
||||
legend: {display: false},
|
||||
tooltip: {enabled: false}
|
||||
},
|
||||
animation: {
|
||||
animateRotate: true,
|
||||
animateScale: true
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 初始化CPU图表
|
||||
const cpuUsageElement = document.querySelector('.text-xl.font-bold.text-dark[data-cpu-usage]');
|
||||
const cpuUsageValue = parseFloat(cpuUsageElement.textContent.replace('%', ''));
|
||||
const cpuCtx = document.getElementById('cpuChart').getContext('2d');
|
||||
new Chart(cpuCtx, getChartOptions(cpuUsageValue));
|
||||
|
||||
// 初始化内存图表
|
||||
const memoryUsageElement = document.querySelector('.text-xl.font-bold.text-dark[data-memory-usage]');
|
||||
const memoryUsageValue = parseFloat(memoryUsageElement.textContent.replace('%', ''));
|
||||
const memoryCtx = document.getElementById('memoryChart').getContext('2d');
|
||||
new Chart(memoryCtx, getChartOptions(memoryUsageValue));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user