Compare commits

...

33 Commits

Author SHA1 Message Date
c8e33d43cb 体验地址更改 2025-06-13 13:56:47 +08:00
6c6af870ae jeecgboot vs 敲敲云 2025-06-13 13:53:33 +08:00
79107599a6 增加jeecgboot vs 敲敲云 说明 2025-06-13 13:51:56 +08:00
b6e8b37aee 禁用不好使 2025-06-12 17:00:35 +08:00
195ddd0421 降低shiro版本号,导致不兼容jdk8 2025-06-11 22:18:48 +08:00
9ddad931ff 【issues/8374】分页始终显示在底部 2025-06-10 09:52:27 +08:00
fc05fe1aff 补充.npmrc 2025-06-09 09:22:21 +08:00
7e325f68ca Merge pull request #8389 from 1298191366/master
open api功能优化
2025-06-06 15:22:11 +08:00
f74da23d06 升级积木报表到最新版2.0.0 2025-06-06 11:21:50 +08:00
e279915ba2 --author:wangshuai---date:2025-06-06---for:【QQYUN-12667】open api功能优化--- 2025-06-06 09:54:55 +08:00
bb6f077a95 Revert "//update---author:wangshuai---date:2025-06-05---for:open api功能优化:ak接口返回未明确提示错误---"
This reverts commit 7a031e6135.
2025-06-06 09:35:43 +08:00
7a031e6135 //update---author:wangshuai---date:2025-06-05---for:open api功能优化:ak接口返回未明确提示错误--- 2025-06-06 09:35:27 +08:00
157877f9a6 【issues/8233】resetFields时无法重置 2025-06-05 09:34:54 +08:00
25a71fa66c 【issues/8232】代码设置JSelectDept组件值没翻译 2025-06-05 09:34:11 +08:00
fdc02fa68a [-- author: liusq---date:20250604--for: issue【日志管理】的异常日志列表显示不正常 #8295 --] 2025-06-05 09:33:24 +08:00
ec3c34969a ---author:chenrui---date:2025/6/4-----for:[issues/8309]系统监控>请求追踪,列表每刷新一下,总数据就减一#8309 --- 2025-06-05 09:31:32 +08:00
5a215525d5 调整aksk测试数据 2025-05-29 16:07:30 +08:00
450b93d916 Merge pull request #8358 from xlh12306/master
使用原生方式对接口进行测试 修复授权管理页面重置AK,SK未写入到数据库中的异常
2025-05-29 15:56:13 +08:00
2d62bad2a9 使用原生方式对接口进行测试 修复授权管理页面重置AK,SK未写入到数据库中的异常
使用原生方式对接口进行测试
修复授权管理页面重置AK,SK未写入到数据库中的异常
2025-05-28 22:22:39 +08:00
0b10096f1c 修复微服务启动失败报错 2025-05-28 15:49:56 +08:00
431ddb8fcb 系统监控的头两个tab不好使,接口404--- 2025-05-27 22:51:25 +08:00
ddf0f61ae5 修复v3.8.0 存在绕过sql黑名单限制sql注入漏洞 #8335
修复minidao `getQueryTableInfo`无法解析带括号的表名、增加数据库名解析能力
2025-05-27 18:40:34 +08:00
4042579167 提供更新的数据库脚本,加入openapi的表和放开AI流程设计菜单 2025-05-27 09:51:52 +08:00
bd5fda5968 访问Swagger接口不带doc.html后缀,会丢失项目前缀/jeecg-boot/导致测试接口,返回下载文件 2025-05-26 19:03:29 +08:00
fdbd9c30ac 解决Swagger3Config 接口文档参数显示问题 #8325 2025-05-25 22:49:07 +08:00
b8b4d3f29d JVxetable升级到最新版 2025-05-25 18:09:19 +08:00
78212aa7c0 通讯录手机号码没有显示出来 #8282 2025-05-25 16:53:33 +08:00
6dc3c6af2a nacos增加aigc配置 2025-05-22 11:01:06 +08:00
b7a6812140 更新pom.xml,添加jeecg-boot-module-airag依赖 2025-05-22 09:25:39 +08:00
7efc51e30e 更新README.md,修改新手指南和在线体验部分的描述 2025-05-19 14:14:18 +08:00
bd83b994bc Merge pull request #8298 from xlh12306/master
openapi相关测试项修改
2025-05-18 17:01:28 +08:00
8fb81f331c 提交openAPiModel 2025-05-16 23:13:11 +08:00
fb188a83a1 提交openapi单元测试代码
调整接口管理的字段位置
授权管理删掉无用配置
授权管理编辑改为重置生成ak sk功能
授权页面应该改成跟角色授权类型的效果
2025-05-16 23:12:28 +08:00
44 changed files with 1397 additions and 361 deletions

View File

@ -68,14 +68,6 @@ JeecgBoot 是一个开源低代码开发平台,支持全信创环境。它兼
技术文档
-----------------------------------
- 官方网站: [http://www.jeecg.com](http://www.jeecg.com)
- 在线演示 [平台演示](http://boot3.jeecg.com) | [APP演示](http://jeecg.com/appIndex) | [体验低代码](https://jeecg.blog.csdn.net/article/details/106079007) | [体验零代码](https://app.qiaoqiaoyun.com/myapps/index)
- 开发文档: [文档中心](https://help.jeecg.com) | [AIGC大模块](https://help.jeecg.com/aigc)
- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart) | [入门视频](http://jeecg.com/doc/video) | [如何反馈问题](https://github.com/jeecgboot/JeecgBoot/issues/new?template=bug_report.md)
- QQ交流群 ⑩716488839、⑨808791225(满)、其他(满)
@ -87,6 +79,29 @@ JeecgBoot 是一个开源低代码开发平台,支持全信创环境。它兼
在线体验
-----------------------------------
> JeecgBoot vs 敲敲云
> - JeecgBoot是低代码产品拥有很多低代码能力比如流程设计、表单设计、大屏设计代码生成器适合半开发模式开发+低代码结合),也可以集成零代码的应用管理模块;
> - 敲敲云是零代码产品完全不写代码通过配置搭建业务系统其在jeecgboot基础上研发而成删除了online、代码生成、OA等很多需要编码的功能只保留了应用管理和聊天、流程、日程、文件四个标准OA功能
- JeecgBoot低代码 https://boot3.jeecg.com
- 敲敲云零代码https://app.qiaoqiaoyun.com
- APP演示: http://jeecg.com/appIndex
技术文档
-----------------------------------
- 官方网站: [http://www.jeecg.com](http://www.jeecg.com)
- 开发文档: [文档中心](https://help.jeecg.com) | [AIGC大模块](https://help.jeecg.com/aigc) | [低代码初体验一分钟](https://jeecg.blog.csdn.net/article/details/106079007)
- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart) | [入门视频](http://jeecg.com/doc/video) | [反馈问题](https://github.com/jeecgboot/JeecgBoot/issues/new?template=bug_report.md)
- QQ交流群 ⑩716488839、⑨808791225(满)、其他(满)
AIGC应用平台介绍
-----------------------------------
@ -105,10 +120,7 @@ JeecgBoot 平台的AIGC功能模块是一套类似`Dify`的`AIGC应用开发
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecg_aivideo.png)](https://www.bilibili.com/video/BV1zmd7YFE4w)
##### 在线体验
- JeecgBoot演示 https://boot3.jeecg.com
- 敲敲云在线搭建AI知识库https://app.qiaoqiaoyun.com
##### Dify `VS` JEECG AI

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -36,7 +36,6 @@ public class RestUtil {
}
return domain;
}
private static String getPath() {
if (path == null) {
path = SpringContextUtils.getApplicationContext().getEnvironment().getProperty("server.servlet.context-path");

View File

@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgSqlInjectionException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
@ -17,6 +18,12 @@ import java.util.regex.Pattern;
*/
@Slf4j
public class SqlInjectionUtil {
/**
* sql注入黑名单数据库名
*/
public final static String XSS_STR_TABLE = "peformance_schema|information_schema";
/**
* 默认—sql注入关键词
*/
@ -168,6 +175,27 @@ public class SqlInjectionUtil {
return false;
}
/**
* 判断是否存在SQL注入关键词字符串
*
* @param keyword
* @return
*/
@SuppressWarnings("AlibabaUndefineMagicConstant")
private static boolean isExistSqlInjectTableKeyword(String sql, String keyword) {
// 需要匹配的sql注入关键词
String[] matchingTexts = new String[]{"`" + keyword, "(" + keyword, "(`" + keyword};
for (String matchingText : matchingTexts) {
String[] checkTexts = new String[]{" " + matchingText, "from" + matchingText};
for (String checkText : checkTexts) {
if (sql.contains(checkText)) {
return true;
}
}
}
return false;
}
/**
* sql注入过滤处理遇到注入关键字抛异常
*
@ -208,6 +236,14 @@ public class SqlInjectionUtil {
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
String[] xssTableArr = XSS_STR_TABLE.split("\\|");
for (String xssTableStr : xssTableArr) {
if (isExistSqlInjectTableKeyword(value, xssTableStr)) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssTableStr);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
// 三、SQL注入检测存在绕过风险 (正则校验)
for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {
@ -244,6 +280,14 @@ public class SqlInjectionUtil {
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
String[] xssTableArr = XSS_STR_TABLE.split("\\|");
for (String xssTableStr : xssTableArr) {
if (isExistSqlInjectTableKeyword(value, xssTableStr)) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssTableStr);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
// 三、SQL注入检测存在绕过风险 (正则校验)
for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {

View File

@ -1,6 +1,6 @@
package org.jeecg.common.util.encryption;
import org.apache.shiro.lang.codec.Base64;
import org.apache.shiro.codec.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;

View File

@ -3,6 +3,8 @@ package org.jeecg.common.util.security;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.exception.JeecgSqlInjectionException;
import org.jeecg.common.util.SqlInjectionUtil;
import org.jeecg.common.util.oConvertUtils;
import java.util.*;
import java.util.regex.Matcher;
@ -66,6 +68,8 @@ public abstract class AbstractQueryBlackListHandler {
if(flag == false){
return false;
}
Set<String> xssTableSet = new HashSet<>(Arrays.asList(SqlInjectionUtil.XSS_STR_TABLE.split("\\|")));
for (QueryTable table : list) {
String name = table.getName();
String fieldRule = ruleMap.get(name);
@ -81,6 +85,16 @@ public abstract class AbstractQueryBlackListHandler {
}
}
// 判断是否调用了黑名单数据库
String dbName = table.getDbName();
if (oConvertUtils.isNotEmpty(dbName)) {
dbName = dbName.toLowerCase().trim();
if (xssTableSet.contains(dbName)) {
flag = false;
log.warn("sql黑名单校验数据库【" + dbName + "】禁止查询");
break;
}
}
}
// 返回黑名单校验结果(不合法直接抛出异常)
@ -135,6 +149,8 @@ public abstract class AbstractQueryBlackListHandler {
* 查询的表的信息
*/
protected class QueryTable {
//数据库名
private String dbName;
//表名
private String name;
//表的别名
@ -158,6 +174,14 @@ public abstract class AbstractQueryBlackListHandler {
this.fields.add(field);
}
public String getDbName() {
return dbName;
}
public void setDbName(String dbName) {
this.dbName = dbName;
}
public String getName() {
return name;
}

View File

@ -11,17 +11,16 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.EventListener;
import org.springframework.http.CacheControl;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
@ -59,6 +58,14 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired(required = false)
private PrometheusMeterRegistry prometheusMeterRegistry;
/**
* meterRegistryPostProcessor
* for [QQYUN-12558]【监控】系统监控的头两个tab不好使接口404
*/
@Autowired(required = false)
@Qualifier("meterRegistryPostProcessor")
private BeanPostProcessor meterRegistryPostProcessor;
/**
* 静态资源的配置 - 使得可以从磁盘中读取 Html、图片、视频、音频等
*/
@ -147,12 +154,17 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
/**
* 解决metrics端点不显示jvm信息的问题(zyf)
* 监听应用启动完成事件,确保 PrometheusMeterRegistry 已经初始化
* for [QQYUN-12558]【监控】系统监控的头两个tab不好使接口404
* @param event
* @author chenrui
* @date 2025/5/26 16:46
*/
@Bean
@ConditionalOnBean(name = "meterRegistryPostProcessor")
InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor) {
return () -> meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
@EventListener
public void onApplicationReady(ApplicationReadyEvent event) {
if(null != meterRegistryPostProcessor){
meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
}
}
// /**

View File

@ -1,2 +1,3 @@
springdoc.auto-tag-classes: false
springdoc.packages-to-scan: org.jeecg
springdoc.default-flat-param-object: true

View File

@ -24,6 +24,12 @@
<artifactId>hibernate-re</artifactId>
</dependency>
<!-- AI大模型管理 -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-module-airag</artifactId>
<version>${jeecgboot.version}</version>
</dependency>
<!-- 企业微信/钉钉 api -->
<dependency>
<groupId>org.jeecgframework</groupId>

View File

@ -19,6 +19,21 @@ public class CustomInMemoryHttpTraceRepository extends InMemoryHttpTraceReposito
return super.findAll();
}
/**
* for [issues/8309]系统监控>请求追踪,列表每刷新一下,总数据就减一#8309
* @param trace
* @author chenrui
* @date 2025/6/4 19:38
*/
@Override
public void add(HttpTrace trace) {
// 只有当请求不是OPTIONS方法并且URI不包含httptrace时才记录数据
if (!"OPTIONS".equals(trace.getRequest().getMethod()) &&
!trace.getRequest().getUri().toString().contains("httptrace")) {
super.add(trace);
}
}
public List<HttpTrace> findAll(String query) {
List<HttpTrace> allTrace = super.findAll();
if (null != allTrace && !allTrace.isEmpty()) {

View File

@ -162,7 +162,7 @@ public class OpenApiController extends JeecgController<OpenApi, OpenApiService>
String method = openApi.getRequestMethod();
String appkey = request.getHeader("appkey");
OpenApiAuth openApiAuth = openApiAuthService.getByAppkey(appkey);
SysUser systemUser = sysUserService.getById(openApiAuth.getSystemUserId());
SysUser systemUser = sysUserService.getUserByName(openApiAuth.getCreateBy());
String token = this.getToken(systemUser.getUsername(), systemUser.getPassword());
httpHeaders.put("X-Access-Token", Lists.newArrayList(token));
httpHeaders.put("Content-Type",Lists.newArrayList("application/json"));

View File

@ -1,35 +1,22 @@
package org.jeecg.modules.openapi.controller;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
import org.jeecg.modules.openapi.service.OpenApiPermissionService;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/openapi/permission")
public class OpenApiPermissionController extends JeecgController<OpenApiPermission, OpenApiPermissionService> {
@PostMapping("add")
public Result add(@RequestBody OpenApiPermission openApiPermission) {
List<String> list = Arrays.asList(openApiPermission.getApiId().split(","));
if (CollectionUtil.isNotEmpty(list)) {
list.forEach(l->{
OpenApiPermission saveApiPermission = new OpenApiPermission();
saveApiPermission.setApiId(l);
saveApiPermission.setApiAuthId(openApiPermission.getApiAuthId());
service.save(saveApiPermission);
});
}
service.add(openApiPermission);
return Result.ok("保存成功");
}
@GetMapping("/list")
public Result list( String apiAuthId) {
return Result.ok(service.list(Wrappers.<OpenApiPermission>lambdaQuery().eq(OpenApiPermission::getApiAuthId,apiAuthId)));
@GetMapping("/getOpenApi")
public Result<?> getOpenApi( String apiAuthId) {
return service.getOpenApi(apiAuthId);
}
}

View File

@ -97,4 +97,9 @@ public class OpenApi implements Serializable {
* 更新时间
*/
private Date updateTime;
/**
* 历史已选接口
*/
@TableField(exist = false)
private String ifCheckBox = "0";
}

View File

@ -1,6 +1,8 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.openapi.entity.OpenApi;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
import java.util.List;
@ -10,4 +12,8 @@ import java.util.List;
*/
public interface OpenApiPermissionService extends IService<OpenApiPermission> {
List<OpenApiPermission> findByAuthId(String authId);
Result<?> getOpenApi(String apiAuthId);
void add(OpenApiPermission openApiPermission);
}

View File

@ -1,22 +1,67 @@
package org.jeecg.modules.openapi.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.openapi.entity.OpenApi;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
import org.jeecg.modules.openapi.mapper.OpenApiPermissionMapper;
import org.jeecg.modules.openapi.service.OpenApiPermissionService;
import org.jeecg.modules.openapi.service.OpenApiService;
import org.springframework.stereotype.Service;
import java.util.Collections;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @date 2024/12/19 17:44
*/
@Service
public class OpenApiPermissionServiceImpl extends ServiceImpl<OpenApiPermissionMapper, OpenApiPermission> implements OpenApiPermissionService {
@Resource
private OpenApiService openApiService;
@Override
public List<OpenApiPermission> findByAuthId(String authId) {
return baseMapper.selectList(Wrappers.lambdaQuery(OpenApiPermission.class).eq(OpenApiPermission::getApiAuthId, authId));
}
@Override
public Result<?> getOpenApi(String apiAuthId) {
List<OpenApi> openApis = openApiService.list();
if (CollectionUtil.isEmpty(openApis)) {
return Result.error("接口不存在");
}
List<OpenApiPermission> openApiPermissions = baseMapper.selectList(Wrappers.<OpenApiPermission>lambdaQuery().eq(OpenApiPermission::getApiAuthId, apiAuthId));
if (CollectionUtil.isNotEmpty(openApiPermissions)) {
Map<String, OpenApi> openApiMap = openApis.stream().collect(Collectors.toMap(OpenApi::getId, o -> o));
for (OpenApiPermission openApiPermission : openApiPermissions) {
OpenApi openApi = openApiMap.get(openApiPermission.getApiId());
if (openApi!=null) {
openApi.setIfCheckBox("1");
}
}
}
return Result.ok(openApis);
}
@Override
public void add(OpenApiPermission openApiPermission) {
this.remove(Wrappers.<OpenApiPermission>lambdaQuery().eq(OpenApiPermission::getApiAuthId, openApiPermission.getApiAuthId()));
List<String> list = Arrays.asList(openApiPermission.getApiId().split(","));
if (CollectionUtil.isNotEmpty(list)) {
list.forEach(l->{
if (StrUtil.isNotEmpty(l)){
OpenApiPermission saveApiPermission = new OpenApiPermission();
saveApiPermission.setApiId(l);
saveApiPermission.setApiAuthId(openApiPermission.getApiAuthId());
this.save(saveApiPermission);
}
});
}
}
}

View File

@ -25,13 +25,6 @@
<version>${jeecgboot.version}</version>
</dependency>
<!-- AI大模型管理 -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-module-airag</artifactId>
<version>${jeecgboot.version}</version>
</dependency>
<!-- flyway 数据库自动升级 -->
<dependency>
<groupId>org.flywaydb</groupId>

View File

@ -40,8 +40,8 @@ public class JeecgSystemApplication extends SpringBootServletInitializer {
String path = oConvertUtils.getString(env.getProperty("server.servlet.context-path"));
log.info("\n----------------------------------------------------------\n\t" +
"Application Jeecg-Boot is running! Access URLs:\n\t" +
"Local: \t\thttp://localhost:" + port + path + "/\n\t" +
"External: \thttp://" + ip + ":" + port + path + "/\n\t" +
"Local: \t\thttp://localhost:" + port + path + "/doc.html\n\t" +
"External: \thttp://" + ip + ":" + port + path + "/doc.html\n\t" +
"Swagger文档: \thttp://" + ip + ":" + port + path + "/doc.html\n" +
"----------------------------------------------------------");

View File

@ -0,0 +1,95 @@
package org.jeecg.modules.openapi.test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.*;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.Test;
import java.security.MessageDigest;
public class SampleOpenApiTest {
private final String base_url = "http://localhost:8080/jeecg-boot";
private final String appKey = "ak-pFjyNHWRsJEFWlu6";
private final String searchKey = "4hV5dBrZtmGAtPdbA5yseaeKRYNpzGsS";
@Test
public void test() throws Exception {
// 根据部门ID查询用户
String url = base_url+"/openapi/call/TEwcXBlr?id=6d35e179cd814e3299bd588ea7daed3f";
JSONObject header = genTimestampAndSignature();
HttpGet httpGet = new HttpGet(url);
// 设置请求头
httpGet.setHeader("Content-Type", "application/json");
httpGet.setHeader("appkey",appKey);
httpGet.setHeader("signature",header.get("signature").toString());
httpGet.setHeader("timestamp",header.get("timestamp").toString());
try (CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(httpGet);) {
// 获取响应状态码
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("[debug] 响应状态码: " + statusCode);
HttpEntity entity = response.getEntity();
System.out.println(entity);
// 获取响应内容
String responseBody = EntityUtils.toString(response.getEntity());
System.out.println("[debug] 响应内容: " + responseBody);
// 解析JSON响应
JSONObject res = JSON.parseObject(responseBody);
//错误日志判断
if(res.containsKey("success")){
Boolean success = res.getBoolean("success");
if(success){
System.out.println("[info] 调用成功: " + res.toJSONString());
}else{
System.out.println("[error] 调用失败: " + res.getString("message"));
}
}else{
System.out.println("[error] 调用失败: " + res.getString("message"));
}
}
}
private JSONObject genTimestampAndSignature(){
JSONObject jsonObject = new JSONObject();
long timestamp = System.currentTimeMillis();
jsonObject.put("timestamp",timestamp);
jsonObject.put("signature", md5(appKey + searchKey + timestamp));
return jsonObject;
}
/**
* 生成md5
* @param sourceStr
* @return
*/
protected String md5(String sourceStr) {
String result = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(sourceStr.getBytes("utf-8"));
byte[] hash = md.digest();
int i;
StringBuffer buf = new StringBuffer(32);
for (int offset = 0; offset < hash.length; offset++) {
i = hash[offset];
if (i < 0) {
i += 256;
}
if (i < 16) {
buf.append("0");
}
buf.append(Integer.toHexString(i));
}
result = buf.toString();
} catch (Exception e) {
throw new RuntimeException("sign签名错误", e);
}
return result;
}
}

View File

@ -56,8 +56,8 @@
<dm8.version>8.1.1.49</dm8.version>
<!-- 积木报表-->
<jimureport-spring-boot-starter.version>1.9.5.1</jimureport-spring-boot-starter.version>
<minidao.version>1.10.7</minidao.version>
<jimureport-spring-boot-starter.version>2.0.0</jimureport-spring-boot-starter.version>
<minidao.version>1.10.10</minidao.version>
<!-- 持久层 -->
<mybatis-plus.version>3.5.3.2</mybatis-plus.version>
<dynamic-datasource-spring-boot-starter.version>4.1.3</dynamic-datasource-spring-boot-starter.version>
@ -68,7 +68,7 @@
<aliyun-java-sdk-dysmsapi.version>2.1.0</aliyun-java-sdk-dysmsapi.version>
<aliyun.oss.version>3.11.2</aliyun.oss.version>
<!-- shiro -->
<shiro.version>2.0.4</shiro.version>
<shiro.version>1.13.0</shiro.version>
<shiro-redis.version>3.2.3</shiro-redis.version>
<java-jwt.version>4.5.0</java-jwt.version>
<codegenerate.version>1.4.9</codegenerate.version>

View File

@ -2,7 +2,6 @@ node_modules
.DS_Store
.github
dist
.npmrc
.cache
tests/server/static

2
jeecgboot-vue3/.npmrc Normal file
View File

@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false

View File

@ -78,8 +78,9 @@
"vue-router": "^4.5.0",
"vue-types": "^5.1.3",
"vuedraggable": "^4.1.0",
"vxe-table": "4.6.17",
"vxe-table-plugin-antd": "4.0.7",
"vxe-table": "4.13.31",
"vxe-table-plugin-antd": "4.0.8",
"vxe-pc-ui": "4.6.12",
"xe-utils": "3.5.26",
"xss": "^1.0.15"
},

View File

@ -1,6 +1,6 @@
<template>
<a-upload name="file" :showUploadList="false" :customRequest="(file) => onClick(file)">
<Button :type="type" :class="getButtonClass">
<Button :type="type" :class="getButtonClass" :disabled="props.disabled">
<template #default="data">
<Icon :icon="preIcon" v-if="preIcon" :size="iconSize" />
<slot v-bind="data || {}"></slot>

View File

@ -51,7 +51,7 @@
//下拉框选项值
const selectOptions = ref<SelectValue>([]);
//下拉框选中值
let selectValues = reactive<object>({
let selectValues = reactive<any>({
value: [],
change: false,
});
@ -74,7 +74,15 @@
watch(
() => props.value,
() => {
props.value && initValue();
if (props.value) {
initValue();
} else {
// update-begin--author:liaozhiyang---date:20250604---for【issues/8233】resetFields时无法重置
if (selectValues.value?.length) {
selectValues.value = [];
}
// update-end--author:liaozhiyang---date:20250604---for【issues/8233】resetFields时无法重置
}
},
{ deep: true, immediate: true }
);

View File

@ -2,6 +2,7 @@ import type { Ref } from 'vue';
import { inject, reactive, ref, computed, unref, watch, nextTick } from 'vue';
import { TreeActionType } from '/@/components/Tree';
import { listToTree } from '/@/utils/common/compUtils';
import { isEqual } from 'lodash-es';
export function useTreeBiz(treeRef, getList, props, realProps, emit) {
//接收下拉框选项
@ -22,7 +23,7 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
const getCheckStrictly = computed(() => (realProps.multiple ? props.checkStrictly : true));
// 是否是首次加载回显,只有首次加载,才会显示 loading
let isFirstLoadEcho = true;
let prevSelectValues = [];
/**
* 监听selectValues变化
*/
@ -32,12 +33,17 @@ export function useTreeBiz(treeRef, getList, props, realProps, emit) {
if(!values){
return;
}
if (openModal.value == false && values.length > 0) {
// update-begin--author:liaozhiyang---date:20250604---for【issues/8232】代码设置JSelectDept组件值没翻译
if (values.length > 0) {
// 防止多次请求
if (isEqual(values, prevSelectValues)) return;
prevSelectValues = values;
loadingEcho.value = isFirstLoadEcho;
isFirstLoadEcho = false;
onLoadData(null, values.join(',')).finally(() => {
loadingEcho.value = false;
});
// update-end--author:liaozhiyang---date:20250604---for【issues/8232】代码设置JSelectDept组件值没翻译
}
},
{ immediate: true }

View File

@ -222,7 +222,7 @@
// update-end--author:sunjianlei---date:220230630---for【QQYUN-5571】自封装选择列解决数据行选择卡顿问题
);
const { getScrollRef, redoHeight } = useTableScroll(getProps, tableElRef, getColumnsRef, getRowSelectionRef, getDataSourceRef, slots);
const { getScrollRef, redoHeight } = useTableScroll(getProps, tableElRef, getColumnsRef, getRowSelectionRef, getDataSourceRef, slots, getPaginationInfo);
const { customRow } = useCustomRow(getProps, {
setSelectedRowKeys,

View File

@ -15,7 +15,8 @@ export function useTableScroll(
columnsRef: ComputedRef<BasicColumn[]>,
rowSelectionRef: ComputedRef<TableRowSelection<any> | null>,
getDataSourceRef: ComputedRef<Recordable[]>,
slots: Slots
slots: Slots,
getPaginationInfo: ComputedRef<any>
) {
const tableHeightRef: Ref<Nullable<number>> = ref(null);
@ -144,6 +145,27 @@ export function useTableScroll(
setHeight(height);
bodyEl!.style.height = `${height}px`;
// update-begin--author:liaozhiyang---date:20240609---for【issues/8374】分页始终显示在底部
if (maxHeight === undefined) {
if (unref(getPaginationInfo) && unref(getDataSourceRef).length) {
const pageSize = unref(getPaginationInfo)?.pageSize;
const current = unref(getPaginationInfo)?.current;
const total = unref(getPaginationInfo)?.total;
const tableBody = tableEl.querySelector('.ant-table-body') as HTMLElement;
const tr = tableEl.querySelector('.ant-table-tbody')?.children ?? [];
const lastrEl = tr[tr.length - 1] as HTMLElement;
const trHeight = lastrEl.offsetHeight;
const dataHeight = trHeight * pageSize;
if (tableBody && lastrEl) {
if (current === 1 && pageSize > unref(getDataSourceRef).length && total <= pageSize) {
tableBody.style.height = `${height}px`;
} else {
tableBody.style.height = `${dataHeight < height ? dataHeight : height}px`;
}
}
}
}
// update-end--author:liaozhiyang---date:20240609---for【issues/8374】分页始终显示在底部
}
useWindowSizeFn(calcTableHeight, 280);
onMountedOrActivated(() => {

View File

@ -28,7 +28,9 @@ export function useColumns(props: JVxeTableProps, data: JVxeDataProps, methods:
// update-begin--author:liaozhiyang---date:20250403---for【issues/7812】linkageConfig改变了vxetable没更新
// linkageConfig变化时也需要执行
const linkageConfig = toRaw(props.linkageConfig);
console.log(linkageConfig);
if (linkageConfig) {
// console.log(linkageConfig);
}
// update-end--author:liaozhiyang---date:20250403---for【issues/7812】linkageConfig改变了vxetable没更新
let columns: JVxeColumn[] = [];
if (isArray(props.columns)) {

View File

@ -18,10 +18,10 @@ export function useData(props: JVxeTableProps): JVxeDataProps {
// rowId: props.rowKey,
rowConfig: {
keyField: props.rowKey,
// 高亮hover的行
isHover: true,
},
// update-end--author:liaozhiyang---date:20240607---for【TV360X-327】vxetable警告
// 高亮hover的行
highlightHoverRow: true,
// --- 【issues/209】自带的tooltip会错位所以替换成原生的title ---
// 溢出隐藏并显示tooltip
@ -43,6 +43,7 @@ export function useData(props: JVxeTableProps): JVxeDataProps {
expandConfig: {
iconClose: 'vxe-icon-arrow-right',
iconOpen: 'vxe-icon-arrow-down',
...props.expandConfig,
},
// 虚拟滚动配置y轴大于xx条数据时启用虚拟滚动
scrollY: {

View File

@ -23,7 +23,12 @@ export function useDragSort(props: JVxeTableProps, methods: JVxeTableMethods) {
function createSortable() {
let xTable = methods.getXTable();
// let dom = xTable.$el.querySelector('.vxe-table--fixed-wrapper .vxe-table--body tbody')
let dom = xTable.$el.querySelector('.body--wrapper>.vxe-table--body tbody');
// let dom = xTable.$el.querySelector('.body--wrapper>.vxe-table--body tbody');
let dom = xTable.$el.querySelector('.vxe-table--body-inner-wrapper > .vxe-table--body tbody');
if (!dom) {
console.warn('[JVxeTable] 拖拽排序初始化失败可能是vxe-table升级导致的版本不兼容。');
return;
}
let startChildren = [];
sortable2 = Sortable.create(dom as HTMLElement, {
handle: '.drag-btn',

View File

@ -424,12 +424,13 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
let xTable = getXTable();
let { setActive, index } = options;
index = index === -1 ? index : xTable.internalData.tableFullData[index];
index = index == null ? -1 : index;
// 插入行
let result = await xTable.insertAt(rows, index);
if (setActive) {
// -update-begin--author:liaozhiyang---date:20240619---for【TV360X-1404】vxetable警告
// 激活最后一行的编辑模式
xTable.setEditRow(result.rows[result.rows.length - 1]);
xTable.setEditRow(result.rows[result.rows.length - 1], true);
// -update-end--author:liaozhiyang---date:20240619---for【TV360X-1404】vxetable警告
}
await recalcSortNumber();
@ -763,7 +764,7 @@ export function useMethods(props: JVxeTableProps, { emit }, data: JVxeDataProps,
// 4.1.0
//await xTable.updateCache();
// 4.1.1
await xTable.cacheRowMap()
await xTable.cacheRowMap(true)
// update-end--author:liaozhiyang---date:20231011---for【QQYUN-5133】JVxeTable 行编辑升级
return await xTable.updateData();
}

View File

@ -1,8 +1,10 @@
import type { App } from 'vue';
// 引入 vxe-table
import 'xe-utils';
import VxeUIAll from 'vxe-pc-ui';
import VXETable /*Grid*/ from 'vxe-table';
import VXETablePluginAntd from 'vxe-table-plugin-antd';
import 'vxe-pc-ui/lib/style.css';
import 'vxe-table/lib/style.css';
import JVxeTable from './JVxeTable';
@ -27,6 +29,7 @@ export function registerJVxeTable(app: App) {
// 注册自定义组件
registerAllComponent();
// 执行注册方法
app.use(VxeUIAll);
app.use(VXETable, VXETableSettings);
app.component('JVxeTable', JVxeTable);
}

View File

@ -43,7 +43,7 @@
const { createMessage } = useMessage();
const checkedKeys = ref<Array<string | number>>([]);
const logColumns = ref<any>(columns);
const logColumns = ref<any>(exceptionColumns);
const searchSchema = ref<any>(searchFormSchema);
const searchInfo = { logType: '4' };
// 列表页面公共参数、方法

View File

@ -26,11 +26,11 @@ export const columns: BasicColumn[] = [
align:"center",
dataIndex: 'blackList'
},
{
title: '状态',
align:"center",
dataIndex: 'status'
},
// {
// title: '状态',
// align:"center",
// dataIndex: 'status'
// },
{
title: '创建人',
align:"center",
@ -67,6 +67,11 @@ export const formSchema: FormSchema[] = [
];
},
},
{
label: '原始地址',
field: 'originUrl',
component: 'Input',
},
{
label: '请求方法',
field: 'requestMethod',
@ -127,11 +132,6 @@ export const formSchema: FormSchema[] = [
component:"Input",
field: 'body'
},
{
label: '原始地址',
field: 'originUrl',
component: 'Input',
},
{
label: '删除标识',
field: 'delFlag',
@ -252,7 +252,6 @@ export const openApiHeaderJVxeColumns: JVxeColumn[] = [
title: '备注',
key: 'note',
type: JVxeTypes.input,
width:"200px",
placeholder: '请输入${title}',
defaultValue:'',
},
@ -297,7 +296,6 @@ export const openApiParamJVxeColumns: JVxeColumn[] = [
title: '备注',
key: 'note',
type: JVxeTypes.input,
width:"200px",
placeholder: '请输入${title}',
defaultValue:'',
},

View File

@ -9,7 +9,7 @@ enum Api {
edit='/openapi/auth/edit',
apiList= '/openapi/list',
genAKSK = '/openapi/auth/genAKSK',
permissionList='/openapi/permission/list',
permissionList='/openapi/permission/getOpenApi',
permissionAdd='/openapi/permission/add',
deleteOne = '/openapi/auth/delete',
deleteBatch = '/openapi/auth/deleteBatch',
@ -87,15 +87,17 @@ export const batchDelete = (params, handleSuccess) => {
* @param isUpdate
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
if (isUpdate) {
return defHttp.put({ url: Api.edit, params }, { isTransformResponse: false });
}
return defHttp.post({ url: Api.save, params }, { isTransformResponse: false });
}
/**
* 全部权限列表接口
* @param params
*/
export const getApiList = (params) => defHttp.get({ url: Api.apiList, params });
export const getApiList = (params) => defHttp.get({ url: Api.apiList, params }, { isTransformResponse: false });
/**
* 获取已授权项目的接口

View File

@ -30,12 +30,11 @@ export const columns: BasicColumn[] = [
align: "center",
dataIndex: 'createTime'
},
{
title: '关联系统用户名',
align: "center",
dataIndex: 'systemUserId_dictText',
},
// {
// title: '关联系统用户名',
// align: "center",
// dataIndex: 'createBy',
// },
];
// 高级查询数据
@ -43,7 +42,7 @@ export const superQuerySchema = {
name: {title: '授权名称',order: 0,view: 'text', type: 'string',},
ak: {title: 'AK',order: 1,view: 'text', type: 'string',},
sk: {title: 'SK',order: 2,view: 'text', type: 'string',},
createBy: {title: '创建人',order: 3,view: 'text', type: 'string',},
createBy: {title: '关联系统用户名',order: 3,view: 'text', type: 'string',},
createTime: {title: '创建时间',order: 4,view: 'datetime', type: 'string',},
systemUserId: {title: '关联系统用户名',order: 5,view: 'text', type: 'string',},
// systemUserId: {title: '关联系统用户名',order: 5,view: 'text', type: 'string',},
};

View File

@ -11,9 +11,9 @@
</a-form-item>
</a-col>
<a-col :lg="6">
<a-form-item name="systemUserId">
<a-form-item name="createBy">
<template #label><span title="关联系统用户名">关联系统用户名</span></template>
<JSearchSelect dict="sys_user,username,id" v-model:value="queryParam.systemUserId" placeholder="请输入关联系统用户名" allow-clear ></JSearchSelect>
<JSearchSelect dict="sys_user,username,username" v-model:value="queryParam.createBy" placeholder="请输入关联系统用户名" allow-clear ></JSearchSelect>
<!-- <a-input placeholder="请输入关联系统用户名" v-model:value="queryParam.systemUserId" allow-clear ></a-input>-->
</a-form-item>
</a-col>
@ -62,6 +62,7 @@
<template v-slot:bodyCell="{ column, record, index, text }">
</template>
</BasicTable>
<!-- 表单区域 -->
<OpenApiAuthModal ref="registerModal" @success="handleSuccess"></OpenApiAuthModal>
<AuthModal ref="authModal" @success="handleSuccess"></AuthModal>
@ -73,7 +74,14 @@
import { BasicTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, superQuerySchema } from './OpenApiAuth.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './OpenApiAuth.api';
import {
list,
deleteOne,
batchDelete,
getImportUrl,
getExportUrl,
getGenAKSK, saveOrUpdate
} from "./OpenApiAuth.api";
import OpenApiAuthModal from './components/OpenApiAuthModal.vue'
import AuthModal from './components/AuthModal.vue'
import { useUserStore } from '/@/store/modules/user';
@ -157,9 +165,23 @@
*/
function handleEdit(record: Recordable) {
registerModal.value.disableSubmit = false;
registerModal.value.authDrawerOpen = true;
registerModal.value.edit(record);
}
/**
* 重置事件
* @param record
*/
async function handleReset(record: Recordable) {
const AKSKObj = await getGenAKSK({});
record.ak = AKSKObj[0];
record.sk = AKSKObj[1];
saveOrUpdate(record,true);
// handleSuccess;
}
/**
* 详情
*/
@ -200,8 +222,12 @@
auth: 'openapi:open_api_auth:edit'
},
{
label: '编辑',
onClick: handleEdit.bind(null, record),
label: '重置',
popConfirm: {
title: '是否重置AK,SK',
confirm: handleReset.bind(null, record),
placement: 'topLeft',
},
auth: 'openapi:open_api_auth:edit'
},
];

View File

@ -1,60 +1,66 @@
<template>
<a-spin :spinning="confirmLoading">
<JFormContainer :disabled="disabled">
<template #detail>
<BasicTable @register="registerTable" :rowSelection="rowSelection"> </BasicTable>
</template>
</JFormContainer>
<a-row :span="24" style="margin-bottom: 10px">
<a-col :span="12" v-for="item in apiList" @click="handleSelect(item)">
<a-card :style="item.checked ? { border: '1px solid #3370ff' } : {}" hoverable class="checkbox-card" :body-style="{ width: '100%', padding: '10px' }">
<div class="checkbox-name" style="display: flex; width: 100%; justify-content: space-between">
<span>接口名称: {{ item.name }}</span>
<a-checkbox v-model:checked="item.checked" @click.stop class="quantum-checker" @change="(e) => handleChange(e, item)"> </a-checkbox>
</div>
<div class="checkbox-name" style="margin-top: 4px">
请求方式: <span>{{item.requestMethod}}</span>
</div>
</a-card>
</a-col>
</a-row>
<Pagination
v-if="apiList.length > 0"
:current="pageNo"
:page-size="pageSize"
:page-size-options="pageSizeOptions"
:total="total"
:showQuickJumper="true"
:showSizeChanger="true"
@change="handlePageChange"
class="list-footer"
size="small"
/>
</a-spin>
</template>
<script lang="ts" setup>
import { ref, reactive, defineExpose, nextTick, defineProps, computed } from 'vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { permissionAddFunction, getPermissionList, getApiList } from '../OpenApiAuth.api';
import { Form } from 'ant-design-vue';
import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
import { BasicTable } from '@/components/Table';
import { useListPage } from '@/hooks/system/useListPage';
import { columns } from '@/views/openapi/OpenApi.data';
import { computed, defineExpose, defineProps, nextTick, reactive, ref } from "vue";
import { useMessage } from '/@/hooks/web/useMessage';
import { getApiList, getPermissionList, permissionAddFunction } from '../OpenApiAuth.api';
import { Form, Pagination } from 'ant-design-vue';
const queryParam = reactive<any>({});
//注册table数据
const { tableContext } = useListPage({
tableProps: {
title: '授权',
api: getApiList,
columns,
canResize: false,
useSearchForm: false,
beforeFetch: async (params) => {
return Object.assign(params, queryParam);
},
},
});
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
tableContext;
const props = defineProps({
const props = defineProps({
formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: () => ({}) },
formBpm: { type: Boolean, default: true },
});
const formRef = ref();
const useForm = Form.useForm;
const emit = defineEmits(['register', 'ok']);
const formData = reactive<Record<string, any>>({
apiAuthId: '',
apiIdList: [],
});
const { createMessage } = useMessage();
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false);
//认证ID
const apiAuthId = ref<string>('');
//表单验证
const validatorRules = reactive({});
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
//api列表
const apiList = ref<any>([]);
//选中的值
const selectedRowKeys = ref<any>([]);
//选中的数据
const selectedRows = ref<any>([]);
//当前页数
const pageNo = ref<number>(1);
//每页条数
const pageSize = ref<number>(10);
//总条数
const total = ref<number>(0);
//可选择的页数
const pageSizeOptions = ref<any>(['10', '20', '30']);
// 表单禁用
const disabled = computed(() => {
@ -68,6 +74,25 @@
return props.formDisabled;
});
/**
* 加载数据
*/
function reload() {
getApiList({ pageNo: pageNo.value, pageSize: pageSize.value, column: 'createTime', order: 'desc'}).then((res)=>{
if (res.success) {
for (const item of res.result.records) {
item.checked = false;
}
apiList.value = res.result.records;
total.value = res.result.total;
setChecked();
} else {
apiList.value = [];
total.value = 0;
}
});
}
/**
* 新增
*/
@ -78,23 +103,27 @@
/**
* 编辑
*/
function edit(record) {
nextTick(() => {
resetFields();
//赋值
formData.apiAuthId = record.id;
async function edit(record) {
selectedRowKeys.value = [];
selectedRows.value = [];
pageNo.value = 1;
pageSize.value = 10;
apiAuthId.value = record.id;
await nextTick(() => {
// 获取当前已授权的项目
getPermissionList({ apiAuthId: record.id }).then((res) => {
if (res && res.length > 0) {
let list = res.result.records || res.result;
let ids = [];
list.forEach((item) => {
ids.push(item.apiId);
if (res.length > 0) {
res.forEach((item) => {
if(item.ifCheckBox == "1"){
selectedRowKeys.value.push(item.id);
selectedRows.value.push(item);
}
});
selectedRowKeys.value = ids;
formData.apiIdList = ids;
//设置选中
setChecked();
}
});
reload();
});
}
@ -102,35 +131,21 @@
* 提交数据
*/
async function submitForm() {
if(selectedRowKeys.value.length === 0)
return emit('ok');
try {
// 触发表单验证
await validate();
} catch ({ errorFields }) {
if (errorFields) {
const firstField = errorFields[0];
if (firstField) {
formRef.value.scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
}
}
return Promise.reject(errorFields);
}
confirmLoading.value = true;
//时间格式化
let model = formData;
// model.apiIdList = selectedRowKeys.value;
let model = {};
let apiId = ""
selectedRowKeys.value.forEach((item) => {
apiId += item +",";
})
model.apiId = apiId;
delete model.apiIdList
model['apiId'] = apiId;
model['apiAuthId'] = apiAuthId.value;
await permissionAddFunction(model)
.then((res) => {
if (res.success) {
createMessage.success(res.message);
emit('ok');
cleanData()
} else {
createMessage.warning(res.message);
}
@ -139,11 +154,89 @@
confirmLoading.value = false;
});
}
const cleanData = () => {
selectedRows.value = []
selectedRowKeys.value = []
};
/**
* 复选框选中事件
* @param item
*/
function handleSelect(item) {
let id = item.id;
const target = apiList.value.find((item) => item.id === id);
if (target) {
target.checked = !target.checked;
}
//存放选中的知识库的id
if (!selectedRowKeys.value || selectedRowKeys.value.length == 0) {
selectedRowKeys.value.push(id);
selectedRows.value.push(item);
return;
}
let findIndex = selectedRowKeys.value.findIndex((item) => item === id);
if (findIndex === -1) {
selectedRowKeys.value.push(id);
selectedRows.value.push(item);
} else {
selectedRowKeys.value.splice(findIndex, 1);
selectedRows.value.splice(findIndex, 1);
}
}
/**
* 复选框选中事件
*
* @param e
* @param item
*/
function handleChange(e, item: any) {
if (e.target.checked) {
selectedRowKeys.value.push(item.id);
selectedRows.value.push(item);
} else {
let findIndex = selectedRowKeys.value.findIndex((val) => val === item.id);
if (findIndex != -1) {
selectedRowKeys.value.splice(findIndex, 1);
selectedRows.value.splice(findIndex, 1);
}
}
}
/**
* 分页改变事件
* @param page
* @param current
*/
function handlePageChange(page, current) {
pageNo.value = page;
pageSize.value = current;
reload();
}
/**
* 设置选装状态
*/
function setChecked() {
if (apiList.value && apiList.value.length > 0){
let value = selectedRowKeys.value.join(',');
apiList.value = apiList.value.map((item) => {
if (value.indexOf(item.id) !== -1) {
item.checked = true;
} else {
item.checked = false;
}
return item;
});
}
}
defineExpose({
add,
edit,
submitForm,
cleanData
});
</script>
@ -151,4 +244,28 @@
.antd-modal-form {
padding: 14px;
}
.list-footer {
position: absolute;
bottom: -22px;
right: 10px;
text-align: center;
}
.checkbox-card {
margin-bottom: 10px;
margin-right: 10px;
}
.checkbox-img {
width: 30px;
height: 30px;
}
.checkbox-name {
margin-left: 4px;
font-size: 13px;
}
.use-select {
color: #646a73;
position: absolute;
bottom: 0;
left: 20px;
}
</style>

View File

@ -1,12 +1,27 @@
<template>
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<AuthForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></AuthForm>
</j-modal>
<!-- <j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">-->
<div style="position: relative;">
<a-modal
v-model:open="authDrawerOpen"
class="custom-class"
root-class-name="root-class-name"
:root-style="{ color: 'blue' }"
:body-style="{ padding: '20px' }"
style="color: red"
:title="title"
:width="600"
@after-open-change="authDrawerOpenChange"
@ok="handleOk"
>
<AuthForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></AuthForm>
</a-modal>
</div>
<!-- </j-modal>-->
</template>
<script lang="ts" setup>
import { ref, nextTick, defineExpose } from 'vue';
import AuthForm from './AuthForm.vue'
import AuthForm from './AuthForm.vue';
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
const title = ref<string>('');
@ -16,6 +31,12 @@
const registerForm = ref();
const emit = defineEmits(['register', 'success']);
const authDrawerOpen = ref(false);
const authDrawerOpenChange = (val: any) => {
if(!val)
registerForm.value.cleanData()
};
/**
* 新增
*/
@ -34,6 +55,7 @@
function edit(record) {
title.value = disableSubmit.value ? '详情' : '授权';
visible.value = true;
authDrawerOpen.value = true;
nextTick(() => {
registerForm.value.edit(record);
});
@ -59,6 +81,7 @@
*/
function handleCancel() {
visible.value = false;
authDrawerOpen.value = false;
}
defineExpose({

View File

@ -19,11 +19,11 @@
<a-input v-model:value="formData.sk" placeholder="请输入SK" disabled allow-clear ></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="关联系统用户名" v-bind="validateInfos.systemUserId" id="OpenApiAuthForm-systemUserId" name="systemUserId">
<JSearchSelect dict="sys_user,username,id" v-model:value="formData.systemUserId" placeholder="请输入关联系统用户名" allow-clear ></JSearchSelect>
</a-form-item>
</a-col>
<!-- <a-col :span="24">-->
<!-- <a-form-item label="关联系统用户名" v-bind="validateInfos.systemUserId" id="OpenApiAuthForm-systemUserId" name="systemUserId">-->
<!-- <JSearchSelect dict="sys_user,username,id" v-model:value="formData.systemUserId" placeholder="请输入关联系统用户名" allow-clear ></JSearchSelect>-->
<!-- </a-form-item>-->
<!-- </a-col>-->
</a-row>
</a-form>
</template>

View File

@ -1,5 +1,5 @@
<template>
<j-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<j-modal :title="title" :width="width" :maxHeight="200" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
<OpenApiAuthForm ref="registerForm" @ok="submitCallback" :title="title" :formDisabled="disableSubmit" :formBpm="false"></OpenApiAuthForm>
</j-modal>
</template>

View File

@ -1,39 +1,39 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="'80%'" @ok="handleSubmit">
<BasicModal :bodyStyle="{ padding: '20px' }" v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" width="80%" @ok="handleSubmit">
<a-row :gutter="24">
<a-col :span="12">
<a-col :span="10">
<BasicForm @register="registerForm" ref="formRef" name="OpenApiForm" />
</a-col>
<a-col :span="12">
<a-col :span="14">
<a-row :gutter="24">
<a-col :span="24">
<a-col :span="24" style="margin-top: -0.6em">
<JVxeTable
keep-source
resizable
ref="openApiHeader"
:loading="openApiHeaderTable.loading"
:columns="openApiHeaderTable.columns"
:dataSource="openApiHeaderTable.dataSource"
:height="340"
:height="240"
:disabled="formDisabled"
:rowNumber="true"
:rowSelection="true"
:toolbar="true"
size="mini"
/>
</a-col>
<a-col :span="24">
<JVxeTable
keep-source
resizable
ref="openApiParam"
:loading="openApiParamTable.loading"
:columns="openApiParamTable.columns"
:dataSource="openApiParamTable.dataSource"
:height="340"
:height="240"
:disabled="formDisabled"
:rowNumber="true"
:rowSelection="true"
:toolbar="true"
size="mini"
/>
</a-col>
</a-row>
@ -75,10 +75,11 @@
});
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
labelWidth: 150,
labelWidth: 100,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: { span: 24 },
wrapperCol: { span: 24 },
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {

View File

@ -26,7 +26,7 @@ export const columns: BasicColumn[] = [
{
title: '手机',
width: 150,
dataIndex: 'telephone',
dataIndex: 'phone',
},
{
title: '邮箱',