Compare commits

40 Commits

Author SHA1 Message Date
534d7c4d01 xxx 2025-12-01 18:42:37 +08:00
2ae751bc4c 保存进度! 2025-12-01 12:26:55 +08:00
cdc0f5b51b 保存进度 2025-11-30 23:32:12 +08:00
8e97044faa 保存进度 2025-11-30 14:59:27 +08:00
835e812d4e 保存进度! 2025-11-27 18:18:22 +08:00
bf6e78b423 保存进度 2025-11-26 09:11:13 +08:00
88ba348242 保存进度 2025-11-24 21:43:08 +08:00
51cf0da474 保存进度 2025-11-24 21:40:18 +08:00
de68525230 保存进度 2025-11-24 21:33:33 +08:00
207e5aeb0c 保存进度 2025-11-24 21:30:20 +08:00
91adce0fef 保存进度 2025-11-23 18:06:00 +08:00
1e20eeb0d1 保存进度! 2025-11-21 18:01:17 +08:00
d22b29f45d baocun 2025-11-20 00:21:22 +08:00
c9c2493671 保存进度 2025-11-19 01:13:28 +08:00
e73ce4b4b0 保存进度! 2025-11-18 19:37:20 +08:00
9e1fdcdd6a 保存进度! 2025-11-18 15:30:57 +08:00
de0b396bcb Merge branch 'feature-制度管理界面' of http://222.76.244.118:3000/CPICXIM/regulatory-management-system into feature-制度管理界面 2025-11-17 19:01:01 +08:00
5d7aee0e7c 保存进度! 2025-11-17 19:00:39 +08:00
f2bc8d14ab 保存进度 2025-11-15 22:54:57 +08:00
1b40137cf9 修改接受文件的控制器 2025-11-13 15:48:36 +08:00
524ea68d89 保存进度! 2025-11-12 20:13:56 +08:00
32b40f700d 11 2025-11-07 18:11:49 +08:00
b04152c5c5 。。。 2025-11-05 18:02:00 +08:00
af3442f276 保存进度! 2025-10-31 19:08:12 +08:00
390a5efbb8 编写上传界面 2025-10-30 18:19:51 +08:00
72a4895328 保存进度! 2025-10-30 16:02:54 +08:00
ef3b31c9e4 编写制度管理。 2025-10-28 23:51:14 +08:00
a11c6ada3d 编写制度文件管理view 2025-10-24 18:07:49 +08:00
ad3320779c 保存进度! 2025-10-24 11:45:27 +08:00
96efb0cf53 保存进度! 2025-10-24 11:45:10 +08:00
44f26dd330 保存进度 2025-10-24 00:36:24 +08:00
7ea8e7ab4d 编写路由代码。 2025-10-23 17:52:01 +08:00
fd4275c3a5 保存进度! 2025-10-22 18:17:36 +08:00
51f96ed6c1 保存进度! 2025-10-17 18:16:51 +08:00
8cc2f8759d 保存进度! 2025-10-16 18:16:46 +08:00
4a79a080c5 更新 README.md 2025-10-16 07:44:55 +00:00
9699e39ecf 保存进度! 2025-10-16 15:43:26 +08:00
c22779bd6a 保存进度! 2025-10-16 15:42:00 +08:00
07bf0e3450 保存进度! 2025-10-16 15:41:29 +08:00
bb25a5db61 保存进度! 2025-10-16 15:40:25 +08:00
67 changed files with 4097 additions and 557 deletions

0
7788.md Normal file
View File

View File

@@ -1,3 +1,9 @@
# regulatory-management-system
<h1 style="text-align:center; font-size:2rem;" title>产险厦门分公司规章制度管理工具</h1>
产险厦门分公司规章制度管理工具。
# 概述
产险厦门分公司规章制度管理工具。
# 密码
## mysql
root@localhost ^QaKwfmo#HNy&0D7

View File

@@ -0,0 +1,178 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-10-16 09:46:42
* @LastEditors: Kane Wang
* @LastModified: 2025-12-01 18:42:31
* @FilePath: src/main/java/com/cpic/xim/utils/files/SaveUploadFile.java
* @Description:
*
* Copyright (c) 2025 by Kane All rights reserved
*/
package com.cpic.xim.utils.files;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Vector;
import org.springframework.web.multipart.MultipartFile;
import com.cpic.xim.utils.files.exceptions.MoveUploadedFileException;
import com.cpic.xim.utils.files.exceptions.ProcessUploadedFileException;
public class SaveUploadFile
{
/**
*
* @param files
* @param tempFilePath
* @return
* @throws ProcessUploadedFileException
*/
public static HashMap<String, String> saveUploadFile(
MultipartFile[] files,
String tempFilePath
)
throws ProcessUploadedFileException
{
HashMap<String, String> savedFiles = new HashMap<>();
// File dir = new File( tempFilePath );
String fileName = "";
String fullPath;
if ( !(tempFilePath.endsWith( "/" ) || tempFilePath.endsWith( "\\" )) )
{
fullPath = tempFilePath + "/";
}
else
{
fullPath = tempFilePath;
}
try
{
for ( MultipartFile file : files )
{
// 空文件跳过
if ( file.isEmpty() )
{
continue;
}
// 文件名前加上时间戳,避免覆盖
Long milliSecond = LocalDateTime.now().toInstant( ZoneOffset.of( "+8" ) ).toEpochMilli();
fileName = String.valueOf( milliSecond ) + " - " + file.getOriginalFilename();
File destFile = new File( tempFilePath, fileName );
file.transferTo( destFile );
savedFiles.put( fileName, fullPath + fileName );
}
}
catch ( IOException error )
{
throw new ProcessUploadedFileException( "临时目录" + tempFilePath + "保存文件" + fileName + "失败!" );
}
return savedFiles;
}
/**
* 将files参数中上传的文件保存到tempFilePath指定的路径文件名加上毫秒数避免重复。
*
* @param files
* @param absoluteFilePath
* @return 返回UploadedFile对象数组
* @throws ProcessUploadedFileException
*/
public static Vector<UploadedFile> saveUploadFiles(
MultipartFile[] files,
String absoluteFilePath,
String relativeFilePath
)
throws ProcessUploadedFileException
{
Vector<UploadedFile> savedFiles = new Vector<>();
UploadedFile uploadedFile = null;
// File dir = new File( tempFilePath );
String fileName = "";
String fullPath = null;
if ( !(absoluteFilePath.endsWith( "/" ) || absoluteFilePath.endsWith( "\\" )) )
{
fullPath = absoluteFilePath + "/";
}
else
{
fullPath = absoluteFilePath;
}
try
{
for ( MultipartFile file : files )
{
// 空文件跳过
if ( file.isEmpty() )
{
continue;
}
// 文件名前加上时间戳,避免覆盖
Long milliSecond = LocalDateTime.now().toInstant( ZoneOffset.of( "+8" ) ).toEpochMilli();
fileName = String.valueOf( milliSecond ) + "-" + file.getOriginalFilename();
File destFile = new File( fullPath, fileName );
file.transferTo( destFile );
uploadedFile = new UploadedFile( fileName, fullPath, relativeFilePath );
savedFiles.add( uploadedFile );
}
}
catch ( IOException error )
{
throw new ProcessUploadedFileException( "临时目录" + absoluteFilePath + "保存文件" + fileName + "失败!" );
}
return savedFiles;
}
/**
*
* @param originFilePath 文件路径
* @param newFilePath 新文件存放路径
* @throws MoveUploadedFileException
*/
public static void MoveUploadedFile(
String originFilePath,
String newFilePath
)
throws MoveUploadedFileException
{
// 防御性验证
File originFile = new File( originFilePath );
File dest = new File( newFilePath );
// 文件如果不存在就抛出异常
if ( originFile.exists() == false )
{
throw new MoveUploadedFileException( "文件不存在!" );
}
// 目的路径有同名文件
if ( dest.exists() == true )
{
throw new MoveUploadedFileException( "目的路径已存在同名文件!" );
}
// 不知道具体原因
if ( originFile.renameTo( dest ) == false )
{
throw new MoveUploadedFileException( "移动文件失败!" );
}
}
}

View File

@@ -0,0 +1,73 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-10-31 17:33:13
* @LastEditors: Kane Wang
* @LastModified: 2025-11-27 11:43:18
* @FilePath: src/main/java/com/cpic/xim/utils/files/UploadedFile.java
* @Description:
*
* Copyright (c) 2025 by Kane All rights reserved
*/
package com.cpic.xim.utils.files;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* 描述上传文件的类。
*
*/
public class UploadedFile
{
@JsonProperty( "fileName" )
private String fileName;
@JsonProperty( "absoluteFilePath" )
private String absoluteFilePath;
@JsonProperty( "relativeFilePath" )
private String relativeFilePath;
public String getFileName()
{
return fileName;
}
public void setFileName( String fileName )
{
this.fileName = fileName;
}
public String getAbsoluteFilePath()
{
return absoluteFilePath;
}
public void setAbsoluteFilePath( String absoluteFilePath )
{
this.absoluteFilePath = absoluteFilePath;
}
public String getRelativeFilePath()
{
return relativeFilePath;
}
public void setRelativeFilePath( String relativeFilePath )
{
this.relativeFilePath = relativeFilePath;
}
public UploadedFile( String fileName, String absoluteFilePath, String relativeFilePath )
{
this.fileName = fileName;
this.absoluteFilePath = absoluteFilePath;
this.relativeFilePath = relativeFilePath;
}
public UploadedFile( String fileName, String absoluteFilePath )
{
this.fileName = fileName;
this.absoluteFilePath = absoluteFilePath;
this.relativeFilePath = "";
}
}

