mirror of https://github.com/jeecgboot/jeecg-boot
Merge branch 'master' of https://github.com/zhangdaiscott/jeecg-boot
commit
47a2a6fbac
57
README.md
57
README.md
|
@ -41,6 +41,35 @@ JeecgBoot AI低代码平台,可以应用在任何J2EE项目的开发中,支
|
|||
|
||||
|
||||
|
||||
项目说明
|
||||
-----------------------------------
|
||||
|
||||
| 项目名 | 说明 |
|
||||
|--------------------|------------------------|
|
||||
| `jeecg-boot` | 后端源码JAVA(SpringBoot微服务架构) |
|
||||
| `jeecgboot-vue3` | 前端源码VUE3(vue3+vite6+ts最新技术栈) |
|
||||
| `JeecgUniapp` | [配套APP框架](https://github.com/jeecgboot/JeecgUniapp) 适配多个终端,支持APP、小程序、H5 |
|
||||
|
||||
|
||||
|
||||
技术文档
|
||||
-----------------------------------
|
||||
|
||||
- 官方网站: [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(满)、其他(满)
|
||||
|
||||
|
||||
|
||||
启动项目
|
||||
-----------------------------------
|
||||
|
||||
- [IDEA启动前后端项目](https://help.jeecg.com/java/setup/idea/startup)
|
||||
- [Docker一键启动前后端](https://help.jeecg.com/java/docker/quick)
|
||||
|
||||
|
||||
|
||||
AIGC应用平台介绍
|
||||
-----------------------------------
|
||||
|
@ -91,34 +120,6 @@ AIGC应用平台介绍
|
|||
| 等等。。 | √ |
|
||||
|
||||
|
||||
项目说明
|
||||
-----------------------------------
|
||||
|
||||
| 项目名 | 说明 |
|
||||
|--------------------|------------------------|
|
||||
| `jeecg-boot` | 后端源码JAVA(SpringBoot微服务架构) |
|
||||
| `jeecgboot-vue3` | 前端源码VUE3(vue3+vite6+ts最新技术栈) |
|
||||
| `JeecgUniapp` | [配套APP框架](https://github.com/jeecgboot/JeecgUniapp) 适配多个终端,支持APP、小程序、H5 |
|
||||
|
||||
|
||||
|
||||
技术文档
|
||||
-----------------------------------
|
||||
|
||||
- 官方网站: [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(满)、其他(满)
|
||||
|
||||
|
||||
|
||||
启动项目
|
||||
-----------------------------------
|
||||
|
||||
- [IDEA启动前后端项目](https://help.jeecg.com/java/setup/idea/startup)
|
||||
- [Docker一键启动前后端](https://help.jeecg.com/java/docker/quick)
|
||||
|
||||
|
||||
|
||||
技术架构:
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package org.jeecg.config.init;
|
||||
|
||||
import io.undertow.server.DefaultByteBufferPool;
|
||||
import io.undertow.server.handlers.BlockingHandler;
|
||||
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
|
||||
import org.jeecg.modules.monitor.actuator.undertow.CustomUndertowMetricsHandler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -12,7 +15,14 @@ import org.springframework.context.annotation.Configuration;
|
|||
* 解决启动提示: WARN io.undertow.websockets.jsr:68 - UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used
|
||||
*/
|
||||
@Configuration
|
||||
public class UndertowConfiguration implements WebServerFactoryCustomizer<UndertowServletWebServerFactory>{
|
||||
public class UndertowConfiguration implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
|
||||
|
||||
/**
|
||||
* 自定义undertow监控指标工具类
|
||||
* for [QQYUN-11902]tomcat 替换undertow 这里的功能还没修改
|
||||
*/
|
||||
@Autowired
|
||||
private CustomUndertowMetricsHandler customUndertowMetricsHandler;
|
||||
|
||||
@Override
|
||||
public void customize(UndertowServletWebServerFactory factory) {
|
||||
|
@ -24,6 +34,9 @@ public class UndertowConfiguration implements WebServerFactoryCustomizer<Underto
|
|||
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 8192));
|
||||
|
||||
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
|
||||
|
||||
// 添加自定义 监控 handler
|
||||
deploymentInfo.addInitialHandlerChainWrapper(next -> new BlockingHandler(customUndertowMetricsHandler.wrap(next)));
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package org.jeecg.modules.monitor.actuator.undertow;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.undertow.server.HttpHandler;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.server.session.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
/**
|
||||
* 自定义undertow监控指标工具类
|
||||
* for [QQYUN-11902]tomcat 替换undertow 这里的功能还没修改
|
||||
* @author chenrui
|
||||
* @date 2025/4/8 19:06
|
||||
*/
|
||||
@Component("jeecgCustomUndertowMetricsHandler")
|
||||
public class CustomUndertowMetricsHandler {
|
||||
|
||||
// 用于统计已创建的 session 数量
|
||||
private final LongAdder sessionsCreated = new LongAdder();
|
||||
|
||||
// 用于统计已销毁的 session 数量
|
||||
private final LongAdder sessionsExpired = new LongAdder();
|
||||
|
||||
// 当前活跃的 session 数量
|
||||
private final AtomicInteger activeSessions = new AtomicInteger();
|
||||
|
||||
// 历史最大活跃 session 数
|
||||
private final AtomicInteger maxActiveSessions = new AtomicInteger();
|
||||
|
||||
// Undertow 内存 session 管理器(用于创建与管理 session)
|
||||
private final InMemorySessionManager sessionManager = new InMemorySessionManager("undertow-session-manager");
|
||||
|
||||
// 使用 Cookie 存储 session ID
|
||||
private final SessionConfig sessionConfig = new SessionCookieConfig();
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param meterRegistry
|
||||
* @author chenrui
|
||||
* @date 2025/4/8 19:07
|
||||
*/
|
||||
public CustomUndertowMetricsHandler(MeterRegistry meterRegistry) {
|
||||
// 注册 Micrometer 指标
|
||||
meterRegistry.gauge("undertow.sessions.created", sessionsCreated, LongAdder::longValue);
|
||||
meterRegistry.gauge("undertow.sessions.expired", sessionsExpired, LongAdder::longValue);
|
||||
meterRegistry.gauge("undertow.sessions.active.current", activeSessions, AtomicInteger::get);
|
||||
meterRegistry.gauge("undertow.sessions.active.max", maxActiveSessions, AtomicInteger::get);
|
||||
|
||||
// 添加 session 生命周期监听器,统计 session 创建与销毁
|
||||
sessionManager.registerSessionListener(new SessionListener() {
|
||||
@Override
|
||||
public void sessionCreated(Session session, HttpServerExchange exchange) {
|
||||
sessionsCreated.increment();
|
||||
int now = activeSessions.incrementAndGet();
|
||||
maxActiveSessions.getAndUpdate(max -> Math.max(max, now));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionDestroyed(Session session, HttpServerExchange exchange, SessionDestroyedReason reason) {
|
||||
sessionsExpired.increment();
|
||||
activeSessions.decrementAndGet();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 包装 Undertow 的 HttpHandler,实现 session 自动创建逻辑
|
||||
* @param next
|
||||
* @return
|
||||
* @author chenrui
|
||||
* @date 2025/4/8 19:07
|
||||
*/
|
||||
public HttpHandler wrap(HttpHandler next) {
|
||||
return exchange -> {
|
||||
// 获取当前 session,如果不存在则创建
|
||||
Session session = sessionManager.getSession(exchange, sessionConfig);
|
||||
if (session == null) {
|
||||
sessionManager.createSession(exchange, sessionConfig);
|
||||
}
|
||||
|
||||
// 执行下一个 Handler
|
||||
next.handleRequest(exchange);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
package org.jeecg.modules.system.test;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.modules.redis.client.JeecgRedisClient;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.config.JeecgBaseConfig;
|
||||
import org.jeecg.modules.base.service.BaseCommonService;
|
||||
import org.jeecg.modules.system.controller.SysUserController;
|
||||
import org.jeecg.modules.system.entity.SysUser;
|
||||
import org.jeecg.modules.system.service.*;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
|
||||
/**
|
||||
* 系统用户单元测试
|
||||
*/
|
||||
@WebMvcTest(SysUserController.class)
|
||||
public class SysUserApiTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@MockBean
|
||||
private ISysUserService sysUserService;
|
||||
|
||||
@MockBean
|
||||
private ISysDepartService sysDepartService;
|
||||
|
||||
@MockBean
|
||||
private ISysUserRoleService sysUserRoleService;
|
||||
|
||||
@MockBean
|
||||
private ISysUserDepartService sysUserDepartService;
|
||||
|
||||
@MockBean
|
||||
private ISysDepartRoleUserService departRoleUserService;
|
||||
|
||||
@MockBean
|
||||
private ISysDepartRoleService departRoleService;
|
||||
|
||||
@MockBean
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Value("${jeecg.path.upload}")
|
||||
private String upLoadPath;
|
||||
|
||||
@MockBean
|
||||
private BaseCommonService baseCommonService;
|
||||
|
||||
@MockBean
|
||||
private ISysUserAgentService sysUserAgentService;
|
||||
|
||||
@MockBean
|
||||
private ISysPositionService sysPositionService;
|
||||
|
||||
@MockBean
|
||||
private ISysUserTenantService userTenantService;
|
||||
|
||||
@MockBean
|
||||
private JeecgRedisClient jeecgRedisClient;
|
||||
|
||||
@MockBean
|
||||
private JeecgBaseConfig jeecgBaseConfig;
|
||||
/**
|
||||
* 测试地址:实际使用时替换成你自己的地址
|
||||
*/
|
||||
private final String BASE_URL = "/sys/user/";
|
||||
|
||||
/**
|
||||
* 测试用例:查询记录
|
||||
*/
|
||||
@Test
|
||||
public void testQuery() throws Exception{
|
||||
// 请求地址
|
||||
String url = BASE_URL + "list";
|
||||
|
||||
Page<SysUser> sysUserPage = new Page<>();
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUsername("admin");
|
||||
List<SysUser> users = new ArrayList<>();
|
||||
users.add(sysUser);
|
||||
sysUserPage.setRecords(users);
|
||||
sysUserPage.setCurrent(1);
|
||||
sysUserPage.setSize(10);
|
||||
sysUserPage.setTotal(1);
|
||||
|
||||
given(this.sysUserService.queryPageList(any(), any(), any(), any())).willReturn(Result.OK(sysUserPage));
|
||||
|
||||
String result = mockMvc.perform(get(url)).andReturn().getResponse().getContentAsString();
|
||||
JSONObject jsonObject = JSON.parseObject(result);
|
||||
Assertions.assertEquals("admin", jsonObject.getJSONObject("result").getJSONArray("records").getJSONObject(0).getString("username"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用例:新增
|
||||
*/
|
||||
@Test
|
||||
public void testAdd() throws Exception {
|
||||
// 请求地址
|
||||
String url = BASE_URL + "add" ;
|
||||
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("username", "wangwuTest");
|
||||
params.put("password", "123456");
|
||||
params.put("confirmpassword","123456");
|
||||
params.put("realname", "单元测试");
|
||||
params.put("activitiSync", "1");
|
||||
params.put("userIdentity","1");
|
||||
params.put("workNo","0025");
|
||||
|
||||
String result = mockMvc.perform(post(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(params.toJSONString()))
|
||||
.andReturn().getResponse().getContentAsString();
|
||||
JSONObject jsonObject = JSON.parseObject(result);
|
||||
Assertions.assertTrue(jsonObject.getBoolean("success"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 测试用例:修改
|
||||
*/
|
||||
@Test
|
||||
public void testEdit() throws Exception {
|
||||
// 数据Id
|
||||
String dataId = "1331795062924374018";
|
||||
// 请求地址
|
||||
String url = BASE_URL + "edit";
|
||||
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("username", "wangwuTest");
|
||||
params.put("realname", "单元测试1111");
|
||||
params.put("activitiSync", "1");
|
||||
params.put("userIdentity","1");
|
||||
params.put("workNo","0025");
|
||||
params.put("id",dataId);
|
||||
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUsername("admin");
|
||||
|
||||
given(this.sysUserService.getById(any())).willReturn(sysUser);
|
||||
|
||||
String result = mockMvc.perform(put(url).contentType(MediaType.APPLICATION_JSON_VALUE).content(params.toJSONString()))
|
||||
.andReturn().getResponse().getContentAsString();
|
||||
JSONObject jsonObject = JSON.parseObject(result);
|
||||
Assertions.assertTrue(jsonObject.getBoolean("success"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 测试用例:删除
|
||||
*/
|
||||
@Test
|
||||
public void testDelete() throws Exception {
|
||||
// 数据Id
|
||||
String dataId = "1331795062924374018";
|
||||
// 请求地址
|
||||
String url = BASE_URL + "delete" + "?id=" + dataId;
|
||||
String result = mockMvc.perform(delete(url)).andReturn().getResponse().getContentAsString();
|
||||
JSONObject jsonObject = JSON.parseObject(result);
|
||||
Assertions.assertTrue(jsonObject.getBoolean("success"));
|
||||
}
|
||||
}
|
|
@ -359,7 +359,7 @@
|
|||
<dependency>
|
||||
<groupId>org.jeecgframework</groupId>
|
||||
<artifactId>weixin4j</artifactId>
|
||||
<version>2.0.1</version>
|
||||
<version>2.0.2</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
<template>
|
||||
<Select @dropdownVisibleChange="handleFetch" v-bind="attrs_" @change="handleChange" :options="getOptions" v-model:value="state">
|
||||
<Select
|
||||
v-bind="attrs_"
|
||||
v-model:value="state"
|
||||
:options="getOptions"
|
||||
@change="handleChange"
|
||||
@dropdownVisibleChange="handleFetch"
|
||||
@popupScroll="handlePopupScroll"
|
||||
>
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)">
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
|
@ -24,9 +31,10 @@
|
|||
import { LoadingOutlined } from '@ant-design/icons-vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { isNumber } from '/@/utils/is';
|
||||
|
||||
type OptionsItem = { label: string; value: string; disabled?: boolean };
|
||||
|
||||
//文档 https://help.jeecg.com/ui/apiSelect#pageconfig%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE
|
||||
export default defineComponent({
|
||||
name: 'ApiSelect',
|
||||
components: {
|
||||
|
@ -35,7 +43,7 @@
|
|||
},
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
value: [Array, Object, String, Number],
|
||||
value: [Array, String, Number],
|
||||
numberToString: propTypes.bool,
|
||||
api: {
|
||||
type: Function as PropType<(arg?: Recordable) => Promise<OptionsItem[]>>,
|
||||
|
@ -46,6 +54,11 @@
|
|||
type: Object as PropType<Recordable>,
|
||||
default: () => ({}),
|
||||
},
|
||||
//分页配置
|
||||
pageConfig: {
|
||||
type: Object as PropType<Recordable>,
|
||||
default: () => ({ isPage: false }),
|
||||
},
|
||||
// support xxx.xxx.xx
|
||||
resultField: propTypes.string.def(''),
|
||||
labelField: propTypes.string.def('label'),
|
||||
|
@ -60,7 +73,15 @@
|
|||
const emitData = ref<any[]>([]);
|
||||
const attrs = useAttrs();
|
||||
const { t } = useI18n();
|
||||
|
||||
// update-begin--author:liusq---date:20250407---for:【QQYUN-11831】ApiSelect 分页下拉方案 #7883
|
||||
const hasMore = ref(true);
|
||||
const pagination = ref({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const defPageConfig = { isPage: false, pageField: 'pageNo', pageSizeField: 'pageSize', totalField: 'total', listField: 'records' };
|
||||
// update-end--author:liusq---date:20250407---for:【QQYUN-11831】ApiSelect 分页下拉方案 #7883
|
||||
// Embedded in the form, just use the hook binding to perform form verification
|
||||
const [state, setState] = useRuleFormItem(props, 'value', 'change', emitData);
|
||||
// update-begin--author:liaozhiyang---date:20230830---for:【QQYUN-6308】解决警告
|
||||
|
@ -114,7 +135,7 @@
|
|||
},
|
||||
{ deep: true }
|
||||
);
|
||||
//监听数值修改,查询数据
|
||||
//监听数值修改,查询数据
|
||||
watchEffect(() => {
|
||||
props.value && handleFetch();
|
||||
});
|
||||
|
@ -122,17 +143,33 @@
|
|||
async function fetch() {
|
||||
const api = props.api;
|
||||
if (!api || !isFunction(api)) return;
|
||||
options.value = [];
|
||||
// update-begin--author:liusq---date:20250407---for:【QQYUN-11831】ApiSelect 分页下拉方案 #7883
|
||||
if (!props.pageConfig.isPage || pagination.value.pageNo == 1) {
|
||||
options.value = [];
|
||||
}
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await api(props.params);
|
||||
if (Array.isArray(res)) {
|
||||
options.value = res;
|
||||
emitChange();
|
||||
return;
|
||||
}
|
||||
if (props.resultField) {
|
||||
options.value = get(res, props.resultField) || [];
|
||||
let { isPage, pageField, pageSizeField, totalField, listField } = { ...defPageConfig, ...props.pageConfig };
|
||||
let params = isPage
|
||||
? { ...props.params, [pageField]: pagination.value.pageNo, [pageSizeField]: pagination.value.pageSize }
|
||||
: { ...props.params };
|
||||
// update-end--author:liusq---date:20250407---for:【QQYUN-11831】ApiSelect 分页下拉方案 #7883
|
||||
const res = await api(params);
|
||||
if (isPage) {
|
||||
// update-begin--author:liusq---date:20250407---for:【QQYUN-11831】ApiSelect 分页下拉方案 #7883
|
||||
options.value = [...options.value, ...res[listField]];
|
||||
pagination.value.total = res[totalField] || 0;
|
||||
hasMore.value = res[totalField] ? options.value.length < res[totalField] : res[listField] < pagination.value.pageSize;
|
||||
// update-end--author:liusq---date:20250407---for:【QQYUN-11831】ApiSelect 分页下拉方案 #7883
|
||||
} else {
|
||||
if (Array.isArray(res)) {
|
||||
options.value = res;
|
||||
emitChange();
|
||||
return;
|
||||
}
|
||||
if (props.resultField) {
|
||||
options.value = get(res, props.resultField) || [];
|
||||
}
|
||||
}
|
||||
emitChange();
|
||||
} catch (error) {
|
||||
|
@ -151,9 +188,17 @@
|
|||
|
||||
function initValue() {
|
||||
let value = props.value;
|
||||
if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
|
||||
state.value = value.split(',');
|
||||
// update-begin--author:liaozhiyang---date:20250407---for:【issues/8037】初始化值单选的值被错误地写入数组值
|
||||
if (unref(attrs).mode == 'multiple') {
|
||||
if (value && typeof value === 'string' && value != 'null' && value != 'undefined') {
|
||||
state.value = value.split(',');
|
||||
} else if (isNumber(value)) {
|
||||
state.value = [value];
|
||||
}
|
||||
} else {
|
||||
state.value = value;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20250407---for:【issues/8037】初始化值单选的值被错误地写入数组值
|
||||
}
|
||||
|
||||
async function handleFetch() {
|
||||
|
@ -171,8 +216,18 @@
|
|||
vModalValue && vModalValue(_);
|
||||
emitData.value = args;
|
||||
}
|
||||
|
||||
return { state, attrs_, attrs, getOptions, loading, t, handleFetch, handleChange };
|
||||
// update-begin--author:liusq---date:20250407---for:【QQYUN-11831】ApiSelect 分页下拉方案 #7883
|
||||
// 滚动加载更多
|
||||
function handlePopupScroll(e) {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
||||
const isNearBottom = scrollHeight - scrollTop <= clientHeight + 20;
|
||||
if (props.pageConfig.isPage && isNearBottom && hasMore.value && !loading.value) {
|
||||
pagination.value.pageNo += 1;
|
||||
fetch();
|
||||
}
|
||||
}
|
||||
// update-end--author:liusq---date:20250407---for:【QQYUN-11831】ApiSelect 分页下拉方案 #7883
|
||||
return { state, attrs_, attrs, getOptions, loading, t, handleFetch, handleChange, handlePopupScroll };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -209,18 +209,11 @@ export const useUserStore = defineStore({
|
|||
//update-begin---author:wangshuai ---date:20230424 for:【QQYUN-5195】登录之后直接刷新页面导致没有进入创建组织页面------------
|
||||
if (redirect && goHome) {
|
||||
//update-end---author:wangshuai ---date:20230424 for:【QQYUN-5195】登录之后直接刷新页面导致没有进入创建组织页面------------
|
||||
// update-begin--author:liaozhiyang---date:20240104---for:【QQYUN-7804】部署生产环境,登录跳转404问题
|
||||
let publicPath = import.meta.env.VITE_PUBLIC_PATH;
|
||||
if (publicPath && publicPath != '/') {
|
||||
// update-begin--author:liaozhiyang---date:20240509---for:【issues/1147】登录跳转时去掉发布路径的最后一个/以解决404问题
|
||||
if (publicPath.endsWith('/')) {
|
||||
publicPath = publicPath.slice(0, -1);
|
||||
}
|
||||
redirect = publicPath + redirect;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240509---for:【issues/1147】登录跳转时去掉发布路径的最后一个/以解决404问题
|
||||
// update-begin--author:liaozhiyang---date:20250407---for:【issues/8034】hash模式下退出重登录默认跳转地址异常
|
||||
// router.options.history.base可替代之前的publicPath
|
||||
// 当前页面打开
|
||||
window.open(redirect, '_self')
|
||||
window.open(`${router.options.history.base}${redirect}`, '_self');
|
||||
// update-end--author:liaozhiyang---date:20250407---for:【issues/8034】hash模式下退出重登录默认跳转地址异常
|
||||
return data;
|
||||
}
|
||||
// update-end-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<a-tabs v-model:activeKey="activeKey" @change="tabChange">
|
||||
<a-tab-pane key="1" tab="服务器信息"></a-tab-pane>
|
||||
<a-tab-pane key="2" tab="JVM信息" force-render></a-tab-pane>
|
||||
<a-tab-pane key="3" tab="Tomcat信息"></a-tab-pane>
|
||||
<!-- <a-tab-pane key="3" tab="Tomcat信息"></a-tab-pane> -->
|
||||
<a-tab-pane key="6" tab="Undertow信息"></a-tab-pane>
|
||||
<a-tab-pane key="4" tab="磁盘监控">
|
||||
<DiskInfo v-if="activeKey == 4" style="height: 100%"></DiskInfo>
|
||||
</a-tab-pane>
|
||||
|
|
|
@ -30,6 +30,11 @@ enum Api {
|
|||
tomcatSessionsRejected = '/actuator/metrics/tomcat.sessions.rejected',
|
||||
|
||||
memoryInfo = '/sys/actuator/memory/info',
|
||||
// undertow 监控
|
||||
undertowSessionsCreated = '/actuator/metrics/undertow.sessions.created',
|
||||
undertowSessionsExpired = '/actuator/metrics/undertow.sessions.expired',
|
||||
undertowSessionsActiveCurrent = '/actuator/metrics/undertow.sessions.active.current',
|
||||
undertowSessionsActiveMax = '/actuator/metrics/undertow.sessions.active.max',
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,6 +212,34 @@ export const getTomcatSessionsRejected = () => {
|
|||
return defHttp.get({ url: Api.tomcatSessionsRejected }, { isTransformResponse: false });
|
||||
};
|
||||
|
||||
/**
|
||||
*undertow 已创建 session 数
|
||||
*/
|
||||
export const getUndertowSessionsCreated = () => {
|
||||
return defHttp.get({ url: Api.undertowSessionsCreated }, { isTransformResponse: false });
|
||||
};
|
||||
|
||||
/**
|
||||
*undertow 已过期 session 数
|
||||
*/
|
||||
export const getUndertowSessionsExpired = () => {
|
||||
return defHttp.get({ url: Api.undertowSessionsExpired }, { isTransformResponse: false });
|
||||
};
|
||||
|
||||
/**
|
||||
*undertow 当前活跃 session 数
|
||||
*/
|
||||
export const getUndertowSessionsActiveCurrent = () => {
|
||||
return defHttp.get({ url: Api.undertowSessionsActiveCurrent }, { isTransformResponse: false });
|
||||
};
|
||||
|
||||
/**
|
||||
*undertow 活跃 session 数峰值
|
||||
*/
|
||||
export const getUndertowSessionsActiveMax = () => {
|
||||
return defHttp.get({ url: Api.undertowSessionsActiveMax }, { isTransformResponse: false });
|
||||
};
|
||||
|
||||
/**
|
||||
* 内存信息
|
||||
*/
|
||||
|
@ -230,6 +263,9 @@ export const getMoreInfo = (infoType) => {
|
|||
if (infoType == '5') {
|
||||
return {};
|
||||
}
|
||||
if (infoType == '6') {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
export const getTextInfo = (infoType) => {
|
||||
|
@ -293,6 +329,16 @@ export const getTextInfo = (infoType) => {
|
|||
'memory.runtime.usage': { color: 'purple', text: 'JVM内存使用率', unit: '%', valueType: 'Number' },
|
||||
};
|
||||
}
|
||||
if (infoType == '6') {
|
||||
// undertow 监控
|
||||
return {
|
||||
'undertow.sessions.created': { color: 'green', text: 'undertow 已创建 session 数', unit: '个' },
|
||||
'undertow.sessions.expired': { color: 'green', text: 'undertow 已过期 session 数', unit: '个' },
|
||||
'undertow.sessions.active.current': { color: 'green', text: 'undertow 当前活跃 session 数', unit: '个' },
|
||||
'undertow.sessions.active.max': { color: 'green', text: 'undertow 活跃 session 数峰值', unit: '个' },
|
||||
'undertow.sessions.rejected': { color: 'green', text: '超过session 最大配置后,拒绝的 session 个数', unit: '个' },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -334,4 +380,13 @@ export const getServerInfo = (infoType) => {
|
|||
if (infoType == '5') {
|
||||
return Promise.all([getMemoryInfo()]);
|
||||
}
|
||||
// undertow监控
|
||||
if (infoType == '6') {
|
||||
return Promise.all([
|
||||
getUndertowSessionsActiveCurrent(),
|
||||
getUndertowSessionsActiveMax(),
|
||||
getUndertowSessionsCreated(),
|
||||
getUndertowSessionsExpired(),
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue