update:213功能需求开发,bug修复
This commit is contained in:
@@ -20,3 +20,11 @@ export const stopHpcJobApi = (params: any) => {
|
||||
export const delHpcJobsApi = (params: any) => {
|
||||
return post(`${PREFIX}pbs/delHpcJobs`, params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询hpc作业资源接口
|
||||
* @returns
|
||||
*/
|
||||
export const queryHpcResourceApi = () => {
|
||||
return get(`${PREFIX}pbs/queryHpcResource`);
|
||||
};
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<div class="comp-hpc-list">
|
||||
<Dialog v-model="visible" diaTitle="作业执行列表" width="80%" height="90%" @close="closeFun">
|
||||
<div class="content">
|
||||
<el-tabs v-model="activeName" type="card" class="demo-tabs">
|
||||
<el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-change="changeTabsFun">
|
||||
<el-tab-pane label="HPC作业列表" name="cloud">
|
||||
<div class="table">
|
||||
<div class="table white">
|
||||
<BaseTable
|
||||
ref="baseTableRef"
|
||||
tableName="HPC_LIST_TABLE"
|
||||
@@ -40,9 +40,53 @@
|
||||
</BaseTable>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="本地作业执行列表" name="local">
|
||||
<div class="table"></div>
|
||||
</el-tab-pane> -->
|
||||
<el-tab-pane label="HPC资源详情" name="resources">
|
||||
<div class="table">
|
||||
<div class="chart-box">
|
||||
<div class="chart-box-left">
|
||||
<commonFilterChart
|
||||
title="资源运行状态"
|
||||
:charts-id="'chart-1'"
|
||||
:bar-type="'pieChart'"
|
||||
:option="chartOptionOne"
|
||||
></commonFilterChart>
|
||||
</div>
|
||||
<div class="chart-box-right">
|
||||
<commonFilterChart
|
||||
title="节点分配状态"
|
||||
:charts-id="'chart-2'"
|
||||
:bar-type="'pieChart'"
|
||||
:option="chartOptionTwo"
|
||||
></commonFilterChart>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pege-table">
|
||||
<BaseTable
|
||||
ref="baseTableRef2"
|
||||
:showSetting="false"
|
||||
tableName="HPC_RESOURCE_LIST"
|
||||
fullHeight
|
||||
hidePagination
|
||||
:data="hpcSourceList"
|
||||
>
|
||||
<template #nodeStatus="{ row }">
|
||||
<el-tag type="success" v-if="row.nodeStatus === 'Online'">在线</el-tag>
|
||||
<el-tag type="info" v-else-if="row.nodeStatus === 'Offline'">离线</el-tag>
|
||||
<el-tag type="warning" v-else>异常</el-tag>
|
||||
</template>
|
||||
|
||||
<template #efficiency="{ row }">
|
||||
<span>{{ row.freeCores }}</span
|
||||
>/<span>{{ row.totalCores }}</span
|
||||
>|<span>{{ getefficiencyFun(row) }}</span>
|
||||
</template>
|
||||
<template #nodeName="{ row }">
|
||||
<el-button link type="primary">{{ row.nodeName }}</el-button>
|
||||
</template>
|
||||
</BaseTable>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</Dialog>
|
||||
@@ -51,13 +95,14 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { delHpcJobsApi, queryJobsApi, stopHpcJobApi } from '@/api/pbs/pbs';
|
||||
import { delHpcJobsApi, queryHpcResourceApi, queryJobsApi, stopHpcJobApi } from '@/api/pbs/pbs';
|
||||
import Dialog from '@/components/common/dialog/index.vue';
|
||||
import BaseTable from '@/components/common/table/baseTable.vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { useDict } from '@/utils/useDict';
|
||||
import { queryAllApplicationApi } from '@/api/system/application';
|
||||
import { FileUtil } from '@/utils/file';
|
||||
import commonFilterChart from '@/components/common/echartCard/commonFilterChart.vue';
|
||||
|
||||
const { WORK_STATUS } = useDict('WORK_STATUS');
|
||||
|
||||
@@ -70,6 +115,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
});
|
||||
|
||||
const baseTableRef = ref();
|
||||
const baseTableRef2 = ref();
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const visible = ref(false);
|
||||
const activeName = ref('cloud');
|
||||
@@ -192,6 +238,135 @@ const getAppInfo = async () => {
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const hpcSourceList = ref<any>([]);
|
||||
const changeTabsFun = () => {
|
||||
if (activeName.value === 'resources') {
|
||||
queryHpcResourceFun();
|
||||
}
|
||||
};
|
||||
|
||||
const queryHpcResourceFun = async () => {
|
||||
try {
|
||||
const res: any = await queryHpcResourceApi();
|
||||
if (res && res.code === 200) {
|
||||
hpcSourceList.value = res.data.nodeList;
|
||||
|
||||
initChartOneFun(res.data);
|
||||
initChartTwoFun(res.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
const colorList = ref<any>(['#409eff', '#67c23a', '#909399']);
|
||||
|
||||
const chartOptionOne = ref();
|
||||
|
||||
const initChartOneFun = (data: any) => {
|
||||
chartOptionOne.value = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: '70%',
|
||||
top: 'center',
|
||||
icon: 'circle',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['60%', '70%'],
|
||||
center: ['30%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center',
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 40,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{ value: data.totalNodes || '', name: '全部' },
|
||||
{ value: data.totalNodes - data.freeNodes || '', name: '已使用' },
|
||||
{ value: data.freeNodes || '', name: '未使用' },
|
||||
],
|
||||
itemStyle: {
|
||||
color: function (params: any) {
|
||||
return colorList.value[params.dataIndex];
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const chartOptionTwo = ref();
|
||||
|
||||
const initChartTwoFun = (data: any) => {
|
||||
chartOptionTwo.value = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: '70%',
|
||||
top: 'center',
|
||||
icon: 'circle',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['60%', '70%'],
|
||||
center: ['30%', '50%'],
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center',
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 40,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{ value: data.totalCores || '', name: '全部' },
|
||||
{ value: data.usedCores || '', name: '已使用' },
|
||||
{ value: data.freeCores || '', name: '未使用' },
|
||||
],
|
||||
itemStyle: {
|
||||
color: function (params: any) {
|
||||
return colorList.value[params.dataIndex];
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
// 获取CPU使用效率
|
||||
const getefficiencyFun = (row: any) => {
|
||||
let num: any = '0';
|
||||
|
||||
if (row.totalCores) {
|
||||
if (row.freeCores === row.totalCores || !row.usedCores) {
|
||||
num = '0';
|
||||
} else if (row.usedCores === row.totalCores || !row.freeCores) {
|
||||
num = '100%';
|
||||
} else {
|
||||
num = (row.usedCores / row.totalCores) * 100 + '%';
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await getAppInfo();
|
||||
});
|
||||
@@ -203,8 +378,37 @@ onMounted(async () => {
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
|
||||
.table {
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
background-color: #f2f3f5;
|
||||
|
||||
.chart-box {
|
||||
width: 100%;
|
||||
height: 350px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.chart-box-left,
|
||||
.chart-box-right {
|
||||
width: calc(50% - 5px);
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.pege-table {
|
||||
width: 100%;
|
||||
height: calc(100% - 360px);
|
||||
margin-top: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.white {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -469,3 +469,17 @@ export const base64ToStrFun = (str: any) => {
|
||||
const base64Str = btoa(binaryStr);
|
||||
return base64Str;
|
||||
};
|
||||
|
||||
/**
|
||||
* 判断字符串是否符合JSON标准
|
||||
* @param str 输入字符串
|
||||
* @returns 返回是否
|
||||
*/
|
||||
export const isJSONFun = (str: any) => {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,13 +3,14 @@ import { nextTick } from 'vue';
|
||||
import { registerCustomNode } from './registerNode';
|
||||
import { queryAllApplicationApi, queryApplicationConfigApi } from '@/api/system/application';
|
||||
import { CommonStore } from '@/stores/common';
|
||||
import { FileUtil } from '@/utils/file';
|
||||
import { FileUtil, isJSONFun } from '@/utils/file';
|
||||
// import startImg from '@/assets/imgs/dragFlow/dragIcon/start.svg';
|
||||
import stencilStartImg from '@/assets/imgs/dragFlow/dragIcon/stencil_start.svg';
|
||||
import stencilEndImg from '@/assets/imgs/dragFlow/dragIcon/stencil_end.svg';
|
||||
import { FLOW_CREATE_ID } from './initGraph';
|
||||
import { FLOW_NODE_TYPE_ENUM } from '@/utils/enum/flow';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { getUserId } from '@/utils/user';
|
||||
|
||||
const commonStore = CommonStore();
|
||||
|
||||
@@ -155,6 +156,19 @@ export const getNodeList = async (noload?: any) => {
|
||||
const apps: any = res.data.data || [];
|
||||
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
const paths = isJSONFun(apps[i].appPath)
|
||||
? JSON.parse(apps[i].appPath as string)
|
||||
: apps[i].appPath;
|
||||
|
||||
if (paths && paths instanceof Object) {
|
||||
const keys = Object.keys(paths);
|
||||
if (keys.includes(getUserId())) {
|
||||
apps[i].appPath = paths[getUserId()];
|
||||
} else {
|
||||
apps[i].appPath = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (apps[i].appStatus === 1) {
|
||||
// if (!noload) {
|
||||
// apps[i].nodeParamConfigName = await getAppConfigListFun(apps[i].uuid);
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
:close-on-click-modal="false"
|
||||
@close="closeFun"
|
||||
>
|
||||
|
||||
<!-- <Dialog
|
||||
v-model="visible"
|
||||
:diaTitle="`${currentRow?.id ? '编辑' : '新增'}应用`"
|
||||
:width="'25%'"
|
||||
:height="'70%'"
|
||||
@close="closeFun"
|
||||
show-footer
|
||||
> -->
|
||||
<div class="content" v-loading="loading">
|
||||
<TableForm ref="tableFormRef" :tableName="tableName" />
|
||||
|
||||
@@ -20,18 +29,21 @@
|
||||
<el-button type="primary" @click="submitFun">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- </Dialog> -->
|
||||
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, nextTick } from 'vue';
|
||||
import { ref, watch, nextTick, onMounted } from 'vue';
|
||||
import TableForm from '@/components/common/table/tableForm.vue';
|
||||
import appNameBg from '@/views/task/appCenter/components/appNameBg.vue';
|
||||
import html2canvas from 'html2canvas';
|
||||
import { dataUploadAvatarApi } from '@/api/data/data';
|
||||
import { delayTime } from '@/utils/common';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import Dialog from '@/components/common/dialog/index.vue';
|
||||
|
||||
const props = defineProps(['currentAppInfo', 'tableName']);
|
||||
const emit = defineEmits(['cancel', 'submit']);
|
||||
@@ -98,22 +110,35 @@ const updatePngFun = async (name: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.currentAppInfo,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
currentRow.value = cloneDeep(newVal);
|
||||
nextTick(() => {
|
||||
if (currentRow.value?.id) {
|
||||
tableFormRef.value.setFormDataFun(currentRow.value);
|
||||
} else {
|
||||
tableFormRef.value.resetFun();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
// watch(
|
||||
// () => props.currentAppInfo,
|
||||
// (newVal) => {
|
||||
// if (newVal) {
|
||||
// currentRow.value = cloneDeep(newVal);
|
||||
// nextTick(() => {
|
||||
// if (currentRow.value?.id) {
|
||||
// tableFormRef.value.setFormDataFun(currentRow.value);
|
||||
// } else {
|
||||
// tableFormRef.value.resetFun();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// { immediate: true }
|
||||
// );
|
||||
|
||||
onMounted(() => {
|
||||
if (props.currentAppInfo) {
|
||||
currentRow.value = cloneDeep(props.currentAppInfo);
|
||||
nextTick(() => {
|
||||
if (currentRow.value?.id) {
|
||||
tableFormRef.value.setFormDataFun(currentRow.value);
|
||||
} else {
|
||||
tableFormRef.value.resetFun();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -100,6 +100,7 @@ import appNameBg from '@/views/task/appCenter/components/appNameBg.vue';
|
||||
import { getDictionaryDataApi } from '@/api/system/systemData';
|
||||
import { getUserId } from '@/utils/user';
|
||||
import appUseChart from './components/appUseChart.vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const env = import.meta.env;
|
||||
const baseTableRef = ref();
|
||||
@@ -164,12 +165,35 @@ const setTableDataFun = async (data: any) => {
|
||||
for (let i = 0; i < res?.data?.data?.length; i++) {
|
||||
res.data.data[i].appStatus = res?.data?.data[i].appStatus.toString();
|
||||
res.data.data[i].appType = res?.data?.data[i].appType.toString();
|
||||
res.data.data[i].userAppPath = cloneDeep(res.data.data[i].appPath);
|
||||
|
||||
const paths = isJSONFun(res.data.data[i].appPath)
|
||||
? JSON.parse(res.data.data[i].appPath as string)
|
||||
: res.data.data[i].appPath;
|
||||
|
||||
if (paths && paths instanceof Object) {
|
||||
const keys = Object.keys(paths);
|
||||
if (keys.includes(getUserId())) {
|
||||
res.data.data[i].appPath = paths[getUserId()];
|
||||
} else {
|
||||
res.data.data[i].appPath = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
const isJSONFun = (str: any) => {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const currentAppInfo = ref<any>({});
|
||||
// 打开编辑应用弹窗
|
||||
const editAppInfoFun = (row?: any) => {
|
||||
@@ -177,7 +201,7 @@ const editAppInfoFun = (row?: any) => {
|
||||
|
||||
// if (uuid) {
|
||||
if (row) {
|
||||
currentAppInfo.value = row;
|
||||
currentAppInfo.value = cloneDeep(row);
|
||||
} else {
|
||||
currentAppInfo.value = {};
|
||||
}
|
||||
@@ -234,16 +258,24 @@ const deleteAppFun = async (row: any) => {
|
||||
};
|
||||
|
||||
const updateAppInfoFun = async (data: any) => {
|
||||
console.log(data, 'datadatadatadata');
|
||||
const uuid =
|
||||
data.appType === 1 || data.appType === '1' ? localStorage.getItem('USER_UUID') : '--';
|
||||
detailVisible.value = false;
|
||||
|
||||
if (uuid) {
|
||||
if (data?.id) {
|
||||
let paths: any = {};
|
||||
if (isJSONFun(currentAppInfo.value.userAppPath)) {
|
||||
paths = JSON.parse(currentAppInfo.value.userAppPath);
|
||||
}
|
||||
|
||||
paths[getUserId()] = data.appPath || '';
|
||||
|
||||
const res: any = await updateApplicationApi({
|
||||
appName: data.appName,
|
||||
appType: Number(data.appType),
|
||||
appPath: data.appPath,
|
||||
appPath: JSON.stringify(paths) || '',
|
||||
appImage: data.appImage.toString(),
|
||||
appStatus: Number(data.appStatus),
|
||||
appVersion: data.appVersion,
|
||||
@@ -256,11 +288,15 @@ const updateAppInfoFun = async (data: any) => {
|
||||
if (res && res.code === 200) {
|
||||
}
|
||||
} else {
|
||||
const path = JSON.stringify({
|
||||
[getUserId()]: data.appPath,
|
||||
});
|
||||
|
||||
const param = {
|
||||
appName: data.appName,
|
||||
appType: Number(data.appType),
|
||||
appStatus: Number(data.appStatus),
|
||||
appPath: data.appPath || '',
|
||||
appPath: path || '',
|
||||
appImage: data?.appImage ? data.appImage.toString() : '',
|
||||
appVersion: data.appVersion || '',
|
||||
appVendor: data.appVendor || '',
|
||||
|
||||
@@ -353,7 +353,7 @@ import { getDictionaryDataApi } from '@/api/system/systemData';
|
||||
import { getUserData, getUserId, getUserTenantId } from '@/utils/user';
|
||||
import reportResult from './runPagecomponent/reportResult.vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { base64ToStrFun } from '@/utils/file';
|
||||
import { base64ToStrFun, isJSONFun } from '@/utils/file';
|
||||
|
||||
const props = defineProps({
|
||||
runInfo: {
|
||||
@@ -734,6 +734,21 @@ const getAppInfo = async (uuid: any) => {
|
||||
try {
|
||||
const res: any = await queryAllApplicationApi(param);
|
||||
if (res && res.code === 200) {
|
||||
for (let i = 0; i < res.data.data.length; i++) {
|
||||
const paths = isJSONFun(res.data.data[i].appPath)
|
||||
? JSON.parse(res.data.data[i].appPath as string)
|
||||
: res.data.data[i].appPath;
|
||||
|
||||
if (paths && paths instanceof Object) {
|
||||
const keys = Object.keys(paths);
|
||||
if (keys.includes(getUserId())) {
|
||||
res.data.data[i].appPath = paths[getUserId()];
|
||||
} else {
|
||||
res.data.data[i].appPath = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const appPath = res.data.data.find((item: any) => {
|
||||
return item.uuid === uuid;
|
||||
})?.appPath;
|
||||
@@ -906,18 +921,21 @@ const getNodeFileIdAndNames = async () => {
|
||||
fileIds.push(list[i].id.toString());
|
||||
fileNames.push(list[i].originalName);
|
||||
}
|
||||
if (flowNodeParamData.value.nodeExeCommand) {
|
||||
taskCmdParam[flowNodeParamData.value.nodeExeCommand] = fileNames.join(',');
|
||||
// = fileNames.map((item: any) => {
|
||||
// return { [flowNodeParamData.value.nodeExeCommand]: item };
|
||||
// });
|
||||
} else {
|
||||
taskCmdParam = {};
|
||||
}
|
||||
// if (flowNodeParamData.value.nodeExeCommand) {
|
||||
// taskCmdParam[flowNodeParamData.value.nodeExeCommand] = './' + fileNames.join(',');
|
||||
// // = fileNames.map((item: any) => {
|
||||
// // return { [flowNodeParamData.value.nodeExeCommand]: item };
|
||||
// // });
|
||||
// } else {
|
||||
// taskCmdParam = {};
|
||||
// }
|
||||
|
||||
if (Object.keys(flowNodeData.value.userParams).includes('--export')) {
|
||||
taskCmdParam['--export'] = flowNodeData.value.userParams['--export'];
|
||||
}
|
||||
if (Object.keys(flowNodeData.value.userParams).includes('--import')) {
|
||||
taskCmdParam['--import'] = './' + fileNames.join(',');
|
||||
}
|
||||
return {
|
||||
fileIds,
|
||||
fileNames,
|
||||
|
||||
Reference in New Issue
Block a user