View File

@@ -0,0 +1,26 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-11-13 14:44:42
* @LastEditors: Kane Wang
* @LastModified: 2025-11-13 14:45:56
* @FilePath: src/main/java/com/cpic/xim/utils/files/exceptions/MoveUploadedFileException.java
* @Description:
*
* Copyright (c) 2025 by Kane All rights reserved
*/
package com.cpic.xim.utils.files.exceptions;
import java.io.IOException;
public class MoveUploadedFileException extends IOException
{
public MoveUploadedFileException()
{
super("移动文件失败。");
}
public MoveUploadedFileException( String message )
{
super( message );
}
}

View File

@@ -2,24 +2,24 @@
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-10-15 12:06:26
* @LastEditors: Kane Wang
* @LastModified: 2025-10-16 09:59:39
* @FilePath: src/main/java/com/cpic/xim/web/controllers/fileupload/ProcessUploadedFileException.java
* @LastModified: 2025-11-13 10:36:12
* @FilePath: src/main/java/com/cpic/xim/utils/files/exceptions/ProcessUploadedFileException.java
* @Description:
*
* Copyright (c) 2025 by Kane All rights reserved
*/
package com.cpic.xim.web.controllers.fileupload;
package com.cpic.xim.utils.files.exceptions;
import java.io.IOException;
public class ProcessUploadedFileException extends IOException
{
ProcessUploadedFileException()
public ProcessUploadedFileException()
{
super("上传文件失败!");
}
ProcessUploadedFileException( String message )
public ProcessUploadedFileException( String message )
{
super(message);
}

View File

@@ -1,145 +0,0 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-10-15 11:42:56
* @LastEditors: Kane Wang
* @LastModified: 2025-10-15 17:48:52
* @FilePath: src/main/java/com/cpic/xim/web/controllers/fileupload/FileUpload.java
* @Description: 用于接受上传文件的Controller。
*
* Copyright (c) 2025 by Kane All rights reserved
*/
package com.cpic.xim.web.controllers.fileupload;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Vector;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.bind.annotation.RequestMethod;
// @SuppressWarnings( "unused" )
@Controller
@RequestMapping( path = "/file" )
public class FileUpload
{
/**
* 接收上传文件,并保存到临时目录:
* 1、临时目录下再用sessionID作为子目录保存文件。
* 2、保存时不更改文件名会覆盖同名文件。
* 3、MultipartFile参数形参名称必须和请求form中file标签的name属性一致否则值为null。
* 4、返回值为接收结果和文件保存绝对路径。
*
* @param taskName 任务名称字符串
* @param files MultipartFile结构的文件对象
* @param request HttpServletRequest对象实例
* @return 返回一个FileUploadResult对象包含上传结果。
*/
// @RequestMapping( path = "/file-upload.do" )
// @ResponseBody
public FileUploadResult getUploadFile(
@RequestParam( "task-name" ) String taskName,
@RequestParam( "files" ) MultipartFile file,
HttpServletRequest request
)
{
// session id用来创建临时目录避免重复
String sessionID = request.getSession().getId();
FileUploadResult result = new FileUploadResult();
Vector<String> fileNames = new Vector<String>();
result.setSuccess( true );
result.setMessage( "上传成功!" );
String filePath = request.getServletContext().getRealPath( "/temp/upload/" + sessionID );
File dir = new File( filePath );
if ( !dir.mkdirs() )
{
}
// 检查文件长度如果为0则跳过
if ( file.isEmpty() )
{
result.setSuccess( false );
result.setMessage( "不允许上传空文件。" );
}
else
{
// 保存文件到临时目录
Long milliSecond = LocalDateTime.now().toInstant( ZoneOffset.of( "+8" ) ).toEpochMilli();
String fileName = String.valueOf( milliSecond ) + file.getOriginalFilename();
File destFile = new File( filePath, fileName );
// String fileName = file.getOriginalFilename();
try
{
file.transferTo( destFile );
// 把上传文件的绝对路径保存,返回给前端
fileNames.add( destFile.getAbsolutePath() );
result.setSuccess( true );
result.setMessage( "上传成功" );
result.setFileList( fileNames );
}
catch ( IOException error )
{
result.setSuccess( false );
result.setMessage( "上传失败,原因:" + error.getMessage() );
}
}
return result;
}
@RequestMapping( path = "/file-upload.do" )
@ResponseBody
public FileUploadResult saveUploadFile(
@RequestParam( "file-name" ) String fileName,
@RequestParam( "files" ) MultipartFile file,
HttpServletRequest request
)
{
// session id用来创建临时目录避免重复
String sessionID = request.getSession().getId();
FileUploadResult result = new FileUploadResult();
MultipartFile[] files = new MultipartFile[1];
result.setSuccess( true );
result.setMessage( "上传成功!" );
String filePath = request.getServletContext().getRealPath( "/temp/upload/" + sessionID );
File dir = new File( filePath );
// 创建临时目录
if ( (!dir.exists()) && (!dir.mkdirs()) )
{
result.setSuccess( false );
result.setMessage( "创建临时目录失败:" + filePath );
return result;
}
try
{
files[0] = file;
SaveUploadFile.saveUploadFile( files, filePath );
}
catch ( ProcessUploadedFileException error )
{
result.setSuccess( false );
result.setMessage( error.getMessage() );
}
return result;
}
}

View File

@@ -0,0 +1,51 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-11-17 17:49:58
* @LastEditors: Kane Wang
* @LastModified: 2025-11-18 15:19:46
* @FilePath: src/main/java/com/cpic/xim/web/controllers/fileupload/MoveFileRequest.java
* @Description:
*
* Copyright (c) 2025 by Kane All rights reserved
*/
package com.cpic.xim.web.controllers.fileupload;
import com.fasterxml.jackson.annotation.JsonProperty;
public class MoveFileRequest
{
@JsonProperty("originFilePath")
private String originFilePath;
@JsonProperty("destFilePath")
private String destFilePath;
public MoveFileRequest()
{}
public MoveFileRequest( String originFilePath, String destFilePath )
{
this.originFilePath = originFilePath;
this.destFilePath = destFilePath;
}
public String getOriginFilePath()
{
return originFilePath;
}
public void setOriginFilePath( String originFilePath )
{
this.originFilePath = originFilePath;
}
public String getDestFilePath()
{
return destFilePath;
}
public void setDestFilePath( String destFilePath )
{
this.destFilePath = destFilePath;
}
}

View File

@@ -0,0 +1,48 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-11-17 17:31:15
* @LastEditors: Kane Wang
* @LastModified: 2025-11-17 17:31:16
* @FilePath: src/main/java/com/cpic/xim/web/controllers/fileupload/MoveFileResult.java
* @Description:
*
* Copyright (c) 2025 by Kane All rights reserved
*/
package com.cpic.xim.web.controllers.fileupload;
import com.cpic.xim.web.controllers.QueryResponse;
import com.fasterxml.jackson.annotation.JsonProperty;
public class MoveFileResponse extends QueryResponse
{
@JsonProperty("new-file-path")
private String newFilePath;
public MoveFileResponse()
{
super();
}
public MoveFileResponse(
boolean success,
String message,
String newFilePath
)
{
super( success, message );
this.newFilePath = newFilePath;
}
public String getNewFilePath()
{
return newFilePath;
}
public void setNewFilePath( String newFilePath )
{
this.newFilePath = newFilePath;
}
}

View File

@@ -1,71 +0,0 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-10-16 09:46:42
* @LastEditors: Kane Wang
* @LastModified: 2025-10-16 15:16:57
* @FilePath: src/main/java/com/cpic/xim/web/controllers/fileupload/SaveUploadFile.java
* @Description:
*
* Copyright (c) 2025 by Kane All rights reserved
*/
package com.cpic.xim.web.controllers.fileupload;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import org.springframework.web.multipart.MultipartFile;
public class SaveUploadFile
{
public static HashMap<String, String> saveUploadFile(
MultipartFile[] files,
String tempFilePath
)
throws ProcessUploadedFileException
{
HashMap<String, String> savedFiles = new HashMap<>();
// File dir = new File( tempFilePath );
String fileName = "";
String fullPath;
if ( !(tempFilePath.endsWith( "/" ) || tempFilePath.endsWith( "\\" )) )
{
fullPath = tempFilePath + "/";
}
else
{
fullPath = tempFilePath;
}
try
{
for ( MultipartFile file : files )
{
// 空文件跳过
if ( file.isEmpty() )
{
continue;
}
// 文件名前加上时间戳,避免覆盖
Long milliSecond = LocalDateTime.now().toInstant( ZoneOffset.of( "+8" ) ).toEpochMilli();
fileName = String.valueOf( milliSecond ) + " - " + file.getOriginalFilename();
File destFile = new File( tempFilePath, fileName );
file.transferTo( destFile );
savedFiles.put( fileName, fullPath + fileName );
}
}
catch ( IOException error )
{
throw new ProcessUploadedFileException( "临时目录" + tempFilePath + "保存文件" + fileName + "失败!" );
}
return savedFiles;
}
}

View File

@@ -0,0 +1,118 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-10-15 11:42:56
* @LastEditors: Kane Wang
* @LastModified: 2025-10-15 17:48:52
* @FilePath: src/main/java/com/cpic/xim/web/controllers/fileupload/FileUpload.java
* @Description: 用于接受上传文件的Controller。
*
* Copyright (c) 2025 by Kane All rights reserved
*/
package com.cpic.xim.web.controllers.fileupload;
import java.io.File;
import java.util.Vector;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.cpic.xim.utils.files.SaveUploadFile;
import com.cpic.xim.utils.files.UploadedFile;
import com.cpic.xim.utils.files.exceptions.MoveUploadedFileException;
import com.cpic.xim.utils.files.exceptions.ProcessUploadedFileException;
import jakarta.servlet.http.HttpServletRequest;
// @SuppressWarnings( "unused" )
@Controller
@RequestMapping( path = "/file" )
public class UploadFileController
{
/**
* 接收上传文件,并保存到临时目录:
* 1、临时目录下再用sessionID作为子目录保存文件。
* 2、保存时不更改文件名会覆盖同名文件。
* 3、MultipartFile参数形参名称必须和请求form中file标签的name属性一致否则值为null。
* 4、返回值为接收结果和文件保存绝对路径。
*
* @param fileName
* @param file
* @param request
* @return FileUploadResult 文件上传结果对象
*/
@RequestMapping( path = "/file-upload.do" )
@ResponseBody
public UploadFileResponse saveUploadFile(
@RequestParam( "fileName" ) String fileName,
@RequestParam( "files" ) MultipartFile file,
HttpServletRequest request
)
{
// session id用来创建临时目录避免重复
String sessionID = request.getSession().getId();
UploadFileResponse result = new UploadFileResponse();
MultipartFile[] files = new MultipartFile[1];
result.setSuccess( true );
result.setMessage( "上传成功!" );
String relativeFilePath = "/temp/upload/" + sessionID;
String absolutefilePath = request.getServletContext().getRealPath( relativeFilePath );
File dir = new File( absolutefilePath );
// 创建临时目录
if ( (!dir.exists()) && (!dir.mkdirs()) )
{
result.setSuccess( false );
result.setMessage( "创建临时目录失败:" + absolutefilePath );
return result;
}
try
{
files[0] = file;
Vector<UploadedFile> uploadFiles = SaveUploadFile.saveUploadFiles( files,
absolutefilePath,
relativeFilePath );
result.setFileList( uploadFiles );
}
catch ( ProcessUploadedFileException error )
{
result.setSuccess( false );
result.setMessage( error.getMessage() );
}
return result;
}
@RequestMapping( path = "/move-file.do" )
@ResponseBody
public MoveFileResponse moveFile( @RequestBody MoveFileRequest request )
{
MoveFileResponse response = new MoveFileResponse( false, null, null );
try
{
SaveUploadFile.MoveUploadedFile( request.getOriginFilePath(), request.getDestFilePath() );
response.setSuccess( true );
response.setMessage( "移动文件路径完成!" );
response.setNewFilePath( request.getDestFilePath() );
}
catch ( MoveUploadedFileException error )
{
response.setSuccess( false );
response.setMessage( error.getMessage() );
response.setNewFilePath( request.getDestFilePath() );
}
return response;
}
}

View File

@@ -17,13 +17,15 @@
package com.cpic.xim.web.controllers.fileupload;
import java.util.Vector;
import com.cpic.xim.utils.files.UploadedFile;
import com.cpic.xim.web.controllers.QueryResponse;
import com.fasterxml.jackson.annotation.JsonProperty;
@SuppressWarnings( "unused" )
public class FileUploadResult extends QueryResponse
public class UploadFileResponse extends QueryResponse
{
public FileUploadResult()
public UploadFileResponse()
{
super();
}
@@ -35,10 +37,10 @@ public class FileUploadResult extends QueryResponse
* @param message 消息字符串
* @param fileList 文件绝对路径字符串数组
*/
public FileUploadResult(
public UploadFileResponse(
boolean success,
String message,
Vector<String> fileList
Vector<UploadedFile> fileList
)
{
super( success, message );
@@ -46,17 +48,17 @@ public class FileUploadResult extends QueryResponse
this.fileList = fileList;
}
public Vector<String> getFileList()
public Vector<UploadedFile> getFileList()
{
return fileList;
}
public void setFileList( Vector<String> fileList )
public void setFileList( Vector<UploadedFile> fileList )
{
this.fileList = fileList;
}
@JsonProperty( "fileList" )
private Vector<String> fileList;
private Vector<UploadedFile> fileList;
}

4
code/db/操作.txt Normal file
View File

@@ -0,0 +1,4 @@
alter user 'root'@localhost identified by '^QaKwfmo#HNy&0D7';
create user 'zhiduguanli'@'%' identified by 'Kane@1981';
regulatory_management.*
grant all privileges on regulatory_management.* to 'zhiduguanli'@'%';

156
code/tomcat/server.xml Normal file
View File

@@ -0,0 +1,156 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Note: A "Server" is not itself a "Container", so you may not
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
<!-- OpenSSL support using Tomcat Native -->
<Listener className="org.apache.catalina.core.AprLifecycleListener" />
<!-- OpenSSL support using FFM API from Java 22 -->
<!-- <Listener className="org.apache.catalina.core.OpenSSLLifecycleListener" /> -->
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->
<Service name="Catalina">
<!--The
connectors can use a shared executor, you can define one or more named thread pools-->
<!--
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="4"/>
-->
<!-- A "Connector" represents an endpoint by which requests are received
and responses are returned. Documentation at :
HTTP Connector: /docs/config/http.html
AJP Connector: /docs/config/ajp.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
-->
<!-- Define an SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
This connector uses the NIO implementation. The default
SSLImplementation will depend on the presence of the APR/native
library and the useOpenSSL attribute of the AprLifecycleListener.
Either JSSE or OpenSSL style configuration may be used regardless of
the SSLImplementation selected. JSSE style configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="150" SSLEnabled="true">
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig>
<Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
certificateKeystorePassword="changeit" type="RSA" />
</SSLHostConfig>
</Connector>
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
<!--
<Connector protocol="AJP/1.3"
address="::1"
port="8009"
redirectPort="8443" />
-->
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host).
Documentation at /docs/config/engine.html -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
<Engine name="Catalina" defaultHost="localhost">
<!--For
clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
/docs/config/cluster.html (reference documentation) -->
<!--
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->
<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->
<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase" />
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path="/regulatory" docBase="D:/数据/制度库/文件" debug="0" privileged="true" />
<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->
<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>
</Engine>
</Service>
</Server>

View File

@@ -13,4 +13,9 @@ VITE_APP_MOCK_DATA=true
VITE_APP_CORS_ORIGIN=http://localhost:3000
VITE_APP_TIMEOUT=5000
VITE_APP_RETRY_ATTEMPTS=3
VITE_APP_CACHE_ENABLED=true
VITE_APP_CACHE_ENABLED=true
#上传文件
VITE_APP_URL_UPLOAD_FILE=http://222.76.244.118:8081/RegulatoryManagementBackend/file/file-upload.do
VITE_APP_URL_MOVE_FILE=http://222.76.244.118:8081/RegulatoryManagementBackend/file/move-file.do
VITE_APP_URL_PREFIX=http://222.76.244.118:8081/RegulatoryManagementBackend/

View File

@@ -21,7 +21,7 @@ module.exports = {
},
extends:["eslint:recommended",],
rules:{
indent: ["warn", 4,],
// indent: ["warn", 4,],
// 圆括号中的空格,为空不加空格,紧跟花括号、方括号、圆括号时也不加入空格
"space-in-parens": ["error", "always", { exceptions: ["{}", "[]", "()", "empty",], },],
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
@@ -81,7 +81,7 @@ module.exports = {
"plugin:@typescript-eslint/recommended",
],
rules: {
indent: ["warn", 4,],
// indent: ["warn", 4,],
"no-trailing-spaces": ["error", {"ignoreComments": true,},],
// 圆括号中的空格,为空不加空格,紧跟花括号、方括号、圆括号时也不加入空格
"space-in-parens": ["error", "always", { exceptions: ["{}", "[]", "()", "empty",], },],
@@ -121,9 +121,9 @@ module.exports = {
},],
// typescript
// "@typescript-eslint/indent": ["warn", 4,],
"@stylistic/indent": ["warn", 4,],
"@stylistic/indent": ["warn", 4, { "SwitchCase": 1, },],
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unsafe-argument": "warn",
// "@typescript-eslint/no-unsafe-argument": "warn",
"@typescript-eslint/no-extra-semi": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-unused-vars": "warn",
@@ -171,9 +171,9 @@ module.exports = {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unsafe-argument": "warn",
// "@typescript-eslint/no-unsafe-argument": "warn",
// "@typescript-eslint/indent": ["error", 4,],
"@stylistic/indent": ["warn", 4,],
"@stylistic/indent": ["warn", 4, { "SwitchCase": 1, },],
"@typescript-eslint/no-extra-semi": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-unused-vars": "warn",

File diff suppressed because it is too large Load Diff

View File

@@ -9,21 +9,32 @@
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.22",
"vue-router": "^4.5.1"
"scss": "^0.2.4",
"vue": "^3.5.24",
"vue-router": "^4.6.3"
},
"devDependencies": {
"@stylistic/eslint-plugin": "^5.4.0",
"@typescript-eslint/eslint-plugin": "^8.46.0",
"@typescript-eslint/parser": "^8.46.0",
"@vitejs/plugin-vue": "^6.0.1",
"@element-plus/icons-vue": "^2.3.2",
"@stylistic/eslint-plugin": "^5.6.1",
"@types/node": "^24.10.1",
"@typescript-eslint/eslint-plugin": "^8.47.0",
"@typescript-eslint/parser": "^8.47.0",
"@vitejs/plugin-vue": "^6.0.2",
"@vue-office/docx": "^1.6.3",
"@vue-office/excel": "^1.7.14",
"@vue-office/pdf": "^2.0.10",
"@vue/tsconfig": "^0.8.1",
"element-plus": "^2.11.4",
"eslint": "^9.37.0",
"eslint-plugin-vue": "^10.5.0",
"axios": "^1.13.2",
"element-plus": "^2.11.8",
"eslint": "^9.39.1",
"eslint-plugin-vue": "^10.6.0",
"path": "^0.12.7",
"sass": "^1.94.2",
"typescript": "~5.9.3",
"vite": "^7.1.9",
"vite": "^7.2.4",
"vue-demi": "^0.14.10",
"vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.1.1"
"vue-pdf-embed": "^2.1.3",
"vue-tsc": "^3.1.5"
}
}

View File

@@ -0,0 +1,17 @@
/*
* @Author: Kane
* @Date: 2023-03-04 17:23:02
* @LastEditors: Kane
* @FilePath: /task_schedule/shims-vue.d.ts
* @Description:
*
* Copyright (c) ${2022} by Kane, All Rights Reserved.
*/
declare module "*.vue"
{
import { type ComponentOptions } from "vue";
const componentOptions: ComponentOptions;
// export default componentOptions;
}
// declare module "./src/router/index.js";

View File

@@ -0,0 +1,14 @@
/*
* @Author: Kane
* @Date: 2023-03-03 09:56:05
* @LastEditors: Kane
* @FilePath: /task_schedule/src/assets/css/index.scss
* @Description: 全局css的入口文件
*
* Copyright (c) ${2022} by Kane, All Rights Reserved.
*/
@import url("./public/reset.scss");
@import url("./public/normalize.scss");
@import url("./public/mixin.scss");
@import url("../font/fonts.css");
// @import url("./public/color.scss");

View File

@@ -0,0 +1,56 @@
/*
* @Author: Kane
* @Date: 2023-02-24 09:36:08
* @LastEditors: Kane
* @LastEditTime: 2023-06-15 15:50:00
* @FilePath: /task_schedule/src/assets/css/public/_public.scss
* @Description: 公共变量
*
* Copyright (c) ${2022} by Kane, All Rights Reserved.
*/
//背景色
$wrap-bg-color:#fff;
$span-font-color:#5f5f5f;
//阴影
$box-shadow:0px 0px 20px -10px rgb(14 18 22 / 25%);
$box-shadow-hover:0px 0px 20px -10px rgb(14 18 22 / 50%);
//文字大小
$font-size-normal:14px;
//查询框
@mixin query-box-wrap {
padding: 0px 10px;
span {
font-weight: normal;
display: block;
text-align: right;
font-size: $font-size-normal;
color: $span-font-color;
}
:deep(.el-row) {
display: flex;
align-items: center;
justify-content: left;
}
.el-row+.el-row {
margin-top: 10px;
}
.button-wrapper-right {
display: flex;
justify-content: right;
align-items: center;
}
.button-wrapper-left {
display: flex;
justify-content: left;
align-items: center;
}
}

View File

@@ -0,0 +1,21 @@
/*
* @Author: Kane
* @Date: 2023-02-28 19:25:30
* @LastEditors: Kane
* @FilePath: /task_schedule/src/assets/css/public/global.scss
* @Description:
*
* Copyright (c) ${2022} by Kane, All Rights Reserved.
*/
@mixin no-select {
-webkit-touch-callout: none;
-moz-user-select: none;
/*火狐*/
-webkit-user-select: none;
/*webkit浏览器*/
-ms-user-select: none;
/*IE10*/
-khtml-user-select: none;
/*早期浏览器*/
user-select: none;
}

View File

@@ -0,0 +1,389 @@
/*
* @Author: Kane
* @Date: 2023-03-03 10:17:59
* @LastEditors: Kane
* @FilePath: /task_schedule/src/assets/css/public/normalize.scss
* @Description:
*
* Copyright (c) ${2022} by Kane, All Rights Reserved.
*/
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15;
/* 1 */
-webkit-text-size-adjust: 100%;
/* 2 */
text-size-adjust: 100%;
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box;
/* 1 */
height: 0;
/* 1 */
overflow: visible;
/* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none;
/* 1 */
text-decoration: underline;
/* 2 */
text-decoration: underline dotted;
/* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace;
/* 1 */
font-size: 1em;
/* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
/* 1 */
font-size: 100%;
/* 1 */
line-height: 1.15;
/* 1 */
margin: 0;
/* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box;
/* 1 */
color: inherit;
/* 2 */
display: table;
/* 1 */
max-width: 100%;
/* 1 */
padding: 0;
/* 3 */
white-space: normal;
/* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box;
/* 1 */
padding: 0;
/* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

View File

@@ -0,0 +1,48 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@@ -0,0 +1,31 @@
/*
* @Author: Kane
* @Date: 2023-02-28 19:25:30
* @LastEditors: Kane
* @FilePath: /task_schedule/src/assets/css/public/variables.scss
* @Description:
*
* Copyright (c) ${2022} by Kane, All Rights Reserved.
*/
// color
/*
$color-bg-01: #fecb96;
$color-bg-02: #f7954e;
$color-bg-03: #f27620;
$color-bg-04: #da3703;
$color-bg-05: #ba1800;
*/
$color-bg-01: #00b2f8;
$color-bg-02: #00b0fb;
$color-bg-03: #0279ea;
$color-bg-04: #046ed6;
$color-bg-05: #033eb6;
$color-title-font: #046ed6;
$color-charts-bg: #ffffff9f;
$color-honorlist-bg: rgba(255, 255, 255, 0.3);
$banner-background-color: #1d74b2;

View File

@@ -0,0 +1,11 @@
@font-face {
font-family: "FZ-ZHUOHEI";
src: url("FZZhuoHJW.TTF");
font-weight: normal;
}
@font-face {
font-family: "FZ-RGAQSAY";
src: url("FZSJ-RUGAQSAY.TTF");
font-weight: normal;
}

View File

@@ -0,0 +1,86 @@
<!--
* @Author: Kane
* @Date: 2023-03-23 15:07:31
* @LastEditors: Kane
* @FilePath: /task_schedule/src/layout/Index.vue
* @Description:
*
* Copyright (c) ${2022} by Kane, All Rights Reserved.
-->
<template>
<el-container class="layout-container">
<el-header class="layout-header">
<LayoutHeader />
</el-header>
<el-container class="layout-container-down">
<el-aside class="layout-aside">
<LayoutAside />
</el-aside>
<el-main class="layout-main">
<LayoutMain />
</el-main>
</el-container>
</el-container>
</template>
<script lang="ts">
// 组件
import LayoutHeader from "./components/Header.vue";
import LayoutAside from "./components/Aside.vue";
import LayoutMain from "./components/Main.vue";
export default {
name: "MainFrame",
components: {
LayoutHeader,
LayoutAside,
LayoutMain,
},
setup()
{
return {};
},
};
</script>
<style lang="scss" scoped>
@media screen {
.layout-container {
height: 100vh;
width: 100vw;
max-height: 100vh;
max-width: 100vw;
.layout-header {
height: 50px;
width: 100vw;
max-height: 50px;
max-width: 100vw;
padding: 0px;
}
.layout-container-down {
height: calc(100vh - 50px);
max-height: calc(100vh - 50px);
width: 100vw;
max-width: 100vw;
.layout-aside {
height: calc(100vh - 50px);
max-height: calc(100vh - 50px);
min-height: calc(100vh - 50px);
width: 200px;
overflow-x: hidden;
background-color: #2f4156;
}
.layout-main {
padding: 0px;
height: calc(100vh - 50px);
width: calc(100vw - 200px);
}
}
}
}
</style>

View File

@@ -0,0 +1,146 @@
<!--
author: Kane Wang <wangkane@qq.com>
date: 2025-10-23 15:32:30
component: Aside
Copyright © CPIC All rights reserved
-->
<template>
<el-scrollbar class="sidebar-wrapper">
<el-menu
class="side-bar"
router
:default-active="currentPath"
background-color="#2f4156"
text-color="#fff"
active-text-color="#ffd04b"
>
<template v-for="route in routes">
<template v-if="!route.hidden">
<template v-if="hasOnlyChild(route.children)">
<!-- 当只有一个子路由时将这个子路由作为顶级菜单项 -->
<el-menu-item
:key="route.children[0].path"
:index="route.children[0].path"
class="sidebar-submenu"
>
<component
:is="route.children[0] && route.children[0].meta.icon"
class="icons"
/>
<template #title>
{{ route.children[0].meta && route.children[0].meta.title }}
</template>
</el-menu-item>
</template>
<template v-else>
<!-- 不止一个子路由可能是咩有子s路由或者有多个子路由 -->
<!-- 如果没有子路由就不渲染 -->
<el-sub-menu
v-if="route.children && route.children.length"
:key="route.path"
:index="route.path"
class="sidebar-submenu"
>
<template #title>
<component
:is="route.meta && route.meta.icon"
class="icons"
/>
<span>{{ route.meta && route.meta.title }}</span>
</template>
<template v-for="child in route.children">
<el-menu-item
v-if="!child.hidden"
:key="child.path"
:index="child.path"
class="sidebar-item"
>
<component
:is="child.meta && child.meta.icon"
class="icons"
/>
<template #title>
{{ child.meta && child.meta.title }}
</template>
</el-menu-item>
</template>
</el-sub-menu>
</template>
</template>
</template>
</el-menu>
</el-scrollbar>
</template>
<script lang="js">
import { hasOnlyChild } from "@/router/index.ts";
import { useRoute, useRouter } from "vue-router";
import { computed } from "vue";
export default {
name: "LayoutAside",
setup()
{
const userRout = useRoute();
const router = useRouter();
const routes = router.getRoutes();// as SideBarRouteRecordNormalized[];
const currentPath = computed(() =>
{
return userRout.path;
});
return { userRout, routes, currentPath, hasOnlyChild, };
},
};
</script>
<style lang="scss" scoped>
@import "@/assets/css/public/variables.scss";
@import "@/assets/css/public/mixin.scss";
.sidebar-wrapper {
@include no-select;
height: 100%;
width: 100%;
}
:deep(.el-menu) {
border-right: none;
/* border-left: 5px solid #1d74b2; */
overflow: auto;
.el-menu-item {
font-weight: normal;
}
.el-sub-menu {
font-weight: normal;
}
.el-menu-item.is-active {
// background-color: #ffffff1f !important;
font-weight: 1000;
font-size: 15px;
color: #ffd04b;
}
}
.sidebar-submenu {
background-color: #2f4156 !important;
}
.sidebar-item {
background-color: #223142 !important;
}
/* .is-opened {
border-left: 5px solid #1d74b2;
} */
.icons {
width: 1em;
height: 1em;
margin-right: 8px;
}
</style>

View File

@@ -0,0 +1,118 @@
<!--
author: Kane Wang <wangkane@qq.com>
date: 2025-10-23 15:32:30
component: Header
Copyright © CPIC All rights reserved
-->
<template>
<div class="header-wrapper">
<span class="company-name">CPIC</span>
<div class="version-wrapper">
<span>制度库后台管理</span>
<span>Build-20251021</span>
</div>
<div class="buttons-wrapper">
<component
:is="'SwitchButton'"
class="icons"
@click="Logout"
/>
</div>
</div>
</template>
<script lang="ts">
import { ElMessageBox } from "element-plus";
// import { logout } from "@/utils/account.js";
export default {
name: "LayoutHeader",
setup()
{
/**
* 退出登录
*/
const Logout = (): void =>
{
ElMessageBox.confirm(
"是否要退出系统?",
"",
{
confirmButtonText: "是",
cancelButtonText: "否",
type: "warning",
}
)
.then((): void =>
{
// debugger;
console.log( "退出" );
// logout();
})
.catch((): void => {});
};
return { Logout, };
},
};
</script>
<style lang="scss" scoped>
.header-wrapper {
// @include no-select;
height: 50px;
max-height: 50px;
padding: 0px 15px;
// position: relative;
display: flex;
justify-content: start;
align-items: center;
background-color: #1d74b2;
color: #fff;
// background-color: $banner-background-color;
span {
text-align: left;
}
>*+* {
margin-left: 10px;
}
.version-wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: start;
font: {
size: 0.75rem;
}
>*+* {
margin-top: 1px;
}
}
.company-name {
font-size: 2rem;
}
.buttons-wrapper {
margin-left: auto;
padding-top: 5px;
// border: 1px solid salmon;
}
.icons {
width: 25px;
height: 25px;
// margin-right: 8px;
cursor: pointer;
}
}
</style>

