feat: 资源池列表页面

This commit is contained in:
baiqi 2023-06-28 17:11:54 +08:00 committed by fit2-zhao
parent f1c8bfe713
commit 6b8df3cadd
16 changed files with 708 additions and 4 deletions

View File

@ -0,0 +1,12 @@
import MSR from '@/api/http/index';
import { PoolListUrl, UpdatePoolUrl } from '@/api/requrls/system/resourcePool';
import type { ResourcePoolItem } from '@/models/system/resourcePool';
import type { TableQueryParams } from '@/models/common';
export function getPoolList(data: TableQueryParams) {
return MSR.post<ResourcePoolItem[]>({ url: PoolListUrl, data });
}
export function updatePoolInfo(data: ResourcePoolItem) {
return MSR.post({ url: UpdatePoolUrl, data });
}

View File

@ -0,0 +1,4 @@
export const PoolListUrl = '/test/resource/pool/page';
export const UpdatePoolUrl = '/test/resource/pool/update';
export const AddPoolUrl = '/test/resource/pool/add';
export const DeletePoolUrl = '/test/resource/pool/delete';

View File

@ -0,0 +1,41 @@
<template>
<a-descriptions :data="props.descriptions" size="large" :column="1">
<a-descriptions-item v-for="item of props.descriptions" :key="item.label" :label="item.label">
<template v-if="item.isTag">
<a-tag
v-for="tag of item.value.split(',')"
:key="tag"
color="var(--color-text-n8)"
class="mr-[8px] font-normal !text-[var(--color-text-1)]"
>
{{ tag }}
</a-tag>
</template>
<div v-else>{{ item.value }}</div>
</a-descriptions-item>
</a-descriptions>
</template>
<script setup lang="ts">
export interface Description {
label: string;
value: string;
isTag?: boolean;
}
const props = defineProps<{ descriptions: Description[] }>();
</script>
<style lang="less" scoped>
.arco-descriptions-item-label {
@apply whitespace-pre-wrap font-normal;
}
.arco-descriptions-item-label-block {
padding-right: 16px !important;
word-wrap: break-word;
width: 120px;
}
.arco-descriptions-item-value-block {
@apply !pr-0 align-top;
}
</style>

View File

@ -0,0 +1,75 @@
<template>
<a-drawer v-model:visible="visible" :width="props.width" :footer="props.footer" @ok="handleOk" @cancel="handleCancel">
<template #title>
<slot name="title">
<div class="flex w-full justify-between">
{{ props.title }}
<a-tag v-if="titleTag" :color="props.titleTagColor" class="ml-[8px] mr-auto">{{ props.titleTag }}</a-tag>
<slot name="tbutton"></slot>
</div>
</slot>
</template>
<slot>
<MsDescription :descriptions="props.descriptions"></MsDescription>
</slot>
</a-drawer>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import MsDescription, { Description } from '@/components/pure/ms-description/index.vue';
interface DrawerProps {
visible: boolean;
title: string | undefined;
titleTag?: string;
titleTagColor?: string;
descriptions: Description[];
footer?: boolean;
[key: string]: any;
}
const props = withDefaults(defineProps<DrawerProps>(), {
footer: true,
});
const emit = defineEmits(['update:visible']);
const visible = ref(props.visible);
watch(
() => props.visible,
(val) => {
visible.value = val;
}
);
const handleOk = () => {
visible.value = false;
};
const handleCancel = () => {
visible.value = false;
emit('update:visible', false);
};
</script>
<style lang="less">
.arco-drawer {
@apply bg-white;
.arco-drawer-header {
height: 56px;
border-bottom: 1px solid var(--color-text-n8);
.arco-drawer-title {
@apply w-full;
}
.arco-drawer-close-btn {
margin-left: 16px;
color: var(--color-text-2);
}
}
.arco-drawer-footer {
@apply text-left;
border-bottom: 1px solid var(--color-text-n8);
}
}
</style>

View File

@ -48,6 +48,7 @@
selectedKeys?: (string | number)[]; selectedKeys?: (string | number)[];
actionConfig?: BatchActionConfig; actionConfig?: BatchActionConfig;
columns?: TableColumnData[]; columns?: TableColumnData[];
noDisable?: boolean;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'selectedChange', value: (string | number)[]): void; (e: 'selectedChange', value: (string | number)[]): void;
@ -121,7 +122,7 @@
}; };
function getRowClass(record: TableData) { function getRowClass(record: TableData) {
if (!record.raw.enable) { if (!record.raw.enable && !props.noDisable) {
return 'ms-table-row-disabled'; return 'ms-table-row-disabled';
} }
} }

