feat(系统设置): 资源池容量详情&详情列表联调

This commit is contained in:
xinxin.wu 2024-10-17 15:50:44 +08:00 committed by Craftsman
parent 620b834789
commit 9f3aecc67a
7 changed files with 189 additions and 61 deletions

View File

@ -4,6 +4,8 @@ import {
DeletePoolUrl,
DetailPoolUrl,
EnablePoolUrl,
PoolCapacityDetailUrl,
PoolCapacityTaskUrl,
PoolListUrl,
UpdatePoolUrl,
} from '@/api/requrls/setting/resourcePool';
@ -11,6 +13,8 @@ import {
import type { CommonList, TableQueryParams } from '@/models/common';
import type {
AddResourcePoolParams,
CapacityDetailType,
CapacityTaskItem,
ResourcePoolDetail,
ResourcePoolItem,
UpdateResourcePoolParams,
@ -47,3 +51,13 @@ export function delPoolInfo(poolId: string) {
export function togglePoolStatus(poolId: string) {
return MSR.post({ url: EnablePoolUrl, params: poolId });
}
// 获取资源池容量列表
export function getCapacityTaskList(data: TableQueryParams) {
return MSR.post<CommonList<CapacityTaskItem>>({ url: PoolCapacityTaskUrl, data });
}
// 获取资源池容量详情
export function getCapacityDetail(data: TableQueryParams) {
return MSR.post<CapacityDetailType>({ url: PoolCapacityDetailUrl, data });
}

View File

@ -4,3 +4,5 @@ export const AddPoolUrl = '/test/resource/pool/add';
export const DeletePoolUrl = '/test/resource/pool/delete';
export const DetailPoolUrl = '/test/resource/pool/detail';
export const EnablePoolUrl = '/test/resource/pool/set/enable/';
export const PoolCapacityTaskUrl = '/test/resource/pool/capacity/task/list'; // 资源池容量列表
export const PoolCapacityDetailUrl = '/test/resource/pool/capacity/detail'; // 资源池容量详情

View File

@ -63,3 +63,35 @@ export type UpdateResourcePoolParams = Omit<ResourcePoolInfo, 'testResourceDTO'>
id: string;
testResourceDTO?: Partial<TestResourceDTO>;
};
// 资源池容量列表项
export interface CapacityTaskItem {
id: string;
taskId: string;
resourceId: string;
resourceName: string;
taskOrigin: string; // 任务来源任务组下的任务id
status: string; // 执行状态
result: string; // 执行结果
resourcePoolId: string;
resourcePoolNode: string;
resourceType: string; // 资源类型
projectId: string;
organizationId: string;
threadId: string; // 线程id
startTime: number;
endTime: number;
executor: string; // 执行人
num: number;
taskName: string;
userName: string;
resourcePoolName: string;
triggerMode: string;
lineNum: number;
}
export interface CapacityDetailType {
concurrentNumber: number; // 最大并发数
occupiedConcurrentNumber: number; // 剩余并发数
memoryUsage: number; // 内存使用量
cpuusage: number; // CPU占用量
}

View File

@ -11,15 +11,14 @@
<div class="flex items-center gap-[16px]">
<div class="count-resources">
{{ t('system.resourcePool.concurrentNumber') }}
<span class="capacity-count">100</span>
<span class="capacity-count">{{ capacityDetail.concurrentNumber }}</span>
</div>
<div class="count-resources">
{{ t('system.resourcePool.remainingConcurrency') }}
<span class="capacity-count">100</span>
<span class="capacity-count">{{ capacityDetail.occupiedConcurrentNumber }}</span>
</div>
</div>
</template>
<!-- TODO 等待联调 -->
<div class="flex items-center justify-between p-[16px]">
<div class="flex items-center gap-[8px]">
<a-select
@ -27,18 +26,23 @@
class="mr-[16px] w-[200px]"
:placeholder="t('common.pleaseSelect')"
allow-clear
@change="(val)=>changeNode(val as string)"
>
<template #prefix>
<div class="text-[var(--color-text-brand)]">{{ t('system.resourcePool.capacityNode') }}</div>
</template>
<template #tree-slot-title="node">
<a-tooltip :content="`${node.name}`" position="tl">
<div class="one-line-text w-[180px]">{{ node.name }}</div>
</a-tooltip>
</template>
<a-tooltip v-for="item of nodeList" :key="item.ip" :mouse-enter-delay="500" :content="item.ip">
<a-option :value="item.ip">
{{ item.ip }}
</a-option>
</a-tooltip>
</a-select>
<CapacityProgress :name="t('system.resourcePool.memory')" :percent="0.3" color="rgb(var(--link-6))" />
<CapacityProgress name="CPU" :percent="0.3" color="rgb(var(--success-6))" />
<CapacityProgress
:name="t('system.resourcePool.memory')"
:percent="capacityDetail.memoryUsage"
color="rgb(var(--link-6))"
/>
<CapacityProgress name="CPU" :percent="capacityDetail.cpuusage" color="rgb(var(--success-6))" />
</div>
<MsTag no-margin size="large" :tooltip-disabled="true" class="cursor-pointer" theme="outline" @click="searchList">
<MsIcon class="text-[16px] text-[var(color-text-4)]" :size="32" type="icon-icon_reset_outlined" />
@ -49,15 +53,15 @@
<template #[FilterSlotNameEnum.TEST_PLAN_REPORT_EXEC_STATUS]="{ filterContent }">
<ExecStatus :status="filterContent.value" />
</template>
<template #execStatus="{ record }">
<ExecStatus :status="record.execStatus" />
<template #result="{ record }">
<ExecutionStatus v-if="record.result !== '-'" :status="record.result" :module-type="ReportEnum.API_REPORT" />
</template>
<template #[FilterSlotNameEnum.API_TEST_CASE_API_REPORT_STATUS]="{ filterContent }">
<ExecutionStatus :module-type="ReportEnum.API_REPORT" :status="filterContent.value" />
</template>
<template #status="{ record }">
<ExecutionStatus :module-type="ReportEnum.API_REPORT" :status="record.status" />
<ExecStatus :status="record.status" />
</template>
</ms-base-table>
</div>
@ -76,10 +80,11 @@
import ExecutionStatus from '@/views/api-test/report/component/reportStatus.vue';
import ExecStatus from '@/views/test-plan/report/component/execStatus.vue';
import { getCapacityDetail, getCapacityTaskList } from '@/api/modules/setting/resourcePool';
import { useI18n } from '@/hooks/useI18n';
import { useTableStore } from '@/store';
import type { ResourcePoolItem } from '@/models/setting/resourcePool';
import type { CapacityDetailType, NodesListItem, ResourcePoolItem } from '@/models/setting/resourcePool';
import { ReportExecStatus } from '@/enums/apiEnum';
import { ReportEnum, ReportStatus } from '@/enums/reportEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
@ -106,33 +111,35 @@
});
const statusList = computed(() => {
return Object.keys(ReportStatus).map((key) => {
return {
value: key,
label: t(ReportStatus[key].label),
};
});
return Object.keys(ReportStatus)
.map((key) => {
return {
value: key,
label: t(ReportStatus[key].label),
};
})
.filter((e) => e.value !== 'FAKE_ERROR');
});
const columns: MsTableColumn = [
{
title: 'system.resourcePool.taskID',
slotName: 'ID',
dataIndex: 'id',
dataIndex: 'num',
showTooltip: true,
width: 200,
width: 120,
columnSelectorDisabled: true,
},
{
title: 'system.resourcePool.taskName',
slotName: 'taskName',
dataIndex: 'enable',
dataIndex: 'taskName',
showDrag: true,
},
{
title: 'system.resourcePool.useCaseName',
slotName: 'useCaseName',
dataIndex: 'useCaseName',
slotName: 'resourceName',
dataIndex: 'resourceName',
showTooltip: true,
width: 150,
showDrag: true,
@ -151,9 +158,9 @@
},
{
title: 'common.executionResult',
slotName: 'execStatus',
dataIndex: 'execStatus',
slotName: 'result',
dataIndex: 'result',
width: 200,
filterConfig: {
options: statusList.value,
filterSlotName: FilterSlotNameEnum.API_TEST_CASE_API_REPORT_STATUS,
@ -171,9 +178,9 @@
},
];
const { propsRes, propsEvent, loadList, setKeyword } = useTable(undefined, {
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getCapacityTaskList, {
tableKey: TableKeyEnum.SYSTEM_RESOURCE_POOL_CAPACITY,
scroll: { y: 'auto' },
scroll: { x: '100%' },
selectable: false,
showSetting: true,
heightUsed: 310,
@ -181,11 +188,71 @@
});
const selectedNode = ref<string>('');
const nodeList = computed<NodesListItem[]>(() => props.activeRecord?.testResourceDTO.nodesList ?? []);
const nodePort = computed(() => nodeList.value.find((e) => e.ip === selectedNode.value)?.port ?? '');
async function searchList() {
setLoadListParams({
poolId: props.activeRecord?.id,
ip: selectedNode.value,
port: nodePort.value,
});
loadList();
}
const defaultCapacityDetail: CapacityDetailType = {
concurrentNumber: 0,
occupiedConcurrentNumber: 0,
memoryUsage: 0,
cpuusage: 0,
};
const capacityDetail = ref<CapacityDetailType>({
...defaultCapacityDetail,
});
function resetCapacityDetail() {
capacityDetail.value = {
...defaultCapacityDetail,
};
}
async function initCapacityDetail() {
try {
capacityDetail.value = await getCapacityDetail({
poolId: props.activeRecord?.id,
ip: selectedNode.value,
port: nodePort.value,
current: 1,
pageSize: 10,
});
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
function changeNode(value: string) {
searchList();
if (value) {
initCapacityDetail();
} else {
resetCapacityDetail();
}
}
watch(
() => innerVisible.value,
(val) => {
if (val) {
searchList();
} else {
selectedNode.value = '';
resetCapacityDetail();
}
}
);
await tableStore.initColumn(TableKeyEnum.SYSTEM_RESOURCE_POOL_CAPACITY, columns, 'drawer');
</script>

View File

@ -1,11 +1,10 @@
<template>
<!-- TODO 等待联调 -->
<div class="progress-capacity">
<div class="capacity-attr">
<span>{{ props.name }}</span>
<span class="capacity-value"> {{ props.percent * 100 }} %</span>
<span class="capacity-value"> {{ Number.isNaN(props.percent) ? 0 : Number(props.percent.toFixed(1)) }} %</span>
</div>
<a-progress :percent="props.percent" :show-text="false" :color="props.color" />
<a-progress :percent="percentValue" :show-text="false" :color="props.color" />
</div>
</template>
@ -15,6 +14,10 @@
percent: number;
color: string;
}>();
const percentValue = computed(() => {
return Number(props.percent / 100);
});
</script>
<style scoped lang="less">

View File

@ -431,7 +431,6 @@
girdConcurrentNumber: 1,
podThreads: 1,
concurrentNumber: 10,
// TODO
singleTaskConcurrentNumber: 3,
nodesList: [
{

View File

@ -20,25 +20,30 @@
</div>
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
<template #name="{ record }">
<div class="flex w-full items-center justify-start gap-[8px]">
<a-tooltip :content="t('system.resourcePool.viewCapacityInfo')" :mouse-enter-delay="300" position="bottom">
<MsIcon
type="icon-icon_pie_filled"
class="cursor-pointer text-[rgb(var(--primary-5))]"
size="16"
@click="capacityDetail(record)"
/>
<div class="flex items-center gap-[8px]">
<a-tooltip
v-if="record.type !== 'Kubernetes'"
:content="t('system.resourcePool.viewCapacityInfo')"
:mouse-enter-delay="300"
position="bottom"
>
<div class="w-[16px]">
<MsIcon
type="icon-icon_pie_filled"
class="cursor-pointer text-[rgb(var(--primary-5))]"
size="16"
@click="capacityDetail(record)"
/>
</div>
</a-tooltip>
<a-button
type="text"
class="px-0"
:class="record.id === '100001100001' ? '' : 'max-w-full justify-start'"
<div
:class="`one-line-text cursor-pointer text-[rgb(var(--primary-5))] ${
record.id === '100001100001' ? 'max-w-[calc(100%-60px)]' : 'max-w-[calc(100%-16px)]'
}`"
@click="showPoolDetail(record.id)"
>
<div class="one-line-text">
{{ record.name }}
</div>
</a-button>
{{ record.name }}
</div>
<MsTag v-if="record.id === '100001100001'" size="small" tooltip-disabled>{{ t('common.default') }}</MsTag>
</div>
</template>
@ -62,9 +67,6 @@
</a-tooltip>
</div>
</template>
<template #lastConcurrentNumber="{ record }">
{{ record.lastConcurrentNumber || 0 }}
</template>
<template #action="{ record }">
<MsButton v-permission="['SYSTEM_TEST_RESOURCE_POOL:READ+UPDATE']" @click="editPool(record)">
{{ t('system.resourcePool.editPool') }}
@ -126,7 +128,7 @@
import { Message } from '@arco-design/web-vue';
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
import { FilterFormItem, FilterResult } from '@/components/pure/ms-advance-filter/type';
import { FilterFormItem } from '@/components/pure/ms-advance-filter/type';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsCard from '@/components/pure/ms-card/index.vue';
import type { Description } from '@/components/pure/ms-description/index.vue';
@ -226,13 +228,22 @@
columns.pop();
}
await tableStore.initColumn(TableKeyEnum.SYSTEM_RESOURCEPOOL, columns, 'drawer');
const { propsRes, propsEvent, loadList, setKeyword } = useTable(getPoolList, {
tableKey: TableKeyEnum.SYSTEM_RESOURCEPOOL,
columns,
scroll: { y: 'auto' },
selectable: false,
showSelectAll: false,
});
const { propsRes, propsEvent, loadList, setKeyword } = useTable(
getPoolList,
{
tableKey: TableKeyEnum.SYSTEM_RESOURCEPOOL,
columns,
scroll: { y: 'auto' },
selectable: false,
showSelectAll: false,
},
(item) => {
return {
...item,
lastConcurrentNumber: item.type === 'Kubernetes' ? '-' : item.lastConcurrentNumber || 0,
};
}
);
const keyword = ref('');
const filterConfigList = ref<FilterFormItem[]>([]);