View File

@@ -0,0 +1,29 @@
<!--
author: Kane Wang <wangkane@qq.com>
date: 2025-10-23 17:52:01
component: Main
Copyright © CPIC All rights reserved
-->
<template>
<el-scrollbar>
<router-view />
</el-scrollbar>
</template>
<script lang="ts">
export default {
name: "LayoutMain",
setup()
{
return {};
},
};
</script>
<style scoped>
.el-scrollbar {
height: 100%;
width: 100%;
/* background-color: #ecf2f9; */
}
</style>

View File

@@ -1,16 +1,32 @@
// eslint-disable-next-line
import { createApp, VueElement } from "vue";
import "./style.css";
import "./assets/css/index.scss";
import "./style.scss";
// 路由
// import { router, hasOnlyChild } from "./router/RouteIndex";
import { router } from "./router/index.ts";
// import App from "./App.vue";
import AppMain from "./AppMain.vue";
import ElementPlus from "element-plus";
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
import "element-plus/dist/index.css";
// eslint-disable-next-line
const app = createApp( AppMain );
app.use( ElementPlus );
app.use( router );
// 引入element-icon
for ( const [key, component,] of Object.entries( ElementPlusIconsVue ))
{
app.component( key, component );
}
app.mount( "#app" );
// eslint-disable-next-line

