v3.26.0 【优化】分页请求2次;【优化】菜单展开单个代码优化;【优化】操作记录返回结果;【优化】json viewer升级;【优化】S3协议优化;【优化】代码生成字典优化;

This commit is contained in:
zhuoda 2025-08-10 13:02:01 +08:00
parent 8135e0ec10
commit d2c55e35ff
109 changed files with 309 additions and 266 deletions

View File

@ -67,7 +67,7 @@ public class AdminInterceptor implements HandlerInterceptor {
Method method = ((HandlerMethod) handler).getMethod();
NoNeedLogin noNeedLogin = ((HandlerMethod) handler).getMethodAnnotation(NoNeedLogin.class);
if (noNeedLogin != null) {
checkActiveTimeout(requestEmployee);
updateActiveTimeout(requestEmployee);
SmartRequestUtil.setRequestUser(requestEmployee);
return true;
}
@ -77,8 +77,8 @@ public class AdminInterceptor implements HandlerInterceptor {
return false;
}
// 检测token 活跃频率
checkActiveTimeout(requestEmployee);
// 更新活跃
updateActiveTimeout(requestEmployee);
// --------------- 第三步 校验 权限 ---------------
@ -123,15 +123,12 @@ public class AdminInterceptor implements HandlerInterceptor {
/**
* 检测token 最低活跃频率单位如果 token 超过此时间没有访问系统就会被冻结
* 更新活跃时间
*/
private void checkActiveTimeout(RequestEmployee requestEmployee) {
// 用户不在线也不用检测
private void updateActiveTimeout(RequestEmployee requestEmployee) {
if (requestEmployee == null) {
return;
}
StpUtil.checkActiveTimeout();
StpUtil.updateLastActiveToNow();
}

View File

@ -81,7 +81,7 @@ public class FileConfig implements WebMvcConfigurer {
StaticCredentialsProvider.create(
AwsBasicCredentials.create(accessKey, secretKey)))
.serviceConfiguration(S3Configuration.builder()
.pathStyleAccessEnabled(false)
.pathStyleAccessEnabled(true)
.chunkedEncodingEnabled(false)
.build())
.build();

View File

@ -101,7 +101,18 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
userMetadata.put(USER_METADATA_FILE_FORMAT, fileType);
userMetadata.put(USER_METADATA_FILE_SIZE, String.valueOf(file.getSize()));
PutObjectRequest putObjectRequest = PutObjectRequest.builder().bucket(cloudConfig.getBucketName()).key(fileKey).metadata(userMetadata).contentLength(file.getSize()).contentType(this.getContentType(fileType)).contentEncoding(StandardCharsets.UTF_8.name()).contentDisposition("attachment;filename=" + urlEncoderFilename).build();
// 根据文件路径获取并设置访问权限
ObjectCannedACL acl = this.getACL(path);
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(cloudConfig.getBucketName())
.key(fileKey)
.metadata(userMetadata)
.contentLength(file.getSize())
.contentType(this.getContentType(fileType))
.contentEncoding(StandardCharsets.UTF_8.name())
.contentDisposition("attachment;filename=" + urlEncoderFilename)
.acl(acl)
.build();
InputStream inputStream = null;
try {
inputStream = file.getInputStream();
@ -112,10 +123,6 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
} finally {
IOUtils.closeQuietly(inputStream);
}
// 根据文件路径获取并设置访问权限
ObjectCannedACL acl = this.getACL(path);
PutObjectAclRequest aclRequest = PutObjectAclRequest.builder().bucket(cloudConfig.getBucketName()).key(fileKey).acl(this.getACL(path)).build();
s3Client.putObjectAcl(aclRequest);
// 返回上传结果
FileUploadVO uploadVO = new FileUploadVO();
uploadVO.setFileName(originalFileName);

View File

@ -11,6 +11,7 @@ import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.base.common.constant.StringConst;
import net.lab1024.sa.base.common.domain.RequestUser;
import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.util.SmartIpUtil;
import net.lab1024.sa.base.common.util.SmartRequestUtil;
import net.lab1024.sa.base.module.support.operatelog.OperateLogDao;
@ -71,14 +72,14 @@ public abstract class OperateLogAspect {
public void logPointCut() {
}
@AfterReturning(pointcut = "logPointCut()")
public void doAfterReturning(JoinPoint joinPoint) {
handleLog(joinPoint, null);
@AfterReturning(pointcut = "logPointCut()", returning = "responseDTO")
public void doAfterReturning(JoinPoint joinPoint, Object responseDTO) {
handleLog(joinPoint, null, responseDTO);
}
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
handleLog(joinPoint, e);
handleLog(joinPoint, e, null);
}
/**
@ -109,16 +110,15 @@ public abstract class OperateLogAspect {
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
}
protected void handleLog(final JoinPoint joinPoint, final Exception e) {
protected void handleLog(final JoinPoint joinPoint, final Exception e, Object responseDTO) {
try {
OperateLog operateLog = this.getAnnotationLog(joinPoint);
if (operateLog == null) {
return;
}
this.submitLog(joinPoint, e);
this.submitLog(joinPoint, e, responseDTO);
} catch (Exception exp) {
log.error("保存操作日志异常:{}", exp.getMessage());
exp.printStackTrace();
}
}
@ -173,11 +173,8 @@ public abstract class OperateLogAspect {
/**
* 提交存储操作日志
*
* @param joinPoint
* @param e
* @throws Exception
*/
private void submitLog(final JoinPoint joinPoint, final Throwable e) throws Exception {
private void submitLog(final JoinPoint joinPoint, final Throwable e, Object responseDTO) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//设置用户信息
RequestUser user = SmartRequestUtil.getRequestUser();
@ -191,7 +188,7 @@ public abstract class OperateLogAspect {
String methodName = joinPoint.getSignature().getName();
String operateMethod = className + "." + methodName;
String failReason = null;
Boolean successFlag = true;
boolean successFlag = true;
if (e != null) {
successFlag = false;
failReason = getExceptionString(e);
@ -210,15 +207,32 @@ public abstract class OperateLogAspect {
.userAgent(user.getUserAgent())
.failReason(failReason)
.successFlag(successFlag).build();
Operation apiOperation = this.getApiOperation(joinPoint);
if (apiOperation != null) {
operateLogEntity.setContent(apiOperation.summary());
}
Tag api = this.getApi(joinPoint);
if (api != null) {
String name = api.name();
operateLogEntity.setModule(StrUtil.join(",", name));
}
// 处理返回值 ResponseDTO
if(responseDTO instanceof ResponseDTO) {
ResponseDTO response = (ResponseDTO) responseDTO;
ResponseDTO logResponseDTO = new ResponseDTO(
response.getCode(),
response.getLevel(),
response.getOk(),
response.getMsg(),
null
);
logResponseDTO.setDataType(response.getDataType());
operateLogEntity.setResponse(JSON.toJSONString(logResponseDTO));
}
taskExecutor.execute(() -> {
this.saveLog(operateLogEntity);
});

View File

@ -71,6 +71,11 @@ public class OperateLogEntity {
*/
private String param;
/**
* 返回值
*/
private String response;
/**
* 客户ip
*/

View File

@ -47,6 +47,9 @@ public class OperateLogVO {
@Schema(description = "请求参数")
private String param;
@Schema(description = "返回值")
private String response;
@Schema(description = "客户ip")
private String ip;

View File

@ -48,7 +48,7 @@
#end
#if($field.frontComponent == "DictSelect")
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<DictSelect width="100%" v-model:value="form.${field.fieldName}" dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
<DictSelect width="100%" v-model:value="form.${field.fieldName}" :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
</a-form-item>
#end
#if($field.frontComponent == "Date")
@ -106,7 +106,7 @@
#end
#if($field.frontComponent == "DictSelect")
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<DictSelect width="100%" v-model:value="form.${field.fieldName}" dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
<DictSelect width="100%" v-model:value="form.${field.fieldName}" :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
</a-form-item>
#end
#if($field.frontComponent == "Date")

View File

@ -22,7 +22,7 @@
#end
#if($field.queryTypeEnum == "Dict")
<a-form-item label="${field.label}" class="smart-query-form-item">
<DictSelect dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="${field.label}" v-model:value="queryForm.${field.fieldName}" width="${field.width}" />
<DictSelect :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="${field.label}" v-model:value="queryForm.${field.fieldName}" width="${field.width}" />
</a-form-item>
#end
#if($field.queryTypeEnum == "Enum")

View File

@ -48,7 +48,7 @@
#end
#if($field.frontComponent == "DictSelect")
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<DictSelect width="100%" v-model:value="form.${field.fieldName}" dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
<DictSelect width="100%" v-model:value="form.${field.fieldName}" :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
</a-form-item>
#end
#if($field.frontComponent == "Date")
@ -106,7 +106,7 @@
#end
#if($field.frontComponent == "DictSelect")
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<DictSelect width="100%" v-model:value="form.${field.fieldName}" dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
<DictSelect width="100%" v-model:value="form.${field.fieldName}" :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
</a-form-item>
#end
#if($field.frontComponent == "Date")

View File

@ -22,7 +22,7 @@
#end
#if($field.queryTypeEnum == "Dict")
<a-form-item label="${field.label}" class="smart-query-form-item">
<DictSelect dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="${field.label}" v-model:value="queryForm.${field.fieldName}" width="${field.width}" />
<DictSelect :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="${field.label}" v-model:value="queryForm.${field.fieldName}" width="${field.width}" />
</a-form-item>
#end
#if($field.queryTypeEnum == "Enum")

View File

@ -26,7 +26,7 @@
AND (INSTR(module,#{query.keywords}) OR INSTR(content,#{query.keywords}))
</if>
<if test="query.requestKeywords != null and query.requestKeywords != ''">
AND (INSTR(url,#{query.requestKeywords}) OR INSTR(method,#{query.requestKeywords}) OR INSTR(param,#{query.requestKeywords}))
AND (INSTR(url,#{query.requestKeywords}) OR INSTR(method,#{query.requestKeywords}) OR INSTR(param,#{query.requestKeywords}) OR INSTR(response,#{query.requestKeywords}))
</if>
<if test="query.successFlag != null">
AND success_flag = #{query.successFlag}

View File

@ -67,7 +67,7 @@ public class AdminInterceptor implements HandlerInterceptor {
Method method = ((HandlerMethod) handler).getMethod();
NoNeedLogin noNeedLogin = ((HandlerMethod) handler).getMethodAnnotation(NoNeedLogin.class);
if (noNeedLogin != null) {
checkActiveTimeout(requestEmployee);
updateActiveTimeout(requestEmployee);
SmartRequestUtil.setRequestUser(requestEmployee);
return true;
}
@ -77,8 +77,8 @@ public class AdminInterceptor implements HandlerInterceptor {
return false;
}
// 检测token 活跃频率
checkActiveTimeout(requestEmployee);
// 更新活跃
updateActiveTimeout(requestEmployee);
// --------------- 第三步 校验 权限 ---------------
@ -123,15 +123,12 @@ public class AdminInterceptor implements HandlerInterceptor {
/**
* 检测token 最低活跃频率单位如果 token 超过此时间没有访问系统就会被冻结
* 更新活跃时间
*/
private void checkActiveTimeout(RequestEmployee requestEmployee) {
// 用户不在线也不用检测
private void updateActiveTimeout(RequestEmployee requestEmployee) {
if (requestEmployee == null) {
return;
}
StpUtil.checkActiveTimeout();
StpUtil.updateLastActiveToNow();
}

View File

@ -81,7 +81,7 @@ public class FileConfig implements WebMvcConfigurer {
StaticCredentialsProvider.create(
AwsBasicCredentials.create(accessKey, secretKey)))
.serviceConfiguration(S3Configuration.builder()
.pathStyleAccessEnabled(false)
.pathStyleAccessEnabled(true)
.chunkedEncodingEnabled(false)
.build())
.build();

View File

@ -106,7 +106,18 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
userMetadata.put(USER_METADATA_FILE_FORMAT, fileType);
userMetadata.put(USER_METADATA_FILE_SIZE, String.valueOf(file.getSize()));
PutObjectRequest putObjectRequest = PutObjectRequest.builder().bucket(cloudConfig.getBucketName()).key(fileKey).metadata(userMetadata).contentLength(file.getSize()).contentType(this.getContentType(fileType)).contentEncoding(StandardCharsets.UTF_8.name()).contentDisposition("attachment;filename=" + urlEncoderFilename).build();
// 根据文件路径获取并设置访问权限
ObjectCannedACL acl = this.getACL(path);
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(cloudConfig.getBucketName())
.key(fileKey)
.metadata(userMetadata)
.contentLength(file.getSize())
.contentType(this.getContentType(fileType))
.contentEncoding(StandardCharsets.UTF_8.name())
.contentDisposition("attachment;filename=" + urlEncoderFilename)
.acl(acl)
.build();
InputStream inputStream = null;
try {
inputStream = file.getInputStream();
@ -117,10 +128,6 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
} finally {
IOUtils.closeQuietly(inputStream);
}
// 根据文件路径获取并设置访问权限
ObjectCannedACL acl = this.getACL(path);
PutObjectAclRequest aclRequest = PutObjectAclRequest.builder().bucket(cloudConfig.getBucketName()).key(fileKey).acl(this.getACL(path)).build();
s3Client.putObjectAcl(aclRequest);
// 返回上传结果
FileUploadVO uploadVO = new FileUploadVO();
uploadVO.setFileName(originalFileName);

View File

@ -3,11 +3,12 @@ package net.lab1024.sa.base.module.support.operatelog.core;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.base.common.constant.StringConst;
import net.lab1024.sa.base.common.domain.RequestUser;
import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.util.SmartIpUtil;
import net.lab1024.sa.base.common.util.SmartRequestUtil;
import net.lab1024.sa.base.module.support.operatelog.OperateLogDao;
@ -20,7 +21,6 @@ import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
@ -36,7 +36,6 @@ import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.BindException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
@ -73,14 +72,14 @@ public abstract class OperateLogAspect {
public void logPointCut() {
}
@AfterReturning(pointcut = "logPointCut()")
public void doAfterReturning(JoinPoint joinPoint) {
handleLog(joinPoint, null);
@AfterReturning(pointcut = "logPointCut()", returning = "responseDTO")
public void doAfterReturning(JoinPoint joinPoint, Object responseDTO) {
handleLog(joinPoint, null, responseDTO);
}
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
handleLog(joinPoint, e);
handleLog(joinPoint, e, null);
}
/**
@ -111,16 +110,15 @@ public abstract class OperateLogAspect {
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
}
protected void handleLog(final JoinPoint joinPoint, final Exception e) {
protected void handleLog(final JoinPoint joinPoint, final Exception e, Object responseDTO) {
try {
OperateLog operateLog = this.getAnnotationLog(joinPoint);
if (operateLog == null) {
return;
}
this.submitLog(joinPoint, e);
this.submitLog(joinPoint, e, responseDTO);
} catch (Exception exp) {
log.error("保存操作日志异常:{}", exp.getMessage());
exp.printStackTrace();
}
}
@ -175,11 +173,8 @@ public abstract class OperateLogAspect {
/**
* 提交存储操作日志
*
* @param joinPoint
* @param e
* @throws Exception
*/
private void submitLog(final JoinPoint joinPoint, final Throwable e) throws Exception {
private void submitLog(final JoinPoint joinPoint, final Throwable e, Object responseDTO) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//设置用户信息
RequestUser user = SmartRequestUtil.getRequestUser();
@ -193,7 +188,7 @@ public abstract class OperateLogAspect {
String methodName = joinPoint.getSignature().getName();
String operateMethod = className + "." + methodName;
String failReason = null;
Boolean successFlag = true;
boolean successFlag = true;
if (e != null) {
successFlag = false;
failReason = getExceptionString(e);
@ -212,15 +207,32 @@ public abstract class OperateLogAspect {
.userAgent(user.getUserAgent())
.failReason(failReason)
.successFlag(successFlag).build();
Operation apiOperation = this.getApiOperation(joinPoint);
if (apiOperation != null) {
operateLogEntity.setContent(apiOperation.summary());
}
Tag api = this.getApi(joinPoint);
if (api != null) {
String name = api.name();
operateLogEntity.setModule(StrUtil.join(",", name));
}
// 处理返回值 ResponseDTO
if(responseDTO instanceof ResponseDTO) {
ResponseDTO response = (ResponseDTO) responseDTO;
ResponseDTO logResponseDTO = new ResponseDTO(
response.getCode(),
response.getLevel(),
response.getOk(),
response.getMsg(),
null
);
logResponseDTO.setDataType(response.getDataType());
operateLogEntity.setResponse(JSON.toJSONString(logResponseDTO));
}
taskExecutor.execute(() -> {
this.saveLog(operateLogEntity);
});

View File

@ -71,6 +71,11 @@ public class OperateLogEntity {
*/
private String param;
/**
* 返回值
*/
private String response;
/**
* 客户ip
*/

View File

@ -47,6 +47,9 @@ public class OperateLogVO {
@Schema(description = "请求参数")
private String param;
@Schema(description = "返回值")
private String response;
@Schema(description = "客户ip")
private String ip;

View File

@ -48,7 +48,7 @@
#end
#if($field.frontComponent == "DictSelect")
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<DictSelect width="100%" v-model:value="form.${field.fieldName}" dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
<DictSelect width="100%" v-model:value="form.${field.fieldName}" :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
</a-form-item>
#end
#if($field.frontComponent == "Date")
@ -106,7 +106,7 @@
#end
#if($field.frontComponent == "DictSelect")
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<DictSelect width="100%" v-model:value="form.${field.fieldName}" dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
<DictSelect width="100%" v-model:value="form.${field.fieldName}" :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
</a-form-item>
#end
#if($field.frontComponent == "Date")

View File

@ -22,7 +22,7 @@
#end
#if($field.queryTypeEnum == "Dict")
<a-form-item label="${field.label}" class="smart-query-form-item">
<DictSelect dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="${field.label}" v-model:value="queryForm.${field.fieldName}" width="${field.width}" />
<DictSelect :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="${field.label}" v-model:value="queryForm.${field.fieldName}" width="${field.width}" />
</a-form-item>
#end
#if($field.queryTypeEnum == "Enum")

View File

@ -48,7 +48,7 @@
#end
#if($field.frontComponent == "DictSelect")
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<DictSelect width="100%" v-model:value="form.${field.fieldName}" dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
<DictSelect width="100%" v-model:value="form.${field.fieldName}" :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
</a-form-item>
#end
#if($field.frontComponent == "Date")
@ -106,7 +106,7 @@
#end
#if($field.frontComponent == "DictSelect")
<a-form-item label="$codeGeneratorTool.removeEnumDesc($!{field.label})" name="${field.fieldName}">
<DictSelect width="100%" v-model:value="form.${field.fieldName}" dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
<DictSelect width="100%" v-model:value="form.${field.fieldName}" :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="$!{field.label}"/>
</a-form-item>
#end
#if($field.frontComponent == "Date")

View File

@ -22,7 +22,7 @@
#end
#if($field.queryTypeEnum == "Dict")
<a-form-item label="${field.label}" class="smart-query-form-item">
<DictSelect dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="${field.label}" v-model:value="queryForm.${field.fieldName}" width="${field.width}" />
<DictSelect :dict-code="DICT_CODE_ENUM.$!{field.dict} || '$!{field.dict}'" placeholder="${field.label}" v-model:value="queryForm.${field.fieldName}" width="${field.width}" />
</a-form-item>
#end
#if($field.queryTypeEnum == "Enum")

View File

@ -26,7 +26,7 @@
AND (INSTR(module,#{query.keywords}) OR INSTR(content,#{query.keywords}))
</if>
<if test="query.requestKeywords != null and query.requestKeywords != ''">
AND (INSTR(url,#{query.requestKeywords}) OR INSTR(method,#{query.requestKeywords}) OR INSTR(param,#{query.requestKeywords}))
AND (INSTR(url,#{query.requestKeywords}) OR INSTR(method,#{query.requestKeywords}) OR INSTR(param,#{query.requestKeywords}) OR INSTR(response,#{query.requestKeywords}))
</if>
<if test="query.successFlag != null">
AND success_flag = #{query.successFlag}

View File

@ -43,7 +43,7 @@
"vue": "3.4.27",
"vue-i18n": "9.13.1",
"vue-router": "4.3.2",
"vue3-json-viewer": "2.2.2"
"vue3-json-viewer": "2.3.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "5.0.4",

View File

@ -51,7 +51,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
<a-modal v-model:open="visibleDiff" width="90%" title="数据比对" :footer="null">

View File

@ -71,7 +71,6 @@
v-model:pageSize="params.pageSize"
:total="total"
@change="queryEmployee"
@showSizeChange="queryEmployee"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -27,7 +27,7 @@ export const appDefaultConfig = {
// 圆角
borderRadius: 6,
// 菜单展开模式
flatPattern: false,
menuSingleExpandFlag: true,
// 标签页
pageTagFlag: true,
// 标签页样式: default、 antd、chrome

View File

@ -21,11 +21,11 @@ export default {
'setting.menu.layout': 'Menu Layout',
'setting.menu.width': 'Menu Width',
'setting.menu.theme': 'Menu Theme',
'setting.menu.expand': 'Menu Expand',
'setting.page.width': 'Page Width',
'setting.border.radius': 'Border Radius',
'setting.compact': 'Page Compact',
'setting.bread': 'Show Bread',
'setting.flatPattern': 'Flat Pattern',
'setting.pagetag': 'Show PageTag',
'setting.pagetag.style': 'PageTag Style',
'setting.footer': 'Show Footer',

View File

@ -21,11 +21,11 @@ export default {
'setting.menu.layout': '菜单布局',
'setting.menu.width': '菜单宽度',
'setting.menu.theme': '菜单主题',
'setting.menu.expand': '菜单展开',
'setting.compact': '页面紧凑',
'setting.border.radius': '页面圆角',
'setting.page.width': '页面宽度',
'setting.bread': '面包屑',
'setting.flatPattern': '菜单展开模式',
'setting.pagetag': '标签页',
'setting.pagetag.style': '标签页样式',
'setting.footer': '页脚',

View File

@ -83,8 +83,8 @@
<a-radio-button value="chrome">Chrome</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item :label="$t('setting.flatPattern')" v-if="formState.layout === LAYOUT_ENUM.SIDE.value">
<a-switch @change="changeFlatPattern" v-model:checked="formState.flatPattern" checked-children="多个" un-checked-children="" />
<a-form-item :label="$t('setting.menu.expand')" v-if="formState.layout === LAYOUT_ENUM.SIDE.value">
<a-switch @change="changeMenuExpandFlag" v-model:checked="formState.menuSingleExpandFlag" checked-children="单个" un-checked-children="" />
</a-form-item>
<a-form-item :label="$t('setting.pagetag')">
<a-switch @change="changePageTagFlag" v-model:checked="formState.pageTagFlag" checked-children="显示" un-checked-children="隐藏" />
@ -213,8 +213,8 @@
borderRadius: appConfigStore.borderRadius,
//
pageTagFlag: appConfigStore.pageTagFlag,
//
flatPattern: appConfigStore.flatPattern,
//
menuSingleExpandFlag: appConfigStore.menuSingleExpandFlag,
//
pageTagStyle: appConfigStore.pageTagStyle,
//
@ -303,9 +303,9 @@
});
}
function changeFlatPattern(e) {
function changeMenuExpandFlag(e) {
appConfigStore.$patch({
flatPattern: e,
menuSingleExpandFlag: e,
});
}

View File

@ -36,7 +36,7 @@
import { useUserStore } from '/@/store/modules/system/user';
const theme = computed(() => useAppConfigStore().$state.sideMenuTheme);
const flatPattern = computed(() => useAppConfigStore().$state.flatPattern);
const menuSingleExpandFlag = computed(() => useAppConfigStore().$state.menuSingleExpandFlag);
const props = defineProps({
collapsed: {
@ -46,8 +46,7 @@
});
const menuTree = computed(() => useUserStore().getMenuTree || []);
const rootSubmenuKeys = computed(()=>menuTree.value.map(item=>item.menuId));
const rootSubmenuKeys = computed(() => menuTree.value.map((item) => item.menuId));
//
let currentRoute = useRoute();
@ -76,9 +75,15 @@
let parentList = menuParentIdListMap.get(currentRoute.name) || [];
// openkey
if (!props.collapsed) {
// 使lodashunion
if (props.collapsed) {
return;
}
let needOpenKeys = _.map(parentList, 'name').map(Number);
if (menuSingleExpandFlag.value) {
openKeys.value = [...needOpenKeys];
} else {
// 使lodashunion
openKeys.value = _.union(openKeys.value, needOpenKeys);
}
}
@ -92,17 +97,18 @@
immediate: true,
}
);
function onOpenChange(openKeysParams) {
if(flatPattern.value){
if (!menuSingleExpandFlag.value) {
return;
}
const latestOpenKey = openKeysParams.find(key => openKeys.value.indexOf(key) === -1);
const latestOpenKey = openKeysParams.find((key) => openKeys.value.indexOf(key) === -1);
if (rootSubmenuKeys.value.indexOf(latestOpenKey) === -1) {
openKeys.value = openKeysParams;
} else {
openKeys.value = latestOpenKey ? [latestOpenKey] : [];
}
};
}
defineExpose({
updateOpenKeysAndSelectKeys,
});

View File

@ -149,7 +149,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -78,7 +78,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -63,7 +63,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryEmployee"
@showSizeChange="queryEmployee"
:show-total="showTableTotal"
/>
</div>

View File

@ -75,7 +75,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -102,7 +102,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -53,7 +53,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryViewRecord"
@showSizeChange="queryViewRecord"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -72,7 +72,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryNoticeList"
@showSizeChange="queryNoticeList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -122,7 +122,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryNoticeList"
@showSizeChange="queryNoticeList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -107,7 +107,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -74,7 +74,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -66,7 +66,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -98,7 +98,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -64,7 +64,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryList"
@showSizeChange="queryList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -103,7 +103,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -79,7 +79,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -88,7 +88,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryHelpDocList"
@showSizeChange="queryHelpDocList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -49,7 +49,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryViewRecord"
@showSizeChange="queryViewRecord"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -117,7 +117,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryJobList"
@showSizeChange="queryJobList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -71,7 +71,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryLogList"
@showSizeChange="queryLogList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -136,7 +136,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryJobList"
@showSizeChange="queryJobList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -102,7 +102,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="onSearch"
@showSizeChange="onSearch"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -89,7 +89,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -47,7 +47,6 @@
v-model:pageSize="queryParam.pageSize"
:total="total"
@change="queryList"
@showSizeChange="queryList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -78,7 +78,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -12,6 +12,10 @@
<div class="info-box">
<a-row class="smart-margin-top10">
<a-col :span="16">
<a-row class="detail-info">
<a-col :span="12"> 用户id{{ detail.operateUserId }}</a-col>
<a-col :span="12"> 用户名称 {{ detail.operateUserName }}</a-col>
</a-row>
<a-row class="detail-info">
<a-col :span="12"> 请求url {{ detail.url }}</a-col>
<a-col :span="12"> 请求日期 {{ detail.createTime }}</a-col>
@ -21,8 +25,7 @@
<a-col :span="12"> IP地区 {{ detail.ipRegion }}</a-col>
</a-row>
<a-row class="detail-info">
<a-col :span="12"> 用户id{{ detail.operateUserId }}</a-col>
<a-col :span="12"> 用户名称 {{ detail.operateUserName }}</a-col>
<a-col :span="12"> 客户端 {{ detail.os }} / {{ detail.browser }} {{ detail.device ? '/' + detail.device : detail.device }}</a-col>
</a-row>
</a-col>
<a-col :span="8">
@ -32,21 +35,26 @@
</a-typography-text>
</a-col>
</a-row>
</div>
<div class="info-box">
<h4>请求明细</h4>
<a-row class="detail-info">
<a-col :span="24"> 方法 {{ detail.method }}</a-col>
</a-row>
<a-row class="detail-info">
<a-col :span="24"> 说明 {{ detail.module }} - {{ detail.content }}</a-col>
</a-row>
</div>
<div class="info-box">
<h4>请求参数</h4>
<JsonViewer :value="detail.param ? JSON.parse(detail.param) : ''" theme="jv-dark" copyable boxed sort />
<JsonViewer :value="detail.param ? JSON.parse(detail.param) : ''" :expanded="true" :expandDepth="10" copyable boxed sort theme="light" />
</div>
<div class="info-box" v-if="detail.successFlag">
<h4>返回结果</h4>
<JsonViewer :value="detail.response ? JSON.parse(detail.response) : ''" :expanded="true" :expandDepth="10" copyable boxed sort theme="light" />
</div>
<div class="info-box" v-if="detail.failReason">
<h4>请求失败原因</h4>
<div>
<a-card>
{{ detail.failReason }}
</div>
</a-card>
</div>
</a-modal>
</template>
@ -57,6 +65,7 @@
import { operateLogApi } from '/@/api/support/operate-log-api';
import { smartSentry } from '/@/lib/smart-sentry';
import { SmartLoading } from '/@/components/framework/smart-loading';
import uaparser from 'ua-parser-js';
defineExpose({
show,
@ -87,11 +96,16 @@
param: '',
url: '',
});
async function getDetail(operateLogId) {
try {
SmartLoading.show();
let res = await operateLogApi.detail(operateLogId);
detail = Object.assign(detail, res.data);
let ua = uaparser(res.data.userAgent);
detail.browser = ua.browser.name;
detail.os = ua.os.name;
detail.device = ua.device.vendor ? ua.device.vendor + ua.device.model : '';
} catch (e) {
smartSentry.captureError(e);
} finally {
@ -107,10 +121,11 @@
font-size: 20px;
font-weight: bold;
}
.info-box {
border-bottom: 1px solid #f0f0f0;
padding: 10px 8px;
}
.detail-info {
.ant-col {
line-height: 1.46;
@ -118,6 +133,7 @@
padding-right: 5px;
}
}
.detail-right-title {
text-align: right;
color: grey;

View File

@ -14,7 +14,7 @@
<a-input style="width: 150px" v-model:value="queryForm.keywords" placeholder="模块/操作内容" />
</a-form-item>
<a-form-item label="请求关键字" class="smart-query-form-item">
<a-input style="width: 220px" v-model:value="queryForm.requestKeywords" placeholder="请求地址/请求方法/请求参数" />
<a-input style="width: 270px" v-model:value="queryForm.requestKeywords" placeholder="请求地址/请求方法/请求参数/返回结果" />
</a-form-item>
<a-form-item label="用户名称" class="smart-query-form-item">
<a-input style="width: 100px" v-model:value="queryForm.userName" placeholder="用户名称" />
@ -24,7 +24,7 @@
<a-range-picker @change="changeCreateDate" v-model:value="createDateRange" :presets="defaultChooseTimeRange" style="width: 240px" />
</a-form-item>
<a-form-item label="快速筛选" class="smart-query-form-item">
<a-form-item label="状态:" class="smart-query-form-item">
<a-radio-group v-model:value="queryForm.successFlag" @change="onSearch">
<a-radio-button :value="undefined">全部</a-radio-button>
<a-radio-button :value="true">成功</a-radio-button>
@ -51,18 +51,23 @@
</a-row>
</a-form>
<a-card size="small" :bordered="false" :hoverable="true" style="height: 100%">
<a-card size="small" :bordered="false" :hoverable="true" >
<a-row justify="end">
<TableOperator class="smart-margin-bottom5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.CONFIG" :refresh="ajaxQuery" />
</a-row>
<a-table size="small" :loading="tableLoading" :dataSource="tableData" :columns="columns" bordered rowKey="operateLogId" :pagination="false">
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'response'">
<a-typography-text v-if="text && text.ok">{{ text ? text.msg : '-' }}</a-typography-text>
<a-typography-text v-else type="warning">{{ text ? text.msg : '-' }}</a-typography-text>
</template>
<template v-if="column.dataIndex === 'successFlag'">
<a-tag :color="text ? 'success' : 'error'">{{ text ? '成功' : '失败' }}</a-tag>
<a-tag :color="text ? 'success' : 'error'">{{ text ? '成功' : '报错' }}</a-tag>
</template>
<template v-if="column.dataIndex === 'userAgent'">
<div>{{ record.browser }} / {{ record.os }} / {{ record.device }}</div>
<div>{{ record.os }} / {{ record.browser }} {{ record.device ? '/' + record.device : record.device }}</div>
</template>
<template v-if="column.dataIndex === 'operateUserType'">
@ -88,7 +93,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>
@ -135,14 +139,15 @@
ellipsis: true,
},
{
title: 'IP',
dataIndex: 'ip',
title: '返回结果',
dataIndex: 'response',
ellipsis: true,
},
{
title: 'IP地区',
dataIndex: 'ipRegion',
ellipsis: true,
width: 150,
},
{
title: '客户端',
@ -150,20 +155,15 @@
ellipsis: true,
},
{
title: '请求方法',
dataIndex: 'method',
ellipsis: true,
},
{
title: '请求结果',
dataIndex: 'successFlag',
width: 80,
},
{
title: '时间',
title: '操作时间',
dataIndex: 'createTime',
width: 150,
},
{
title: '状态',
dataIndex: 'successFlag',
width: 60,
},
{
title: '操作',
dataIndex: 'action',
@ -212,6 +212,10 @@
let responseModel = await operateLogApi.queryList(queryForm);
for (const e of responseModel.data.list) {
if(e.response){
e.response = JSON.parse(e.response);
}
if (!e.userAgent) {
continue;
}

View File

@ -32,7 +32,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -63,7 +63,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -75,7 +75,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -68,7 +68,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -94,7 +94,6 @@
v-model:pageSize="params.pageSize"
:total="total"
@change="queryEmployee"
@showSizeChange="queryEmployee"
:show-total="showTableTotal"
/>
</div>

View File

@ -8,23 +8,24 @@
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
-->
<template>
<a-modal :open="visible" width="600px" :bodyStyle="{height:'480px'}" title="" :closable="false" :maskClosable="true">
<a-row><div style="font-weight:bolder;margin: 0 auto;font-size: 16px">助力卓大抖音1000个粉丝开播写代码🎉🎉</div> </a-row>
<a-row><div style="font-weight:bolder;margin: 20px auto;font-size: 15px">和1024创新实验室一起热爱代码热爱生活永远年轻永远前行🎉🎉</div> </a-row>
<a-modal :open="visible" width="600px" :bodyStyle="{height:'360px'}" title="" :closable="false" :maskClosable="true">
<a-row><div style="font-weight:bolder;margin: 0 auto;font-size: 16px;color: red;">重磅更新国产数据库支持🎉🎉</div> </a-row>
<a-row><div style="font-weight:bolder;margin: 10px auto;font-size: 16px;color: red;">支持达梦人大金仓华为高斯GaussDB 🎉🎉</div> </a-row>
<br />
<div class="app-qr-box">
<div class="app-qr">
<a-image
:width="300"
:width="200"
style="border-radius: 15px;"
src="https://img.smartadmin.1024lab.net/wechat/douyin.png"
src="https://img.smartadmin.1024lab.net/wechat/zhuoda-wechat.jpg"
/>
<span class="qr-desc strong"> 打开抖音APP-点击左上角侧边栏-点击扫一扫-进行关注</span>
<span class="qr-desc strong"> 添加卓大微信备注对应数据库 达梦</span>
</div>
</div>
<template #footer>
<a-button type="primary" @click="hide">知道了</a-button>
<a-button type="default" @click="hide">知道了</a-button>
<a-button danger type="default" @click="goto" target="_blank" href="https://smartadmin.vip/views/other/china-db/">去看看国产数据库了解一下</a-button>
</template>
</a-modal>
</template>
@ -36,6 +37,9 @@ defineExpose({
});
const visible = ref(true);
function goto(){
}
function show() {
visible.value = true;
}
@ -64,8 +68,7 @@ defineExpose({
display: flex;
margin-top: 20px;
align-items: center;
font-size: 13px;
color: red;
font-size: 15px;
text-align: center;
overflow-x: hidden;
> img {

View File

@ -108,7 +108,7 @@
let lunarMonth = lunar.getMonthInChinese();
let lunarDay = lunar.getDayInChinese();
//
let jieqi = lunar.getPrevJieQi().getName();
let jieqi = lunar.getJieQi();
let next = lunar.getNextJieQi();
let nextJieqi = next.getName() + ' ' + next.getSolar().toYmd();

View File

@ -86,7 +86,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -68,7 +68,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryRoleEmployee"
@showSizeChange="queryRoleEmployee"
:show-total="showTableTotal"
/>
</div>

View File

@ -44,7 +44,7 @@
"vue": "3.4.27",
"vue-i18n": "9.13.1",
"vue-router": "4.3.2",
"vue3-json-viewer": "2.2.2"
"vue3-json-viewer": "2.3.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "5.1.4",

View File

@ -51,7 +51,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
<a-modal v-model:open="visibleDiff" width="90%" title="数据比对" :footer="null">

View File

@ -71,7 +71,6 @@
v-model:pageSize="params.pageSize"
:total="total"
@change="queryEmployee"
@showSizeChange="queryEmployee"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -27,7 +27,7 @@ export const appDefaultConfig = {
// 圆角
borderRadius: 6,
// 菜单展开模式
flatPattern: false,
menuSingleExpandFlag: true,
// 标签页
pageTagFlag: true,
// 标签页样式: default、 antd、chrome

View File

@ -21,11 +21,11 @@ export default {
'setting.menu.layout': 'Menu Layout',
'setting.menu.width': 'Menu Width',
'setting.menu.theme': 'Menu Theme',
'setting.menu.expand': 'Menu Expand',
'setting.page.width': 'Page Width',
'setting.border.radius': 'Border Radius',
'setting.compact': 'Page Compact',
'setting.bread': 'Show Bread',
'setting.flatPattern': 'Flat Pattern',
'setting.pagetag': 'Show PageTag',
'setting.pagetag.style': 'PageTag Style',
'setting.footer': 'Show Footer',

View File

@ -21,11 +21,11 @@ export default {
'setting.menu.layout': '菜单布局',
'setting.menu.width': '菜单宽度',
'setting.menu.theme': '菜单主题',
'setting.menu.expand': '菜单展开',
'setting.compact': '页面紧凑',
'setting.border.radius': '页面圆角',
'setting.page.width': '页面宽度',
'setting.bread': '面包屑',
'setting.flatPattern': '菜单展开模式',
'setting.pagetag': '标签页',
'setting.pagetag.style': '标签页样式',
'setting.footer': '页脚',

View File

@ -83,8 +83,8 @@
<a-radio-button value="chrome">Chrome</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item :label="$t('setting.flatPattern')" v-if="formState.layout === LAYOUT_ENUM.SIDE.value">
<a-switch @change="changeFlatPattern" v-model:checked="formState.flatPattern" checked-children="多个" un-checked-children="" />
<a-form-item :label="$t('setting.menu.expand')" v-if="formState.layout === LAYOUT_ENUM.SIDE.value">
<a-switch @change="changeMenuExpandFlag" v-model:checked="formState.menuSingleExpandFlag" checked-children="单个" un-checked-children="" />
</a-form-item>
<a-form-item :label="$t('setting.pagetag')">
<a-switch @change="changePageTagFlag" v-model:checked="formState.pageTagFlag" checked-children="显示" un-checked-children="隐藏" />
@ -213,8 +213,8 @@
borderRadius: appConfigStore.borderRadius,
//
pageTagFlag: appConfigStore.pageTagFlag,
//
flatPattern: appConfigStore.flatPattern,
//
menuSingleExpandFlag: appConfigStore.menuSingleExpandFlag,
//
pageTagStyle: appConfigStore.pageTagStyle,
//
@ -303,9 +303,9 @@
});
}
function changeFlatPattern(e) {
function changeMenuExpandFlag(e) {
appConfigStore.$patch({
flatPattern: e,
menuSingleExpandFlag: e,
});
}

View File

@ -36,7 +36,7 @@
import { useUserStore } from '/@/store/modules/system/user';
const theme = computed(() => useAppConfigStore().$state.sideMenuTheme);
const flatPattern = computed(() => useAppConfigStore().$state.flatPattern);
const menuSingleExpandFlag = computed(() => useAppConfigStore().$state.menuSingleExpandFlag);
const props = defineProps({
collapsed: {
@ -46,8 +46,7 @@
});
const menuTree = computed(() => useUserStore().getMenuTree || []);
const rootSubmenuKeys = computed(()=>menuTree.value.map(item=>item.menuId));
const rootSubmenuKeys = computed(() => menuTree.value.map((item) => item.menuId));
//
let currentRoute = useRoute();
@ -76,9 +75,15 @@
let parentList = menuParentIdListMap.get(currentRoute.name) || [];
// openkey
if (!props.collapsed) {
// 使lodashunion
if (props.collapsed) {
return;
}
let needOpenKeys = _.map(parentList, 'name').map(Number);
if (menuSingleExpandFlag.value) {
openKeys.value = [...needOpenKeys];
} else {
// 使lodashunion
openKeys.value = _.union(openKeys.value, needOpenKeys);
}
}
@ -92,17 +97,18 @@
immediate: true,
}
);
function onOpenChange(openKeysParams) {
if(flatPattern.value){
if (!menuSingleExpandFlag.value) {
return;
}
const latestOpenKey = openKeysParams.find(key => openKeys.value.indexOf(key) === -1);
const latestOpenKey = openKeysParams.find((key) => openKeys.value.indexOf(key) === -1);
if (rootSubmenuKeys.value.indexOf(latestOpenKey) === -1) {
openKeys.value = openKeysParams;
} else {
openKeys.value = latestOpenKey ? [latestOpenKey] : [];
}
};
}
defineExpose({
updateOpenKeysAndSelectKeys,
});

View File

@ -149,7 +149,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -78,7 +78,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -63,7 +63,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryEmployee"
@showSizeChange="queryEmployee"
:show-total="showTableTotal"
/>
</div>

View File

@ -75,7 +75,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -102,7 +102,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -53,7 +53,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryViewRecord"
@showSizeChange="queryViewRecord"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -72,7 +72,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryNoticeList"
@showSizeChange="queryNoticeList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -122,7 +122,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryNoticeList"
@showSizeChange="queryNoticeList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -107,7 +107,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -74,7 +74,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -66,7 +66,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -98,7 +98,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -64,7 +64,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryList"
@showSizeChange="queryList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -103,7 +103,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -79,7 +79,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -88,7 +88,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryHelpDocList"
@showSizeChange="queryHelpDocList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -49,7 +49,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryViewRecord"
@showSizeChange="queryViewRecord"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -117,7 +117,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryJobList"
@showSizeChange="queryJobList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -71,7 +71,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryLogList"
@showSizeChange="queryLogList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -136,7 +136,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryJobList"
@showSizeChange="queryJobList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -102,7 +102,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="onSearch"
@showSizeChange="onSearch"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -89,7 +89,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -47,7 +47,6 @@
v-model:pageSize="queryParam.pageSize"
:total="total"
@change="queryList"
@showSizeChange="queryList"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -78,7 +78,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="queryData"
@showSizeChange="queryData"
:show-total="(total) => `共${total}条`"
/>
</div>

View File

@ -12,6 +12,10 @@
<div class="info-box">
<a-row class="smart-margin-top10">
<a-col :span="16">
<a-row class="detail-info">
<a-col :span="12"> 用户id{{ detail.operateUserId }}</a-col>
<a-col :span="12"> 用户名称 {{ detail.operateUserName }}</a-col>
</a-row>
<a-row class="detail-info">
<a-col :span="12"> 请求url {{ detail.url }}</a-col>
<a-col :span="12"> 请求日期 {{ detail.createTime }}</a-col>
@ -21,8 +25,7 @@
<a-col :span="12"> IP地区 {{ detail.ipRegion }}</a-col>
</a-row>
<a-row class="detail-info">
<a-col :span="12"> 用户id{{ detail.operateUserId }}</a-col>
<a-col :span="12"> 用户名称 {{ detail.operateUserName }}</a-col>
<a-col :span="12"> 客户端 {{ detail.os }} / {{ detail.browser }} {{ detail.device ? '/' + detail.device : detail.device }}</a-col>
</a-row>
</a-col>
<a-col :span="8">
@ -32,21 +35,26 @@
</a-typography-text>
</a-col>
</a-row>
</div>
<div class="info-box">
<h4>请求明细</h4>
<a-row class="detail-info">
<a-col :span="24"> 方法 {{ detail.method }}</a-col>
</a-row>
<a-row class="detail-info">
<a-col :span="24"> 说明 {{ detail.module }} - {{ detail.content }}</a-col>
</a-row>
</div>
<div class="info-box">
<h4>请求参数</h4>
<JsonViewer :value="detail.param ? JSON.parse(detail.param) : ''" theme="jv-dark" copyable boxed sort />
<JsonViewer :value="detail.param ? JSON.parse(detail.param) : ''" :expanded="true" :expandDepth="10" copyable boxed sort theme="light" />
</div>
<div class="info-box" v-if="detail.successFlag">
<h4>返回结果</h4>
<JsonViewer :value="detail.response ? JSON.parse(detail.response) : ''" :expanded="true" :expandDepth="10" copyable boxed sort theme="light" />
</div>
<div class="info-box" v-if="detail.failReason">
<h4>请求失败原因</h4>
<div>
<a-card>
{{ detail.failReason }}
</div>
</a-card>
</div>
</a-modal>
</template>
@ -57,6 +65,7 @@
import { operateLogApi } from '/@/api/support/operate-log-api';
import { smartSentry } from '/@/lib/smart-sentry';
import { SmartLoading } from '/@/components/framework/smart-loading';
import uaparser from 'ua-parser-js';
defineExpose({
show,
@ -87,11 +96,16 @@
param: '',
url: '',
});
async function getDetail(operateLogId) {
try {
SmartLoading.show();
let res = await operateLogApi.detail(operateLogId);
detail = Object.assign(detail, res.data);
let ua = uaparser(res.data.userAgent);
detail.browser = ua.browser.name;
detail.os = ua.os.name;
detail.device = ua.device.vendor ? ua.device.vendor + ua.device.model : '';
} catch (e) {
smartSentry.captureError(e);
} finally {
@ -107,10 +121,11 @@
font-size: 20px;
font-weight: bold;
}
.info-box {
border-bottom: 1px solid #f0f0f0;
padding: 10px 8px;
}
.detail-info {
.ant-col {
line-height: 1.46;
@ -118,6 +133,7 @@
padding-right: 5px;
}
}
.detail-right-title {
text-align: right;
color: grey;

View File

@ -14,7 +14,7 @@
<a-input style="width: 150px" v-model:value="queryForm.keywords" placeholder="模块/操作内容" />
</a-form-item>
<a-form-item label="请求关键字" class="smart-query-form-item">
<a-input style="width: 220px" v-model:value="queryForm.requestKeywords" placeholder="请求地址/请求方法/请求参数" />
<a-input style="width: 270px" v-model:value="queryForm.requestKeywords" placeholder="请求地址/请求方法/请求参数/返回结果" />
</a-form-item>
<a-form-item label="用户名称" class="smart-query-form-item">
<a-input style="width: 100px" v-model:value="queryForm.userName" placeholder="用户名称" />
@ -24,7 +24,7 @@
<a-range-picker @change="changeCreateDate" v-model:value="createDateRange" :presets="defaultChooseTimeRange" style="width: 240px" />
</a-form-item>
<a-form-item label="快速筛选" class="smart-query-form-item">
<a-form-item label="状态:" class="smart-query-form-item">
<a-radio-group v-model:value="queryForm.successFlag" @change="onSearch">
<a-radio-button :value="undefined">全部</a-radio-button>
<a-radio-button :value="true">成功</a-radio-button>
@ -51,18 +51,23 @@
</a-row>
</a-form>
<a-card size="small" :bordered="false" :hoverable="true" style="height: 100%">
<a-card size="small" :bordered="false" :hoverable="true" >
<a-row justify="end">
<TableOperator class="smart-margin-bottom5" v-model="columns" :tableId="TABLE_ID_CONST.SUPPORT.CONFIG" :refresh="ajaxQuery" />
</a-row>
<a-table size="small" :loading="tableLoading" :dataSource="tableData" :columns="columns" bordered rowKey="operateLogId" :pagination="false">
<template #bodyCell="{ text, record, column }">
<template v-if="column.dataIndex === 'response'">
<a-typography-text v-if="text && text.ok">{{ text ? text.msg : '-' }}</a-typography-text>
<a-typography-text v-else type="warning">{{ text ? text.msg : '-' }}</a-typography-text>
</template>
<template v-if="column.dataIndex === 'successFlag'">
<a-tag :color="text ? 'success' : 'error'">{{ text ? '成功' : '失败' }}</a-tag>
<a-tag :color="text ? 'success' : 'error'">{{ text ? '成功' : '报错' }}</a-tag>
</template>
<template v-if="column.dataIndex === 'userAgent'">
<div>{{ record.browser }} / {{ record.os }} / {{ record.device }}</div>
<div>{{ record.os }} / {{ record.browser }} {{ record.device ? '/' + record.device : record.device }}</div>
</template>
<template v-if="column.dataIndex === 'operateUserType'">
@ -88,7 +93,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>
@ -135,14 +139,15 @@
ellipsis: true,
},
{
title: 'IP',
dataIndex: 'ip',
title: '返回结果',
dataIndex: 'response',
ellipsis: true,
},
{
title: 'IP地区',
dataIndex: 'ipRegion',
ellipsis: true,
width: 150,
},
{
title: '客户端',
@ -150,20 +155,15 @@
ellipsis: true,
},
{
title: '请求方法',
dataIndex: 'method',
ellipsis: true,
},
{
title: '请求结果',
dataIndex: 'successFlag',
width: 80,
},
{
title: '时间',
title: '操作时间',
dataIndex: 'createTime',
width: 150,
},
{
title: '状态',
dataIndex: 'successFlag',
width: 60,
},
{
title: '操作',
dataIndex: 'action',
@ -212,6 +212,10 @@
let responseModel = await operateLogApi.queryList(queryForm);
for (const e of responseModel.data.list) {
if(e.response){
e.response = JSON.parse(e.response);
}
if (!e.userAgent) {
continue;
}

View File

@ -32,7 +32,6 @@
v-model:pageSize="queryForm.pageSize"
:total="total"
@change="ajaxQuery"
@showSizeChange="ajaxQuery"
:show-total="(total) => `共${total}条`"
/>
</div>

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