View File

@ -172,6 +172,7 @@
.arco-layout-content { .arco-layout-content {
padding: 16px 16px 16px 0; padding: 16px 16px 16px 0;
min-width: 1000px; min-width: 1000px;
height: calc(100vh - 72px);
} }
} }
</style> </style>

View File

@ -25,6 +25,8 @@ export default {
'menu.settings.system.user': 'User', 'menu.settings.system.user': 'User',
'menu.settings.system.organizationAndProject': 'Org & Project', 'menu.settings.system.organizationAndProject': 'Org & Project',
'menu.settings.system.resourcePool': 'Resource Pool', 'menu.settings.system.resourcePool': 'Resource Pool',
'menu.settings.system.resourcePoolDetail': 'Add resource pool',
'menu.settings.system.resourcePoolEdit': 'Edit resource pool',
'navbar.action.locale': 'Switch to English', 'navbar.action.locale': 'Switch to English',
...sys, ...sys,
...localeSettings, ...localeSettings,

View File

@ -27,7 +27,6 @@ export default {
'menu.settings.system.resourcePool': '资源池', 'menu.settings.system.resourcePool': '资源池',
'menu.settings.system.resourcePoolDetail': '添加资源池', 'menu.settings.system.resourcePoolDetail': '添加资源池',
'menu.settings.system.resourcePoolEdit': '编辑资源池', 'menu.settings.system.resourcePoolEdit': '编辑资源池',
'menu.user': '个人中心',
'navbar.action.locale': '切换为中文', 'navbar.action.locale': '切换为中文',
...sys, ...sys,
...localeSettings, ...localeSettings,

View File

@ -5,6 +5,7 @@ import './message-box';
import './api-test'; import './api-test';
import './system/user'; import './system/user';
import './system/project'; import './system/project';
import './system/resourcePool';
Mock.setup({ Mock.setup({
timeout: '600-1000', timeout: '600-1000',

View File

@ -0,0 +1,166 @@
import Mock from 'mockjs';
import setupMock, { makeMockUrl, successTableResponseWrap } from '@/utils/setup-mock';
import { PoolListUrl } from '@/api/requrls/system/resourcePool';
const getPoolList = () => {
return [
{
id: '938uh9',
name: 'ksajdfkas',
type: 'Node',
description: '年卡是健康的就是肯德基富士康',
enable: true,
createTime: 0,
updateTime: 0,
createUser: 'hShdf',
apiTest: true,
loadTest: true,
uiTest: true,
serverUrl: 'fldskfsl.sdjks.com',
deleted: false,
configuration: 'akjfhbgkdjfsokdsdfs',
organizationList: [
{
id: 'string',
num: 0,
name: '组织 1',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '组织 2',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '组织 3',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '组织 4',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
],
resources: ['192.168.1.1', '192.168.11.23'],
},
{
id: 'dfopi03wif3',
name: 'ksajdfkas',
type: 'K8s',
description: '生动地很多次私董会佛山的',
enable: false,
createTime: 0,
updateTime: 0,
createUser: 'hShdf',
apiTest: true,
loadTest: true,
uiTest: true,
serverUrl: 'fldskfsl.sdjks.com',
deleted: false,
configuration: 'akjfhbgkdjfsokdsdfs',
organizationList: [
{
id: 'string',
num: 0,
name: '组织 1',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '组织 2',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '组织 3',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
{
id: 'string',
num: 0,
name: '组织 4',
description: 'blabla',
createTime: 0,
updateTime: 0,
createUser: 'string',
updateUser: 'string',
deleted: true,
deleteUser: 'string',
deleteTime: 0,
enable: true,
},
],
resources: ['192.168.1.1', '192.168.11.23'],
},
];
};
setupMock({
setup: () => {
Mock.mock(makeMockUrl(PoolListUrl), () => {
return successTableResponseWrap(getPoolList());
});
},
});

View File

@ -0,0 +1,20 @@
import type { OrganizationListItem } from './user';
export interface ResourcePoolItem {
id: string;
name: string;
type: string;
description: string;
enable: boolean;
createTime: number;
updateTime: number;
createUser: string;
apiTest: boolean;
loadTest: boolean;
uiTest: boolean;
serverUrl: string;
deleted: boolean;
configuration: string;
organizationList: OrganizationListItem[];
resources: string[];
}

View File

@ -11,7 +11,7 @@ export interface UserRoleListItem {
pos: number; pos: number;
} }
export interface OrganizationList { export interface OrganizationListItem {
id: string; id: string;
num: number; num: number;
name: string; name: string;
@ -41,7 +41,7 @@ export interface UserListItem {
lastProjectId: string; lastProjectId: string;
createUser: string; createUser: string;
updateUser: string; updateUser: string;
organizationList: OrganizationList[]; organizationList: OrganizationListItem[];
userRoleList: UserRoleListItem[]; userRoleList: UserRoleListItem[];
} }

View File

@ -0,0 +1,7 @@
<template>
<div> </div>
</template>
<script setup lang="ts"></script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,298 @@
<template>
<div class="mb-4 flex items-center justify-between">
<a-button type="primary" @click="addPool">
{{ t('system.resourcePool.createPool') }}
</a-button>
<a-input-search
:placeholder="t('system.resourcePool.searchPool')"
class="w-[230px]"
@search="searchPool"
></a-input-search>
</div>
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
<template #name="{ record }">
<a-button type="text" @click="showPoolDetail(record)">{{ record.name }}</a-button>
</template>
<template #enable="{ record }">
<div v-if="record.enable" class="flex items-center">
<icon-check-circle-fill class="mr-[2px] text-[rgb(var(--success-6))]" />
{{ t('system.resourcePool.tableEnable') }}
</div>
<div v-else class="flex items-center text-[var(--color-text-4)]">
<icon-stop class="mr-[2px]" />
{{ t('system.resourcePool.tableDisable') }}
</div>
</template>
<template #action="{ record }">
<MsButton @click="editPool(record)">{{ t('system.resourcePool.editPool') }}</MsButton>
<MsButton v-if="record.enable" @click="disabledPool(record)">{{
t('system.resourcePool.tableDisable')
}}</MsButton>
<MsButton v-else @click="enablePool(record)">{{ t('system.resourcePool.tableEnable') }}</MsButton>
<MsTableMoreAction :list="tableActions" @select="handleSelect($event, record)"></MsTableMoreAction>
</template>
</ms-base-table>
<MsDrawer
v-model:visible="showDetailDrawer"
width="480px"
:title="activePool?.name"
:title-tag="activePool?.enable ? t('system.resourcePool.tableEnable') : t('system.resourcePool.tableDisable')"
:title-tag-color="activePool?.enable ? 'green' : 'gray'"
:descriptions="activePoolDesc"
:footer="false"
>
<template #tbutton>
<a-button type="outline" size="mini" @click="editPool(activePool)">
{{ t('system.resourcePool.editPool') }}
</a-button>
</template>
</MsDrawer>
</template>
<script setup lang="ts">
import { onMounted, Ref, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue';
import { useI18n } from '@/hooks/useI18n';
import { getPoolList } from '@/api/modules/system/resourcePool';
import useModal from '@/hooks/useModal';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import useTable from '@/components/pure/ms-table/useTable';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import type { Description } from '@/components/pure/ms-description/index.vue';
import type { MsTableColumn } from '@/components/pure/ms-table/type';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import type { ResourcePoolItem } from '@/models/system/resourcePool';
const { t } = useI18n();
const router = useRouter();
const columns: MsTableColumn = [
{
title: 'system.resourcePool.tableColunmName',
slotName: 'name',
dataIndex: 'name',
width: 200,
},
{
title: 'system.resourcePool.tableColunmStatus',
slotName: 'enable',
dataIndex: 'enable',
},
{
title: 'system.resourcePool.tableColunmDescription',
dataIndex: 'description',
},
{
title: 'system.resourcePool.tableColunmType',
dataIndex: 'type',
},
{
title: 'system.resourcePool.tableColunmCreateTime',
dataIndex: 'createTime',
},
{
title: 'system.resourcePool.tableColunmUpdateTime',
dataIndex: 'updateTime',
},
{
title: 'system.resourcePool.tableColunmActions',
slotName: 'action',
fixed: 'right',
width: 120,
},
];
const { propsRes, propsEvent, loadList, setKeyword } = useTable(getPoolList, {
columns,
scroll: { y: 'auto' },
selectable: false,
showSelectAll: false,
});
const keyword = ref('');
onMounted(async () => {
setKeyword(keyword.value);
await loadList();
});
async function searchPool() {
setKeyword(keyword.value);
await loadList();
}
const { openModal } = useModal();
const tableActions: ActionsItem[] = [
{
label: 'system.resourcePool.delete',
eventTag: 'delete',
danger: true,
},
];
/**
* 启用资源池
*/
function enablePool(record: any) {
try {
Message.success(t('system.resourcePool.enablePoolSuccess'));
return true;
} catch (error) {
console.log(error);
return false;
}
}
/**
* 禁用资源池
*/
function disabledPool(record: any) {
openModal({
type: 'warning',
title: t('system.resourcePool.disablePoolTip', { name: record.name }),
content: t('system.resourcePool.disablePoolContent'),
okText: t('system.resourcePool.disablePoolConfirm'),
cancelText: t('system.resourcePool.disablePoolCancel'),
okButtonProps: {
status: 'danger',
},
onBeforeOk: async () => {
try {
Message.success(t('system.resourcePool.disablePoolSuccess'));
return true;
} catch (error) {
console.log(error);
return false;
}
},
hideCancel: false,
});
}
/**
* 删除资源池
*/
function deletePool(record: any) {
openModal({
type: 'warning',
title: t('system.resourcePool.deletePoolTip', { name: record.name }),
content: t('system.resourcePool.deletePoolContentUsed'),
okText: t('system.resourcePool.deletePoolConfirm'),
cancelText: t('system.resourcePool.deletePoolCancel'),
okButtonProps: {
status: 'danger',
},
onBeforeOk: async () => {
try {
Message.success(t('system.resourcePool.deletePoolSuccess'));
return true;
} catch (error) {
console.log(error);
return false;
}
},
hideCancel: false,
});
}
/**
* 处理表格更多按钮事件
* @param item
*/
function handleSelect(item: ActionsItem, record: any) {
switch (item.eventTag) {
case 'delete':
deletePool(record);
break;
default:
break;
}
}
const showDetailDrawer = ref(false);
const activePoolDesc: Ref<Description[]> = ref([]);
const activePool: Ref<ResourcePoolItem | null> = ref(null);
/**
* 查看资源池详情
* @param record
*/
function showPoolDetail(record: any) {
activePool.value = { ...record };
if (activePool.value) {
const poolUses = [
activePool.value.loadTest ? t('system.resourcePool.usePerformance') : '',
activePool.value.apiTest ? t('system.resourcePool.useAPI') : '',
activePool.value.uiTest ? t('system.resourcePool.useUI') : '',
];
activePoolDesc.value = [
{
label: t('system.resourcePool.detailDesc'),
value: activePool.value.description,
},
{
label: t('system.resourcePool.detailUrl'),
value: activePool.value.serverUrl,
},
{
label: t('system.resourcePool.detailRange'),
value: activePool.value.organizationList.map((e) => e.name).join(','),
isTag: true,
},
{
label: t('system.resourcePool.detailUse'),
value: poolUses.filter((e) => e !== '').join(','),
isTag: true,
},
{
label: t('system.resourcePool.detailMirror'),
value: activePool.value.configuration,
},
{
label: t('system.resourcePool.detailJMHeap'),
value: activePool.value.configuration,
},
{
label: t('system.resourcePool.detailType'),
value: activePool.value.configuration,
},
{
label: t('system.resourcePool.detailResources'),
value: activePool.value.resources.join(','),
isTag: true,
},
];
}
showDetailDrawer.value = true;
}
/**
* 编辑资源池
* @param record
*/
function editPool(record: any) {
router.push({
name: 'settingSystemResourcePoolDetail',
query: {
id: record.id,
},
});
}
/**
* 添加资源池
* @param record
*/
function addPool(record: any) {
router.push({
name: 'settingSystemResourcePoolDetail',
});
}
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,39 @@
export default {
'system.resourcePool.createPool': 'Add resource pool',
'system.resourcePool.searchPool': 'Search by name',
'system.resourcePool.delete': 'Delete',
'system.resourcePool.tableEnable': 'Enable',
'system.resourcePool.tableDisable': 'Disable',
'system.resourcePool.editPool': 'Edit',
'system.resourcePool.tableColunmName': 'Name',
'system.resourcePool.tableColunmStatus': 'Status',
'system.resourcePool.tableColunmDescription': 'Description',
'system.resourcePool.tableColunmType': 'Type',
'system.resourcePool.tableColunmCreateTime': 'CreateTime',
'system.resourcePool.tableColunmUpdateTime': 'UpdateTime',
'system.resourcePool.tableColunmActions': 'Actions',
'system.resourcePool.enablePoolSuccess': 'Enabled successfully',
'system.resourcePool.disablePoolTip': 'About to disable resource pool `{name}`',
'system.resourcePool.disablePoolContent': 'When disabled, tests using this resource pool will stop executing',
'system.resourcePool.disablePoolConfirm': 'Confirm',
'system.resourcePool.disablePoolCancel': 'Cancel',
'system.resourcePool.disablePoolSuccess': 'Disabled successfully',
'system.resourcePool.deletePoolTip': 'Are you sure to delete the `{name}` resource?',
'system.resourcePool.deletePoolContentUsed':
'This resource pool has been used, and related tests will stop immediately after deletion, please operate with caution!',
'system.resourcePool.deletePoolContentUnuse': 'This resource pool is not in use. Are you sure to delete it?',
'system.resourcePool.deletePoolConfirm': 'Confirm',
'system.resourcePool.deletePoolCancel': 'Cancel',
'system.resourcePool.deletePoolSuccess': 'Deleted successfully',
'system.resourcePool.detailDesc': 'Description',
'system.resourcePool.detailUrl': 'Current site URL',
'system.resourcePool.detailRange': 'Available range',
'system.resourcePool.detailUse': 'Use',
'system.resourcePool.detailMirror': 'Mirror',
'system.resourcePool.detailJMHeap': 'JMeter HEAP',
'system.resourcePool.detailType': 'Type',
'system.resourcePool.detailResources': 'Added resource',
'system.resourcePool.usePerformance': 'Performance test',
'system.resourcePool.useAPI': 'API test',
'system.resourcePool.useUI': ' UI test',
};

View File

@ -0,0 +1,38 @@
export default {
'system.resourcePool.createPool': '添加资源池',
'system.resourcePool.searchPool': '通过名称搜索',
'system.resourcePool.delete': '删除',
'system.resourcePool.tableEnable': '启用',
'system.resourcePool.tableDisable': '禁用',
'system.resourcePool.editPool': '编辑',
'system.resourcePool.tableColunmName': '名称',
'system.resourcePool.tableColunmStatus': '状态',
'system.resourcePool.tableColunmDescription': '描述',
'system.resourcePool.tableColunmType': '类型',
'system.resourcePool.tableColunmCreateTime': '创建时间',
'system.resourcePool.tableColunmUpdateTime': '更新时间',
'system.resourcePool.tableColunmActions': '操作',
'system.resourcePool.enablePoolSuccess': '启用成功',
'system.resourcePool.disablePoolTip': '即将禁用资源池 `{name}`',
'system.resourcePool.disablePoolContent': '禁用后,正在使用该资源池的测试会停止执行',
'system.resourcePool.disablePoolConfirm': '确认禁用',
'system.resourcePool.disablePoolCancel': '取消',
'system.resourcePool.disablePoolSuccess': '禁用成功',
'system.resourcePool.deletePoolTip': '确认删除 `{name}` 这个资源吗?',
'system.resourcePool.deletePoolContentUsed': '该资源池已被使用,删除后相关测试会立即停止,请谨慎操作!',
'system.resourcePool.deletePoolContentUnuse': '该资源池未被使用,是否确认删除?',
'system.resourcePool.deletePoolConfirm': '确认删除',
'system.resourcePool.deletePoolCancel': '取消',
'system.resourcePool.deletePoolSuccess': '删除成功',
'system.resourcePool.detailDesc': '描述',
'system.resourcePool.detailUrl': '当前站点 URL',
'system.resourcePool.detailRange': '可用范围',
'system.resourcePool.detailUse': '用途',
'system.resourcePool.detailMirror': '镜像',
'system.resourcePool.detailJMHeap': 'JMeter HEAP',
'system.resourcePool.detailType': '类型',
'system.resourcePool.detailResources': '已添加资源',
'system.resourcePool.usePerformance': '性能测试',
'system.resourcePool.useAPI': '接口测试',
'system.resourcePool.useUI': ' UI 测试',
};