View File

@@ -2,17 +2,124 @@
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-10-13 15:31:41
* @LastEditors: Kane Wang
* @LastModified: 2025-10-13 15:37:20
* @LastModified: 2025-11-05 11:20:46
* @FilePath: src/router/index.ts
* @Description:
*
* Copyright (c) 2025 by Kane All rights reserved
*/
import { createRouter, createWebHashHistory } from "vue-router";
import { type RouteRecordNormalized } from "vue-router";
// import index from "../layout/console/index.vue";
export declare interface SideBarRouteRecordNormalized extends RouteRecordNormalized
{
hidden?: boolean;
}
const routes = [
{
path: "/",
name: "root",
redirect: "Overview",
hidden: true,
},
{
path: "/desktop",
name: "Desktop",
meta: {
title: "工作台",
icon: "house",
},
component: () => import( "../layout/console/Index.vue" ),
children:[
{
path: "/overview",
name: "Overview",
meta: {
title: "总览",
icon: "house",
},
component: () => import( "@/views/console/desktop/Overview.vue" ),
},
],
},
{
path: "/data",
name: "Data",
meta: {
title: "制度库管理",
icon:"Collection",
},
component: () => import( "../layout/console/Index.vue" ),
children:[
{
path: "/regulatory-management",
name:"RegulatoryManagement",
meta: {
title: "制度文件管理",
icon:"document",
},
component: ()=> import( "@/views/console/data/RegulatoryManagement.vue" ),
},
{
path: "/department-management",
name: "DepartmentManagement",
meta:{
title: "组织机构管理",
icon: "OfficeBuilding",
},
component: ()=> import( "@/views/console/data/RegulatoryManagement.vue" ),
},
{
path: "/new-regulatory",
name: "NewRegulatory",
meta: {
title: "测试用 - 新建",
icon: "OfficeBuilding",
},
hidden: false,
component: () => import( "@/views/console/data/NewRegulatory.vue" ),
},
{
path: "/upload-regulatory",
name: "UploadRegulatory",
meta: {
title: "测试用 - 查看制度",
icon: "OfficeBuilding",
},
hidden: false,
component: () => import( "@/views/console/data/UploadRegulatory.vue" ),
},
],
},
];
const router = createRouter({
history: createWebHashHistory(),
routes:[],
routes: routes,
});
export default router;
// 工具函数
/* eslint-disable */
function hasOnlyChild( children: any ): boolean
{
if ( !children )
{
return false;
}
const routes = children.filter(( item: any )=>
{
return !item.hidden;
});
if ( routes.length === 1 )
{
return true;
}
return false;
}
export { router, hasOnlyChild };

