This commit is contained in:
JEECG
2025-04-09 13:45:29 +08:00
9 changed files with 443 additions and 60 deletions

View File

@@ -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>

View File

@@ -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---forQQYUN-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---forissues/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: 修复登录成功后,没有正确重定向的问题

View File

@@ -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>

View File

@@ -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(),
]);
}
};