View File

@@ -0,0 +1,21 @@
body
{
width: 100vw;
height: 100vh;
padding: 0px;
margin: 0px;
overflow: hidden;
// @include no-select
-webkit-touch-callout: none;
-moz-user-select: none;
/*火狐*/
-webkit-user-select: none;
/*webkit浏览器*/
-ms-user-select: none;
/*IE10*/
-khtml-user-select: none;
/*早期浏览器*/
user-select: none;
}

View File

@@ -0,0 +1,27 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-10-28 10:13:04
* @LastEditors: Kane Wang
* @LastModified: 2025-10-28 10:13:04
* @FilePath: src/types/regulatory.ts
* @Description: 和制度相关的类型定义
*
* Copyright (c) 2025 by Kane All rights reserved
*/
interface RegulatoryData {
department_name: string;
release_year: string;
regulatory_name: string;
comment: string;
regulatory_files: RegulatoryFile[];
}
interface RegulatoryFile {
regulatory_file_name: string;
file_url: string;
local_file_path: string;
file_type: string;
}
export { type RegulatoryData, type RegulatoryFile };

View File

@@ -0,0 +1,29 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-11-21 10:19:11
* @LastEditors: Kane Wang
* @LastModified: 2025-11-27 17:41:28
* @FilePath: src/types/upload_file.ts
* @Description:
*
* Copyright (c) 2025 by Kane All rights reserved
*/
interface UploadFileResponse
{
success: boolean;
message: string;
fileList: UploadedFile[];
}
interface UploadedFile
{
fileName: string;
absoluteFilePath: string;
relativeFilePath: string;
}
export {
type UploadedFile,
type UploadFileResponse
};

View File

@@ -0,0 +1,19 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-11-21 09:39:59
* @LastEditors: Kane Wang
* @LastModified: 2025-11-21 09:40:00
* @FilePath: src/utils/config.ts
* @Description: 保存应用的配置参数。
*
* Copyright (c) 2025 by Kane All rights reserved
*/
const API_URL= {
URL_UPLOAD_FILE: import.meta.env.VITE_APP_URL_UPLOAD_FILE,
URL_MOVE_FILE: import.meta.env.VITE_APP_URL_MOVE_FILE,
URL_PREFIX: import.meta.env.VITE_APP_URL_PREFIX,
};
console.log( API_URL );
export { API_URL };

View File

@@ -0,0 +1,56 @@
/**
* @Author: Kane Wang <wangkane@qq.com>
* @Date: 2025-10-23 16:52:10
* @LastEditors: Kane Wang
* @LastModified: 2025-11-21 11:17:40
* @FilePath: src/utils/utils.ts
* @Description: 提供 一些功能性的函数
*
* Copyright (c) 2025 by Kane All rights reserved
*/
/**
* 取文件路径末尾的扩展名作为文件类型
* @param filePath 文件路径
* @returns 文件类型字符串
*/
function getFileType( filePath: string ): string
{
let type = "未知类型";
if ( filePath == null || filePath.length == 0 )
{
return type;
}
const startIndex = filePath.lastIndexOf( "." );
const fileType = filePath.slice( startIndex + 1 ).toUpperCase();
// ignore-eslint-next-line
switch( fileType )
{
case "DOCX":
type = "WPS文档";
break;
case "XLSX":
type = "WPS表格";
break;
case "PDF":
type = "PDF文档";
break;
case "JPG":
case "PNG":
case "BMP":
case "GIF":
type = "图片文件";
break;
default:
type = "未知文件类型";
}
// type = fileType.length != 0 ? fileType + "文件" : "未知类型";
return type;
}
export { getFileType };

View File

@@ -0,0 +1,407 @@
<!--
author: Kane Wang <wangkane@qq.com>
date: 2025-10-30 15:06:18
component: NewRegulatory
Copyright © CPIC All rights reserved
-->
<template>
<div class="wrapper">
<div class="query-box-wrapper">
<el-row :gutter="10">
<el-col :span="1">
<span>名称</span>
</el-col>
<el-col :span="10">
<el-input v-model.trim.lazy="ui.newRegulatory.regulatory_name" style="text-align:center;" />
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="1">
<span>部门</span>
</el-col>
<el-col :span="4">
<el-input v-model.trim="ui.newRegulatory.department_name" />
</el-col>
<el-col :span="2">
<span>发布修订年份</span>
</el-col>
<el-col :span="4">
<el-input v-model.lazy.number.trim="ui.newRegulatory.release_year" />
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="1">
<span>备注</span>
</el-col>
<el-col :span="10">
<el-input v-model.lazy.trim="ui.newRegulatory.comment" type="textarea" :rows="3" />
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="3">
<div class="button-wrapper-left">
<el-button type="primary" icon="document" @click="showUploadFileDialog">
新增文档
</el-button>
</div>
</el-col>
<el-col :span="5" />
<el-col :span="3">
<div class="button-wrapper-right">
<el-button
type="primary" icon="document"
@click="onPreviewUploadedFile(1)"
>
提交
</el-button>
</div>
</el-col>
</el-row>
</div>
<el-table
width="100%" stripe
border="true"
:head-cell-style="headerCellStyle"
:row-class-name="tableRowClassName"
empty-text="请上传文件"
:data="ui.newRegulatory.regulatory_files"
>
<el-table-column label="文件名" align="center">
<template #default="file">
<span>{{ file.row.regulatory_file_name }}</span>
</template>
</el-table-column>
<el-table-column label="文件类型" align="center" width="200px">
<template #default="file">
<span>{{ file.row.file_type }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="200px">
<template #default="file">
<el-button
type="primary" icon="search"
circle
@click="onPreviewUploadedFile(file.row.rowIndex)"
/>
<el-button
type="danger" icon="delete"
circle
@click="onDeleteUploadedFile(file.row.rowIndex)"
/>
</template>
</el-table-column>
</el-table>
<div class="upload-dialog-wrapper">
<el-dialog
v-model="ui.showUploadDialog" title="上传文件"
width="600px"
:close-on-click-model="false" :close-on-press-escape="false"
:show-close="true"
>
<el-upload
drag
:action="ui.urlFileUpload"
name="files"
:show-file-list="false"
:data="ui.uploadParameters"
:on-success="onUploadSuccess"
>
<el-icon class="el-icon--upload">
<upload-filled />
</el-icon>
<div class="el-upload__text">
将文件拖到此处<em>点击上传</em>
</div>
</el-upload>
</el-dialog>
<el-dialog
v-model="ui.isPDF" :close-on-click-model="false"
:close-on-press-escape="false"
:show-close="true"
align-center
title="文件预览"
width="1024px"
>
<VueOfficePdf
v-if="ui.isPDF" :src="ui.fileURL"
style="height:calc(100vh - 100px);"
@error="errorHandle"
/>
</el-dialog>
<el-dialog
v-model="ui.isDOCX" :close-on-click-model="false"
:close-on-press-escape="false"
:show-close="true"
align-center
title="文件预览"
width="1024px"
>
<VueOfficeDocx
v-if="ui.isDOCX" :src="ui.fileURL"
style="height:calc(100vh - 100px);"
@error="errorHandle"
/>
</el-dialog>
<el-dialog
v-model="ui.isXLSX" :close-on-click-model="false"
:close-on-press-escape="false"
:show-close="true"
align-center
title="文件预览"
width="1024px"
>
<VueOfficeExcel
:src="ui.fileURL"
style="height:calc(100vh - 100px);"
@error="errorHandle"
/>
</el-dialog>
</div>
</div>
</template>
<script lang="ts">
import {reactive, ref} from "vue";
import { type UploadProps, type UploadFile, type UploadFiles, ElMessage, ElMessageBox } from "element-plus";
import {type RegulatoryData, type RegulatoryFile} from "@/types/regulatory/regulatory.ts";
import { type UploadedFile, type UploadFileResponse } from "@/types/upload_file.ts";
import {API_URL} from "@/utils/config.ts";
import { getFileType } from "@/utils/utils";
import VueOfficePdf from "@vue-office/pdf";
import VueOfficeDocx from "@vue-office/docx";
import VueOfficeExcel from "@vue-office/excel";
//引入相关样式
import "@vue-office/docx/lib/index.css";
import "@vue-office/excel/lib/index.css";
interface UI{
showUI: boolean;
showUploadDialog: boolean;
showPreviewDialog: boolean;
urlFileUpload: string;
uploadParameters: {
fileName: string;
};
newRegulatory: RegulatoryData;
showFileList: boolean;
isPDF: boolean;
isDOCX: boolean;
isXLSX: boolean;
fileURL: string;
};
export default {
name: "NewRegulatory",
components: {VueOfficePdf, VueOfficeDocx, VueOfficeExcel,},
setup()
{
const ui: UI = reactive({
showUI: true,
showUploadDialog: false,
showPreviewDialog: false,
urlFileUpload: API_URL.URL_UPLOAD_FILE,
uploadParameters: {
fileName: "1234",
},
newRegulatory: {
department_name: "",
release_year: "",
regulatory_name: "",
comment: "",
regulatory_files: [],
},
showFileList: false,
isPDF: false,
isDOCX: false,
isXLSX: false,
fileURL: "",
});
const headerCellStyle = reactive(
{
textAlign: "center",
}
);
const cellStyle = reactive({
textAlign: "center",
});
const showUploadFileDialog = (): void =>
{
ui.showUploadDialog = true;
};
/*表格操作相关 */
/**
* 删除对应的上传文件记录。
* @param rowId 行号
*/
const onDeleteUploadedFile = ( rowId: number ): void =>
{
console.log( `点击的rowid${rowId}` );
ElMessage.info( `选取的行号:${rowId}` );
ElMessageBox.confirm(
"确认删除文件?",
"删除文件",
{
confirmButtonText: "确定",
type: "warning",
center: true,
}
)
.then(()=>
{
ui.newRegulatory.regulatory_files?.splice( rowId, 1 );
})
.catch(()=>{});
};
const onPreviewUploadedFile = ( rowId: number ): void =>
{
// ui.showPreviewDialog = true;
ui.fileURL = ui.newRegulatory.regulatory_files[rowId]?.file_url + "/" + ui.newRegulatory.regulatory_files[rowId]?.regulatory_file_name;
console.log( "完整路径:", ui.fileURL );
ui.isDOCX = false;
ui.isPDF = false;
ui.isXLSX = false;
switch ( ui.newRegulatory.regulatory_files[rowId].file_type )
{
case "WPS文档":
ui.isDOCX = true;
break;
case "WPS表格":
ui.isXLSX = true;
break;
case "PDF文档":
ui.isPDF = true;
break;
default:
break;
}
};
/**
* 用来给 el-table 行设置样式。
* 其中给row加一个参数rowIndex用行号赋值。
* @param element-plus给于的参数
*/
const tableRowClassName = ({row, rowIndex,}: {row:any, rowIndex:number}): void =>
{
console.log( `${row}设置行号${rowIndex}` );
row.rowIndex = rowIndex;
};
/**
* 上传成功的响应函数。
* @param response
* @param uploadFile
* @param uploadFiles
*/
const onUploadSuccess: UploadProps["onSuccess"] = ( response: UploadFileResponse, uploadFile: UploadFile, uploadFiles: UploadFiles ): void =>
{
console.log( `上传制度文件响应:${response}` );
console.log( `上传文件:${uploadFile}` );
console.log( `上传多文件:${uploadFiles}` );
ui.showUploadDialog = false;
// 先判断成功标志位
if ( response.success )
{
// 成功,把文件写入清单
if ( response.fileList === null || response.fileList.length === 0 )
{
// 上传文件路径有问题,提示一下
ElMessageBox.confirm(
"上传文件的保存路径有误,请联系开发人员。",
"上传文件错误",
{
confirmButtonText: "确定",
type: "warning",
center: true,
}
)
.then((): void => {})
.catch((): void => {});
}
const uploadedFile: RegulatoryFile = {
regulatory_file_name: response.fileList[0].fileName ?? "",
local_file_path: response.fileList[0]?.absoluteFilePath ?? "",
file_type: getFileType( response.fileList[0]?.fileName ),
file_url: API_URL.URL_PREFIX + "/" + response.fileList[0]?.relativeFilePath,
};
ui.newRegulatory.regulatory_files?.push( uploadedFile );
console.log( "文件列表", ui.newRegulatory.regulatory_files );
}
else
{
// 失败了,提示一下
ElMessageBox.confirm(
response.message,
"上传文件错误",
{
confirmButtonText: "确定",
type: "warning",
center: true,
}
)
.then((): void => {})
.catch((): void => {});
}
};
const errorHandle = ()=>
{
ElMessage.error( "渲染文档出错!" );
};
return {
ui,
headerCellStyle,
cellStyle,
onUploadSuccess,
tableRowClassName,
showUploadFileDialog,
onDeleteUploadedFile,
onPreviewUploadedFile,
errorHandle,
};
},
};
</script>
<style lang="scss" scoped>
@import "@/assets/css/public/_public.scss";
.wrapper {
// max-width: 800px;
min-width: 1024px;
margin: 10px 10px 10px 10px;
>*+* {
margin-top: 10px;
}
}
.query-box-wrapper
{
@include query-box-wrap;
}
:deep(.el-input) .el-input__inner {
text-align: center;
}
:deep(.el-dialog) {
min-width: calc(100wh - 100px);
max-width: calc(100wh - 100px);
}
</style>

View File

@@ -0,0 +1,127 @@
<!--
author: Kane Wang <wangkane@qq.com>
date: 2025-10-23 21:40:24
component: RegulatoryManagement
Copyright © CPIC All rights reserved
-->
<template>
<div class="wrapper">
<el-row :gutter="10">
<el-col span="24">
<el-button
type="warning"
icon="upload"
>
上传
</el-button>
<el-button
type="primary"
icon="refresh"
>
刷新
</el-button>
</el-col>
</el-row>
<el-table
width="100%" stripe
:header-cell-style="headerCellStyle"
border
:data="ui.regulatoryData"
>
<el-table-column label="部门" align="center" width="200px">
<template #default="regulatory">
<span>{{ regulatory.row.department_name }}</span>
</template>
</el-table-column>
<el-table-column width="150px" label="发布、修订年份" align="center">
<template #default="regulatory">
<span>{{ regulatory.row.release_year }}</span>
</template>
</el-table-column>
<el-table-column label="制度名称" align="center" :cell-style="cellStyle">
<template #default="regulatory">
<span>{{ regulatory.row.regulatory_name }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="200px">
<el-button
type="primary"
icon="search"
>
查看
</el-button>
<el-button type="warning" icon="document">
编辑
</el-button>
</el-table-column>
</el-table>
</div>
</template>
<script lang="ts">
import { reactive } from "vue";
import { type RegulatoryData } from "@/types/regulatory/regulatory.ts";
interface UI
{
showUI: boolean,
showUploadDialog: boolean,
tablePageSize: number,
tableCurrentPageIndex: number,
regulatoryData: RegulatoryData[],
}
export default {
name: "RegulatoryManagement",
setup()
{
const headerCellStyle = reactive(
{
textAlign: "center",
}
);
const cellStyle = reactive({
textAlign: "center",
});
const ui:UI = reactive({
showUI: true,
showUploadDialog: false,
tablePageSize: 10,
tableCurrentPageIndex: 1,
regulatoryData: [
{
department_name: "信息技术部",
release_year: "2019",
regulatory_name: "关于印发修订后的《太平洋产险厦门分公司信息系统账号权限管理实施细则》的通知",
comment:"",
regulatory_files: [],
},
{
department_name: "信息技术部",
release_year: "2019",
regulatory_name: "关于印发修订后的《太平洋产险厦门分公司个人信息保护管理实施细则》的通知",
comment:"",
regulatory_files: [],
},
],
});
return { ui, headerCellStyle, cellStyle, };
},
};
</script>
<style lang="scss" scoped>
@import "@/assets/css/public/mixin.scss";
.wrapper {
margin: 10px 10px 10px 10px;
>*+* {
margin-top: 10px;
}
}
.pagination_wrapper {
display: flex;
justify-content: flex-end;
}
</style>

View File

@@ -0,0 +1,78 @@
<!--
author: Kane Wang <wangkane@qq.com>
date: 2025-10-28 11:18:51
component: UploadRegulatory
Copyright © CPIC All rights reserved
-->
<template>
<div class="wrapper">
<!-- <VuePdfEmbed v-if="ui.isPDF" :source="ui.fileURL" text-layer annotation-layer /> -->
<VueOfficePdf v-if="ui.isPDF" :src="ui.fileURLPDF" @error="errorHandle" />
<VueOfficeDocx v-if="ui.isDocx" :src="ui.fileURLDocx" @error="errorHandle" />
<VueOfficeExcel
v-if="ui.isXlsx"
:src="ui.fileURLXlsx"
@error="errorHandle"
/>
</div>
</template>
<script lang="ts">
import {reactive, ref } from "vue";
import { ElMessage } from "element-plus";
// import VuePdfEmbed, { useVuePdfEmbed } from "vue-pdf-embed";
import VueOfficePdf from "@vue-office/pdf";
import VueOfficeDocx from "@vue-office/docx";
import VueOfficeExcel from "@vue-office/excel";
import "vue-pdf-embed/dist/styles/annotationLayer.css";
import "vue-pdf-embed/dist/styles/textLayer.css";
import "@vue-office/excel/lib/index.css";
export default {
name:"UploadRegulatory",
components: {
VueOfficePdf,
VueOfficeDocx,
VueOfficeExcel,
},
setup()
{
const ui = reactive({
showUI: true,
isPDF: false,
isDocx: false,
isXlsx: true,
fileURLPDF: "http://10.39.0.1:8080/regulatory/%E4%BF%A1%E6%81%AF%E6%8A%80%E6%9C%AF%E9%83%A8/%E5%85%B3%E4%BA%8E%E5%8D%B0%E5%8F%91%E3%80%8A%E5%A4%AA%E5%B9%B3%E6%B4%8B%E4%BA%A7%E9%99%A9%E5%8E%A6%E9%97%A8%E5%88%86%E5%85%AC%E5%8F%B8IT%E8%B5%84%E4%BA%A7%E7%AE%A1%E7%90%86%E5%8A%9E%E6%B3%95%E3%80%8B%EF%BC%882019%E5%B9%B4%E4%BF%AE%E8%AE%A2%EF%BC%89%E7%9A%84%E9%80%9A%E7%9F%A5/%E5%8E%A6%E5%A4%AA%E4%BF%9D%E4%BA%A7%E5%8F%91%E3%80%902019%E3%80%9146%E5%8F%B7%E5%85%B3%E4%BA%8E%E5%8D%B0%E5%8F%91%E3%80%8A%E5%A4%AA%E5%B9%B3%E6%B4%8B%E4%BA%A7%E9%99%A9%E5%8E%A6%E9%97%A8%E5%88%86%E5%85%AC%E5%8F%B8IT%E8%B5%84%E4%BA%A7%E7%AE%A1%E7%90%86%E5%8A%9E%E6%B3%95%E3%80%8B%EF%BC%882019%E5%B9%B4%E4%BF%AE%E8%AE%A2%EF%BC%89%E7%9A%84%E9%80%9A%E7%9F%A5.pdf",
fileURLDocx: "http://10.39.0.1:8080/regulatory/%E4%BF%A1%E6%81%AF%E6%8A%80%E6%9C%AF%E9%83%A8/%E5%85%B3%E4%BA%8E%E5%8D%B0%E5%8F%91%E3%80%8A%E5%A4%AA%E5%B9%B3%E6%B4%8B%E4%BA%A7%E9%99%A9%E5%8E%A6%E9%97%A8%E5%88%86%E5%85%AC%E5%8F%B8%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E9%9C%80%E6%B1%82%E7%AE%A1%E7%90%86%E5%8A%9E%E6%B3%95%EF%BC%882020%E5%B9%B4%E4%BF%AE%E8%AE%A2%EF%BC%89%E3%80%8B%E7%9A%84%E9%80%9A%E7%9F%A5/%E9%99%84%E4%BB%B62%EF%BC%9A%E4%B8%9A%E5%8A%A1%E9%9C%80%E6%B1%82%E8%AF%B4%E6%98%8E%E4%B9%A6.docx",
fileURLXlsx: "http://10.39.0.1:8080/regulatory/%E4%BF%A1%E6%81%AF%E6%8A%80%E6%9C%AF%E9%83%A8/%E5%85%B3%E4%BA%8E%E5%8D%B0%E5%8F%91%E3%80%8A%E5%A4%AA%E5%B9%B3%E6%B4%8B%E4%BA%A7%E9%99%A9%E5%8E%A6%E9%97%A8%E5%88%86%E5%85%AC%E5%8F%B8%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E9%9C%80%E6%B1%82%E7%AE%A1%E7%90%86%E5%8A%9E%E6%B3%95%EF%BC%882020%E5%B9%B4%E4%BF%AE%E8%AE%A2%EF%BC%89%E3%80%8B%E7%9A%84%E9%80%9A%E7%9F%A5/%E9%99%84%E4%BB%B64%EF%BC%9A%E5%88%86%E5%85%AC%E5%8F%B8%E8%87%AA%E5%BB%BA%E7%B3%BB%E7%BB%9F%E9%83%A8%E7%BD%B2%E6%83%85%E5%86%B5%E7%99%BB%E8%AE%B0%E8%A1%A8.xlsx",
});
const errorHandle = ()=>
{
ElMessage.error( "渲染文档出错!" );
};
return {
ui,
errorHandle, };
},
};
</script>
<style lang="scss">
.wrapper {
margin: 10px 10px 10px 10px;
width: calc(100% - 20px);
height: calc( 100vh - 70px);
// .VueOfficeExcel {
// height: calc( 100vh - 70px);
// }
>*+* {
margin-top: 10px;
}
}
</style>

View File

@@ -0,0 +1,32 @@
<!--
author: Kane Wang <wangkane@qq.com>
date: 2025-10-23 21:40:24
component: RegulatoryManagement
Copyright © CPIC All rights reserved
-->
<template>
<div class="wrapper">
总览
</div>
</template>
<script lang="ts">
export default {
name: "OverViews",
setup()
{
const ui = {};
return { ui, };
},
};
</script>
<style lang="scss" scoped>
@import "@/assets/css/public/mixin.scss";
.wrapper {
@include no-select;
margin: 5px;
border: 1px solid red;
}
</style>

View File

@@ -1 +1,8 @@
/// <reference types="vite/client" />
// / <reference types="vite/client" />
declare module "*.vue"
{
import type { DefineComponent } from "vue";
const vueComponent: DefineComponent<unknown, unknown, any>;
export default vueComponent;
}

View File

@@ -1,16 +1,9 @@
// {
// "files": [],
// "references": [
// {
// "path": "./tsconfig.node.json"
// },
// {
// "path": "./tsconfig.app.json"
// }
// ],
// }
{
"compilerOptions": {
// "allowJs": true,
// "declaration": true,
"noEmit": true,
"allowImportingTsExtensions": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"forceConsistentCasingInFileNames": true,
"useDefineForClassFields": true,
@@ -45,7 +38,7 @@
"src/**/*.tsx",
"src/**/*.vue",
"*.d.ts",
"src/router/index.d.ts",
".vscode/bak/RouteIndex.d.ts",
"vite.config.*",
"vitest.config.*",
"cypress.config.*",

View File

@@ -19,7 +19,8 @@
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
"noUncheckedSideEffectImports": true,
"declaration":true,
},
"include": ["vite.config.ts"]
}

View File

@@ -1,7 +1,30 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
/* eslint-disable */
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
})
plugins: [vue(),],
resolve: {
//配置别名
alias: [
{
find: /^~/,
replacement: "",
},
{
find: "@",
replacement: path.resolve( __dirname, "src" ),
},
],
},
css: {
preprocessorOptions: {
scss: {
// additionalData: '@import "./src/assets/css/public/variables.scss";@import "./src/assets/css/public/mixin.scss";',
// additionalData: '@import "@/assets/css/public/variables.scss";',
},
},
},
});

162
开发日志.md Normal file
View File

@@ -0,0 +1,162 @@
# 前端
## 问题
### vue3 引入@路径
#### 引入path模块问题
node.js 自带的path模块是JavaScript代码要引入ts文件需要安装@type/node模块
```shell
npm install @types/node --save-dev
```
之后就可以加载path。在 vite.config.ts 中加上
```typescript
import path from "path";
resolve: {
//配置别名
alias: [
{
find: /^~/,
replacement: "",
},
{
find: "@",
replacement: path.resolve( __dirname, "src" ),
},
],
},
```
### tomcat 跨域下载文件
tomcat 默认是不支持跨域的不做配置使用vue下载文件会有Access-Control-Allow-Origin问题。
解决方法: 编辑 web.xml 文件,找到
```xml
<!-- The mapping for the HTTP header security Filter -->
```
在下面添加
```xml
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
```
重启 tomcat 解决。
## 组件
### pdf预览组件
使用
## 技术
### element-plus 文件上传
element-plus 文件上传组件的响应函数,作为:on-success属性值。要传递的参数作为:data属性值。
```typescript
import { type FileUploadResponse } from "@/utils/fileUpload.js";
import { type UploadProps, type UploadFile, type UploadFiles, ElMessage, ElMessageBox } from "element-plus";
const onUploadSuccess: UploadProps["onSuccess"] = ( response: FileUploadResponse, uploadFile: UploadFile, uploadFiles: UploadFiles ): void =>
{
// 先判断成功标志位
if ( response.success )
{
// 成功,发出导入报表请求
if ( response.fileList.length === 0 )
{
// 上传文件路径有问题,提示一下
ElMessageBox.confirm(
"上传文件的保存路径有误,请联系开发人员。",
"上传文件错误",
{
confirmButtonText: "确定",
type: "warning",
center: true,
}
)
.then((): void => {})
.catch((): void => {});
}
const request: ImportBIReportRequest = {
filePath: response.fileList[0],
reportType: ui.selectedReportType,
firstRow: ui.firstRow,
sheetIndex: ui.sheetIndex,
};
console.log( "请求参数", request );
// 发出请求
importBIReport( request, importResponseHandler );
}
else
{
// 失败了,提示一下
ElMessageBox.confirm(
response.message,
"上传文件错误",
{
confirmButtonText: "确定",
type: "warning",
center: true,
}
)
.then((): void => {})
.catch((): void => {});
}
};
```
### typescript 的安全链式调用 和 强制链式调用
在链式调用时,在成员访问操作符前加上?,表示安全链式调用;加上!表示强制链式调用。
如果前的属性存在则正常调用否则返回null。
```typescript
// 这里 Error对象定义的stack是可选参数如果这样写的话编译器会提示
// 出错 TS2532: Object is possibly 'undefined'.
return new Error().stack.split('\n');
// 我们可以添加?操作符当stack属性存在时调用 stack.split。
// 若stack不存在则返回空
return new Error().stack?.split('\n');
// 以上代码等同以下代码, 感谢 @dingyanhe 的监督
const err = new Error();
return err.stack && err.stack.split('\n');
```
强制链式调用表示!前的属性一定会存在。
```typescript
// 这里 Error对象定义的stack是可选参数如果这样写的话编译器会提示
// 出错 TS2532: Object is possibly 'undefined'.
new Error().stack.split('\n');
// 我们确信这个字段100%出现,那么就可以添加!,强调这个字段一定存在
new Error().stack!.split('\n');
```