This commit is contained in:
2025-11-07 09:18:10 +08:00
18 changed files with 744 additions and 423 deletions

View File

@@ -142,3 +142,65 @@ export const deleteSimulationParameterApi = (params: any) => {
return get(`${PREFIX}simulationParameterLibrary/deleteSimulationParameter`, params);
};
/**
* 查询用户配额
* @param params
* @returns
*/
export const listUserQuotaApi = (params: any) => {
return post(`${PREFIX}dataStorageAnalysis/listUserQuota`, params);
};
/**
* 新增用户配额
* @param params
* @returns
*/
export const addUserQuotaApi = (params: any) => {
return post(`${PREFIX}dataStorageAnalysis/addUserQuota`, params);
};
/**
* 批量修改用户配额
* @param params
* @returns
*/
export const batchUpdateUserQuotaApi = (params: any) => {
return post(`${PREFIX}dataStorageAnalysis/batchUpdateUserQuota`, params);
};
/**
* 存储系统大文件筛选
* @param params
* @returns
*/
export const listBigFileApi = (params: any) => {
return post(`${PREFIX}dataStorageAnalysis/listBigFile`, params);
};
/**
* 批量删除大文件
* @param params
* @returns
*/
export const batchDeleteBigFileApi = (params: any) => {
return post(`${PREFIX}dataStorageAnalysis/batchDeleteBigFile`, params);
};
/**
* 根据nodeId(项目nideId)获取指定查询类型文件空间占用
* @param params
* @returns
*/
export const getNodeSizeByNodeTypeApi = (params: any) => {
return get(`${PREFIX}dataStorageAnalysis/getNodeSizeByNodeType`, params);
};
/**
* 根据用户组和用户id获取用户的空间占用
* @param params
* @returns
*/
export const getDirectorySizeByUserIdApi = (params: any) => {
return get(`${PREFIX}dataStorageAnalysis/getDirectorySizeByUserId`, params);
};

View File

@@ -13,7 +13,7 @@ export const queryTaskListApi = (params: any) => {
};
export const updateTaskStatusApi = (params: any) => {
return get(`${PREFIX}task/operation`, params);
return post(`${PREFIX}task/operation`, params);
};
export const attentionTaskApi = (params: any) => {

View File

@@ -17,9 +17,12 @@
</div>
<slot name="otherLeftOptions"></slot>
</template>
<!-- <template #eMemberList="{row}">
<template #eMemberList="{row}">
{{ disposeMemberList(row,'eMemberList') }}
</template> -->
</template>
<template #pMemberList="{row}">
{{ disposeMemberList(row,'pMemberList') }}
</template>
<template #tableActions="{ row }">
<div class="gl-table-actions" v-if="!readonly">
<el-link type="primary" @click="editTableRowFun(row)">编辑</el-link>
@@ -338,9 +341,10 @@ const onNodeDetailOkFun = (formData: any) => {
if (addRow.nodeType === NODE_TYPE.TASK) {
addRow.exeStatus = TASK_PROCESS_STATUS.NO_STARTED;
addRow.achieveStatus = TASK_CALCULATE_STATUS.NO_CALCULATE;
addRow.eMemberListStr = addRow.eMemberList;
addRow.eMemberList = addRow.eMemberListArr;
addRow.pMemberList = addRow.pMemberListArr;
}
console.log('addRow', addRow);
// 给新增节点赋值tagKeyMapList
// const tagKeyList = disposeTagKey(addRow, checkRowData[0]?.tagKeyList || [], tagKeyMapList.value);
// if (addRow?.nodeType === NodeTaskType.ROUNDS || addRow.type === TaskType.PERFORMANCE) {
@@ -387,9 +391,13 @@ const onNodeDetailOkFun = (formData: any) => {
// for (const key in formData) {
// item[key] = formData[key];
// }
getVxeRef().setRow(item, formData).then(res => {
const rowInfo = {
...formData,
eMemberList: formData.eMemberListArr,
pMemberList: formData.pMemberListArr,
};
getVxeRef().setRow(item, rowInfo).then(res => {
console.log('res', res);
});
}
});

View File

@@ -8,10 +8,10 @@
:diaTitle="diaTitle"
@show="onShowFun"
:confirm-closable="false"
width="800"
width="400"
>
<template #default>
<TableForm :colNum="2" ref="tableFormRef" :tableName="tableName" @change="onFormChangeFun">
<TableForm :colNum="detail.nodeType === NODE_TYPE.TASK?2:1" ref="tableFormRef" :tableName="tableName" @change="onFormChangeFun">
<template #form-standard>
<el-cascader
v-model="standard"
@@ -41,6 +41,7 @@ import TableForm from '@/components/common/table/tableForm.vue';
import { dataListDirApi, dataQueryDirApi } from '@/api/data/data';
import { cloneDeep } from 'lodash-es';
import type { CascaderProps } from 'element-plus';
import { NODE_TYPE } from '@/utils/enum/node';
interface Props {
modelValue: boolean;
@@ -79,6 +80,9 @@ const onFormChangeFun = (data:any) => {
if (data.key === 'eMemberList') {
data.data.eMemberListArr = data.val;
}
if (data.key === 'pMemberList') {
data.data.pMemberListArr = data.val;
}
};
const onCancelFun = () => {
visible.value = false;

View File

@@ -8,3 +8,7 @@ export const getUserData = () => {
export const getUserId = () => {
return getUserData().user_id;
};
export const getUserTenantId = () => {
return getUserData().tenant_id;
};

View File

@@ -668,7 +668,6 @@ const loadcaseAnalysisVisable = ref(false);
const selectAnalysisData = ref<any>({});
const openDataAnalysisFn = (flag:any, tableName:any) => {
selectAnalysisData.value = {
flag,
data: compareList.value,

View File

@@ -0,0 +1,124 @@
<template>
<div class="setting-page">
<BaseTable
tableName="FILE_STORAGE_MANAGE_TABLE"
ref="baseTableRef"
:api="listBigFileApi"
:params="{
fileName,
createTime,
fileSize,
fileSizeUnit
}"
showCheckbox
showIndex
>
<template #leftOptions>
<div>
<el-form :inline="true" class="filter-form" :model="filterFormData">
<el-form-item label="文件名称:">
<el-input v-model="filterFormData.fileName"></el-input>
</el-form-item>
<el-form-item label="文件大小(≥)">
<el-input class="w80 mr5" v-model="filterFormData.fileSize"></el-input>
<el-select class="w80" v-model="filterFormData.fileSizeUnit">
<el-option v-for="item in unitList" :label="item" :value="item" :key="item"></el-option>
</el-select>
</el-form-item>
<el-form-item label="文件生成时间:" >
<el-date-picker
v-model="filterFormData.createTime"
type="datetime"
placeholder="选择具体的时间"
format="YYYY-MM-DD HH:mm:ss"
date-format="MMM DD, YYYY"
time-format="HH:mm"
/>
</el-form-item>
<el-form-item >
<el-button type="" @click="searchFn">搜索</el-button>
<el-button type="primary" @click="deleteFileBatchFn">批量删除</el-button>
</el-form-item>
</el-form>
</div>
</template>
</BaseTable>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, reactive } from 'vue';
import BaseTable from '@/components/common/table/baseTable.vue';
import { listBigFileApi, batchDeleteBigFileApi } from '@/api/data/data';
import { ElMessage } from 'element-plus';
const baseTableRef = ref();
const filterFormData = reactive<any>({
fileName: '',
fileSize: '',
fileSizeUnit: 'TB',
createTime: '',
});
const fileName = ref('');
const createTime = ref('');
const fileSize = ref('');
const fileSizeUnit = ref('');
const unitList = ref(['TB', 'GB', 'MB', 'KB']);
const searchFn = () => {
fileName.value = filterFormData.fileName;
createTime.value = filterFormData.createTime;
fileSize.value = filterFormData.fileSize;
fileSizeUnit.value = filterFormData.fileSizeUnit;
};
const deleteFileBatchFn = async () => {
const checkList:any = baseTableRef.value?.tableRef?.getCheckboxRecords() || [];
if (checkList.length) {
const param = checkList.map((item:any) => {
return item.id;
});
const res:any = await batchDeleteBigFileApi(param);
if (res && res.code === 200) {
ElMessage.success('删除成功');
searchFn();
}
} else {
ElMessage.warning('请选择文件');
}
};
</script>
<style lang="scss" scoped>
.setting-page{
width: 100%;
height: 100%;
.filter-form{
.el-form-item{
margin-bottom: 0 !important;
.w80{
width: 80px;
}
.mr5{
margin-right: 5px;
}
}
}
}
</style>

View File

@@ -0,0 +1,153 @@
<template>
<div class="setting-page">
<BaseTable
tableName="USER_STORAGE_TABLE"
ref="baseTableRef"
:api="listUserQuotaApi"
:params="{
userName
}"
showCheckbox
showIndex
>
<template #leftOptions>
<div>
<el-form :inline="true" class="filter-form" :model="filterFormData">
<el-form-item label="用户名:">
<el-input v-model="filterFormData.userName"></el-input>
</el-form-item>
<el-form-item label="用户初始存储:">
<el-input class="w80 mr5" v-model="filterFormData.quotaValue"></el-input>
<el-select class="w80" v-model="filterFormData.quotaUnit">
<el-option v-for="item in unitList" :label="item" :value="item" :key="item"></el-option>
</el-select>
</el-form-item>
<el-form-item >
<el-button @click="searchFn">搜索</el-button>
<el-button type="primary" @click="batchEditFn">批量修改</el-button>
</el-form-item>
</el-form>
</div>
</template>
<template #operate="{row}">
<el-button type="primary" link @click="editUserStorageFn(row)"> 编辑</el-button>
</template>
</BaseTable>
</div>
<el-drawer
v-model="visible"
title="新增用户配额"
:size="400"
:close-on-click-modal="false"
@close="closeFun"
>
<div class="content">
<TableForm ref="tableFormRef" tableName="USER_STORAGE_TABLE" />
</div>
<template #footer>
<div>
<el-button @click="closeFun">关闭</el-button>
<el-button type="primary" @click="submitFun">确定</el-button>
</div>
</template>
</el-drawer>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, reactive } from 'vue';
import BaseTable from '@/components/common/table/baseTable.vue';
import { listUserQuotaApi, addUserQuotaApi, batchUpdateUserQuotaApi } from '@/api/data/data';
import TableForm from '@/components/common/table/tableForm.vue';
import { ElMessage } from 'element-plus';
const baseTableRef = ref();
const visible = ref(false);
const tableFormRef = ref();
const userName = ref('');
const filterFormData = reactive<any>({
userName: '',
quotaValue: '',
quotaUnit: 'TB',
});
const unitList = ref(['TB', 'GB', 'MB', 'KB']);
const editUserStorageFn = (row:any) => {
visible.value = true;
tableFormRef.value.setFormDataFun(row);
};
const closeFun = () => {
visible.value = false;
};
const submitFun = () => {
};
const searchFn = () => {
userName.value = filterFormData.userName;
};
const batchEditFn = async () => {
const checkList:any = baseTableRef.value?.tableRef?.getCheckboxRecords() || [];
if (!filterFormData.quotaValue.length || !Number(filterFormData.quotaValue)) {
ElMessage.warning('请填写正确的存储空间大小');
return;
}
if (checkList.length) {
const params = checkList.map((item:any) => {
return {
userId: item.uuid,
quotaValue: filterFormData.quotaValue,
quotaUnit: filterFormData.quotaUnit,
};
});
const res:any = await batchUpdateUserQuotaApi(params);
if (res && res.code === 200) {
searchFn();
}
} else {
ElMessage.warning('请选择用户后进行修改');
}
};
</script>
<style lang="scss" scoped>
.setting-page{
width: 100%;
height: 100%;
.filter-form{
.el-form-item{
margin-bottom: 0 !important;
.w80{
width: 80px;
}
.mr5{
margin-right: 5px;
}
}
}
}
</style>

View File

@@ -0,0 +1,202 @@
<template>
<div class="storage-page">
<div class="operate-box">
<el-radio-group v-model="radio" @change="radioChangeFn">
<el-radio value="default" size="large">默认</el-radio>
<el-radio value="increment" size="large">增量</el-radio>
</el-radio-group>
</div>
<!-- 项目存储空间统计 -->
<div class="chart-item" >
<div class="chart-filter">
<span>项目</span>
<div class="project-name-content">
<ProjectSelect
class="select-width margin-right-12"
v-model="projectStorageSpaceStatisticsFormData.nodeId"
@change="initProjectStorageSpaceStatisticsFn"
/>
</div>
</div>
<div class="chart-content" id="chart1" ref="projectStorageSpaceStatisticsRef"></div>
</div>
<!-- 学科存储空间统计 -->
<div class="chart-item" >
<div class="chart-filter">
<span>学科</span>
<div class="project-name-content">
<el-select v-model="statisticsOfSubjectStorageSpaceFormData.nodeId" @change="initStatisticsOfSubjectStorageSpaceFn">
<el-option v-for="item in disciplineOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</div>
</div>
<div class="chart-content" id="chart2" ref="statisticsOfSubjectStorageSpaceRef"></div>
</div>
<!-- 用户存储空间统计 -->
<div class="chart-item" >
<div class="chart-filter">
<span>用户组</span>
<div class="project-name-content mr10" >
<el-select
clearable
class="select-width margin-right-12"
v-model="userStorageSpaceStatisticsFormData.userGroupId"
@change="initUserStorageSpaceStatisticsFn"
>
<el-option
v-for="item in userGroupOptions"
:label="item.label"
:value="item.value"
:key="item.value"
></el-option>
</el-select>
</div>
<span>用户</span>
<div class="project-name-content">
<UserSelect
class="select-width"
v-model="userStorageSpaceStatisticsFormData.userIds"
@change="initUserStorageSpaceStatisticsFn"
/>
</div>
</div>
<div class="chart-content" id="chart3" ref="userStorageSpaceStatisticsRef"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, reactive, onMounted } from 'vue';
import ProjectSelect from '@/components/common/projectSelect/index.vue';
import UserSelect from '@/components/common/userSelect/index.vue';
import { queryNodeListApi } from '@/api/project/node';
import { userQueryGroupApi } from '@/api/system/user';
const radio = ref('default');
const radioChangeFn = () => {
};
// 项目存储空间统计
const projectStorageSpaceStatisticsRef = ref();
const projectStorageSpaceStatisticsFormData = reactive({
nodeId: '',
});
const initProjectStorageSpaceStatisticsFn = async () => {
};
// 学科存储空间统计
const statisticsOfSubjectStorageSpaceRef = ref();
const statisticsOfSubjectStorageSpaceFormData = reactive({
nodeId: '',
});
const initStatisticsOfSubjectStorageSpaceFn = async () => {
};
// 用户存储空间统计
const userStorageSpaceStatisticsRef = ref();
const userStorageSpaceStatisticsFormData = reactive({
userIds: '',
userGroupId: '',
});
const initUserStorageSpaceStatisticsFn = async () => {
};
const disciplineOptions = ref<any>([]);
const getDisciplineOptionsFun = async () => {
queryNodeListApi({ current: 1, size: 999, nodeType: 'discipline', pid: null }).then((res: any) => {
if (res.code === 200) {
disciplineOptions.value = res.data.data.map((item: any) => {
return {
label: item.nodeName,
value: item.id,
};
});
}
});
};
const userGroupOptions = ref<any>([]);
const getGroupList = async () => {
const res: any = await userQueryGroupApi({
current: 1,
size: 1000,
});
if (res && res.code === 200) {
userGroupOptions.value = [];
res.data.data.forEach((item: any) => {
userGroupOptions.value.push({
label: item.groupName,
value: item.id,
});
});
if (userGroupOptions.value?.length) {
userStorageSpaceStatisticsFormData.userGroupId = userGroupOptions.value[0].value;
}
}
};
onMounted(async () => {
await getDisciplineOptionsFun();
await getGroupList();
});
</script>
<style lang="scss" scoped>
.storage-page {
width: 100%;
padding: 10px;
display: flex;
flex-wrap: wrap;
.operate-box{
width: 100%;
height: 50px;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 10px;
}
.chart-item {
width: calc(50% - 10px);
height: 400px;
margin-right: 10px;
margin-bottom: 10px;
border: 1px solid #e4e4e4;
.chart-filter{
width: 100%;
height: 40px;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 10px;
.project-name-content{
width: 150px;
margin-left: 10px;
}
.mr10{
margin-right: 10px;
}
}
}
}
</style>

View File

@@ -1,61 +1,23 @@
<template>
<div class="gl-page-content-grey-full">
<div class="content">
<div class="chart-box margin-style" >
<EchartCard
title="项目存储空间统计"
ref="projectStorageSpaceStatisticsRef"
:charts-id="'chart-1'"
:bar-type="'barChart'"
@refresh="changeProjectFun"
>
<template #formSlot>
<el-form :inline="true">
<el-form-item label="项目" >
<el-select v-model="projectStorageSpaceStatisticsFormData.project" clearable @change="changeProjectFun">
<el-option v-for="item in projectOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</el-form-item>
</el-form>
</template>
</EchartCard>
<div class="page-title">
<el-radio-group v-model="currentTab" @change="currentTabChangeFn">
<el-radio-button label="存储统计" value="Storage statistics"></el-radio-button>
<el-radio-button label="存储设置" value="Storage Settings"></el-radio-button>
<el-radio-button label="文件管理" value="Document Management"></el-radio-button>
</el-radio-group>
</div>
<div class="chart-box">
<EchartCard
title="学科存储空间统计"
ref="disciplineStorageSpaceStatisticsRef"
:charts-id="'chart-2'"
:bar-type="'pieChart'"
@refresh="changeDisciplineFun"
>
<template #formSlot>
<el-form :inline="true">
<el-form-item label="学科" >
<el-select v-model="projectStorageSpaceStatisticsFormData.discipline" clearable @change="changeDisciplineFun">
<el-option v-for="item in disciplineOptions" :label="item.label" :value="item.value" :key="item.value"></el-option>
</el-select>
</el-form-item>
</el-form>
</template>
</EchartCard>
</div>
<div class="chart-box">
<EchartCard
title="用户存储空间统计"
ref="userStorageSpaceStatisticsRef"
:charts-id="'chart-3'"
:bar-type="'barChart'"
@refresh="changeUserFun"
>
<template #formSlot>
<el-form :inline="true">
<el-form-item label="用户" >
<UserSelect v-model="projectStorageSpaceStatisticsFormData.userId" @change="changeUserFun" />
</el-form-item>
</el-form>
</template>
</EchartCard>
<div class="page-content">
<storageStatistics v-if="currentTab === 'Storage statistics' "></storageStatistics>
<storageSettings v-if="currentTab === 'Storage Settings' "></storageSettings>
<documentManagement v-if="currentTab === 'Document Management' "></documentManagement>
</div>
</div>
</div>
@@ -63,287 +25,19 @@
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import EchartCard from '@/components/common/echartCard/index.vue';
import { queryNodeListApi } from '@/api/project/node';
import { NODE_TYPE } from '@/utils/enum/node';
import { getDirectorySizeByUserIdApi, getNodeSizeByNodeTypeApi } from '@/api/data/dataStorageAnalysis';
import UserSelect from '@/components/common/userSelect/index.vue';
import storageStatistics from './components/storageStatistics/index.vue';
import storageSettings from './components/storageSettings/index.vue';
import documentManagement from './components/documentManagement/index.vue';
const projectStorageSpaceStatisticsRef = ref();
const getNodeSize = async (nodeType:string) => {
const res:any = await getNodeSizeByNodeTypeApi({ nodeType, nodeId: nodeType === NODE_TYPE.PROJECT ? projectStorageSpaceStatisticsFormData.value?.project : projectStorageSpaceStatisticsFormData.value?.discipline });
const yData:any = [];
const xData:any = [];
if (res.code === 200) {
res.data?.forEach((item:any) => {
yData.push(item.totalSize ? parseFloat( item.totalSize ) : 0 );
xData.push(item.nodeName);
});
}
return {
yData,
xData,
};
};
// 初始化图表chart-1
const initProjectTaskAchievementStatistics = async () => {
const { yData, xData } = await getNodeSize(NODE_TYPE.PROJECT);
projectStorageSpaceStatisticsRef.value.commonChartRef.disposeEchartsByKey('chart-1');
projectStorageSpaceStatisticsRef.value.commonChartRef.option = {
title: {
show: false,
},
tooltip: {
trigger: 'axis',
},
legend: {
show: false,
top: '0%',
left: 'center',
const currentTab = ref<any>('Storage statistics');
},
grid: {
top: '10%',
bottom: '10%',
left: '5%',
right: '5%',
},
xAxis: {
type: 'category',
data: xData,
},
yAxis: {
type: 'value',
},
dataZoom:
xData.length > 4
? [
{
type: 'slider',
show: true,
xAxisIndex: [0],
start: 0,
end: 100,
textStyle: {
color: 'transparent',
},
maxValueSpan: 4,
minValueSpan: 4,
moveHandleSize: 10,
height: 0,
filterMode: 'empty',
bottom: 15,
},
]
: null,
series: [
{
type: 'bar',
barWidth: '30%',
data: yData,
tooltip: {
valueFormatter: (val:any) => val + ' GB',
},
},
const currentTabChangeFn = () => {
],
};
projectStorageSpaceStatisticsRef.value.commonChartRef.initChart();
};
const disciplineStorageSpaceStatisticsRef = ref();
// 初始化图表chart-2
const initDisciplineTaskAchievementStatistics = async () => {
const { yData, xData } = await getNodeSize('discipline');
disciplineStorageSpaceStatisticsRef.value.commonChartRef.disposeEchartsByKey('chart-2');
disciplineStorageSpaceStatisticsRef.value.commonChartRef.option = {
title: {
show: false,
},
tooltip: {
trigger: 'item',
formatter: function (params:any) {
return `${params.name} : ${params.value} GB (${params.percent}%)`;
},
},
legend: {
top: '0%',
left: 'center',
},
grid: {
top: '10%',
bottom: '10%',
left: '5%',
right: '5%',
},
series: [
{
// name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2,
},
label: {
show: false,
position: 'center',
},
emphasis: {
label: {
show: true,
fontSize: 20,
fontWeight: 'bold',
},
},
labelLine: {
show: false,
},
data: xData.map((item:any, index:number) => {
return {
value: yData[index],
name: item,
console.log(currentTab.value);
};
}),
},
],
};
disciplineStorageSpaceStatisticsRef.value.commonChartRef.initChart();
};
const userStorageSpaceStatisticsRef = ref();
const getDirectorySize = async () => {
const res:any = await getDirectorySizeByUserIdApi({ userId: projectStorageSpaceStatisticsFormData.value?.userId });
const yData:any = [];
const xData:any = [];
if (res.code === 200) {
res.data?.forEach((item:any) => {
yData.push(item.totalFileSize ? parseFloat( item.totalFileSize ) : 0 );
xData.push(item.userName);
});
}
return {
yData,
xData,
};
};
// 初始化图表chart-3
const initUserTaskAchievementStatistics = async () => {
const { yData, xData } = await getDirectorySize();
userStorageSpaceStatisticsRef.value.commonChartRef.disposeEchartsByKey('chart-3');
userStorageSpaceStatisticsRef.value.commonChartRef.option = {
title: {
show: false,
},
tooltip: {
trigger: 'axis',
},
legend: {
show: false,
top: '0%',
left: 'center',
},
grid: {
top: '10%',
bottom: '10%',
left: '5%',
right: '5%',
},
xAxis: {
type: 'category',
data: xData,
},
yAxis: {
type: 'value',
},
dataZoom:
xData.length > 4
? [
{
type: 'slider',
show: true,
xAxisIndex: [0],
start: 0,
end: 100,
textStyle: {
color: 'transparent',
},
maxValueSpan: 4,
minValueSpan: 4,
moveHandleSize: 10,
height: 0,
filterMode: 'empty',
bottom: 15,
},
]
: null,
series: [
{
// name: '完成',
type: 'bar',
barWidth: '30%',
data: yData,
tooltip: {
valueFormatter: (val:any) => val + ' GB',
},
},
],
};
userStorageSpaceStatisticsRef.value.commonChartRef.initChart();
};
const projectStorageSpaceStatisticsFormData = ref<any>({
project: '',
discipline: '',
userId: '',
});
const projectOptions = ref<any[]>([]);
const disciplineOptions = ref<any[]>([]);
const getProjectOptionsFun = () => {
queryNodeListApi({ current: 1, size: 999, nodeType: NODE_TYPE.PROJECT }).then((res: any) => {
console.log(res);
if (res.code === 200) {
projectOptions.value = res.data.data.map((item: any) => {
return {
label: item.nodeName,
value: item.id,
};
});
}
});
};
const getDisciplineOptionsFun = () => {
queryNodeListApi({ current: 1, size: 999, nodeType: 'discipline' }).then((res: any) => {
console.log(res);
if (res.code === 200) {
disciplineOptions.value = res.data.data.map((item: any) => {
return {
label: item.nodeName,
value: item.id,
};
});
}
});
};
const changeUserFun = async ( val: any) => {
console.log('1127', val);
await initUserTaskAchievementStatistics();
};
const changeDisciplineFun = async() => {
await initDisciplineTaskAchievementStatistics();
};
const changeProjectFun = async() => {
await initProjectTaskAchievementStatistics();
};
onMounted(async () => {
getProjectOptionsFun();
getDisciplineOptionsFun();
await initProjectTaskAchievementStatistics();
await initDisciplineTaskAchievementStatistics();
await initUserTaskAchievementStatistics();
});
</script>
@@ -352,36 +46,24 @@ onMounted(async () => {
.gl-page-content-grey-full {
.content {
height: 100%;
display: flex;
flex-wrap: wrap;
gap: 12px;
.margin-style{
width: 100% !important;
}
.chart-box {
width: calc(50% - 6px);
height: calc(50% - 6px);
overflow: hidden;
position: relative;
height: 100%;
background-color: #fff;
padding: 10px;
.el-form {
width: 100%;
display: flex;
justify-content: flex-end;
.el-form-item {
margin-bottom: 0 !important;
margin-right: 0 !important;
.el-select {
width: 215px;
}
}
}
.chart-item {
.page-title{
width: 100%;
height: 100%;
height: 50px;
display: flex;
align-items: center;
justify-content: flex-end;
}
}
.page-content{
width: 100%;
height: calc(100% - 50px);
overflow: auto;
}
}
}
</style>

View File

@@ -9,7 +9,7 @@ export const keyboardEvents = (graph:any) => {
graph.bindKey('ctrl+v', () => {
if (!graph.isClipboardEmpty()) {
const cells = graph.paste({ offset: 32 });
const cells = graph.paste({ offset: 20 });
graph.cleanSelection();
graph.select(cells);
}

View File

@@ -0,0 +1,59 @@
import { reactive, ref } from 'vue';
const showPorts = (ports: NodeListOf<SVGElement>, show: boolean) => {
for (let i = 0, len = ports.length; i < len; i += 1) {
ports[i].style.visibility = show ? 'visible' : 'hidden';
}
};
export const initNodeEvents = (graph: any) => {
graph.on('node:mouseenter', () => {
const container = document.getElementById('container')!;
const ports = container.querySelectorAll(
'.x6-port-body'
) as NodeListOf<SVGElement>;
showPorts(ports, true);
});
graph.on('node:mouseleave', () => {
const container = document.getElementById('container')!;
const ports = container.querySelectorAll(
'.x6-port-body'
) as NodeListOf<SVGElement>;
showPorts(ports, false);
});
graph.on('node:selected', ({ node }:any) => {
console.log('node', node);
node.addTools({
name: 'boundary',
args: {
attrs: {
// fill: '#7c68fc',
stroke: '#333',
'stroke-width': 1,
'fill-opacity': 0.2,
},
},
});
});
graph.on('node:unselected', ({ node }:any) => {
console.log('node', node);
node.removeTools();
const container = document.getElementById('container')!;
const ports = container.querySelectorAll(
'.x6-port-body'
) as NodeListOf<SVGElement>;
showPorts(ports, false);
});
};
export const nodeAttributeRef:any = ref({
name: '',
type: '',
value: '',
});
export const setNodeAttribute = (property:any) => {
console.log('setNodeAttribute', property);
for (const key in property) {
nodeAttributeRef.value[key] = property[key];
}
};

View File

@@ -5,11 +5,18 @@
<div class="app-name">{{ appProperty.name }}</div>
<div class="app-tag">{{ appProperty.type }}</div>
</div>
<div>
<span @click="attributeFun">属性</span>
<span @click="paramFun">参数</span>
</div>
</div>
</template>
<script lang="ts" setup>
import { inject, onMounted, reactive } from 'vue';
import { setNodeAttribute } from './nodeEvents';
const emits = defineEmits(['attribute', 'param']);
const appProperty = reactive({
name: '',
@@ -18,6 +25,21 @@ const appProperty = reactive({
});
const getNode:any = inject('getNode');
const attributeFun = () => {
console.log('click attributeFun');
setNodeAttribute(appProperty);
// const store = CommonStore();
// console.log('store', nodeRef, store);
// console.log('attributeFun');
// emits('attribute', appProperty);
};
const paramFun = () => {
console.log('paramFun');
emits('param', appProperty);
};
onMounted(() => {
if (getNode) {
const node = getNode();

View File

@@ -16,10 +16,11 @@ import { Snapline } from '@antv/x6-plugin-snapline';
import { Keyboard } from '@antv/x6-plugin-keyboard';
import { Clipboard } from '@antv/x6-plugin-clipboard';
import { History } from '@antv/x6-plugin-history';
import { onMounted } from 'vue';
import { onMounted, watch } from 'vue';
import { getTeleport } from '@antv/x6-vue-shape';
import { keyboardEvents } from './components/keyboard';
import { stencilRegister } from './components/stencil';
import { initNodeEvents, nodeAttributeRef } from './components/nodeEvents';
// 注册连线
Graph.registerConnector(
@@ -176,25 +177,8 @@ const initGraph = () => {
true
);
// 控制连接桩显示/隐藏
const showPorts = (ports: NodeListOf<SVGElement>, show: boolean) => {
for (let i = 0, len = ports.length; i < len; i += 1) {
ports[i].style.visibility = show ? 'visible' : 'hidden';
}
};
graph.on('node:mouseenter', () => {
const container = document.getElementById('container')!;
const ports = container.querySelectorAll(
'.x6-port-body'
) as NodeListOf<SVGElement>;
showPorts(ports, true);
});
graph.on('node:mouseleave', () => {
const container = document.getElementById('container')!;
const ports = container.querySelectorAll(
'.x6-port-body'
) as NodeListOf<SVGElement>;
showPorts(ports, false);
});
initNodeEvents(graph);
graph.use(
new Snapline({
@@ -287,6 +271,15 @@ const initGraph = () => {
// const importJson = () => {
// };
watch(
() => nodeAttributeRef.value,
(val) => {
console.log('nodeAttributeRef', val);
},
{ deep: true }
);
onMounted(() => {
initGraph();
// document.addEventListener('keydown', (e) => {

View File

@@ -6,22 +6,20 @@ const commonStore = CommonStore();
/** 将用户数组转换成用户名字符串显示 */
export const disposeMemberList = (row:any, key:string = 'memberList') => {
if (row) {
const managerNames:string[] = [];
if (row[key]?.length) {
row[key].forEach((item: {
const managerNames:string[] = [];
if (Array.isArray(row[key]) && row[key]?.length) {
row[key].forEach(
(item: {
name: string;
realName: string;
nickname: string;
}) => {
}) => {
managerNames.push(item.nickname || item.realName || item.name);
});
}
const nameStr = managerNames.join(',');
row.hoverTitle = nameStr;
return nameStr;
}
return row;
const nameStr = managerNames.join(',');
row.hoverTitle = nameStr;
return nameStr;
};
export const fakeUserList = [

View File

@@ -165,6 +165,8 @@ import { modifyNodeTaskPerformanceApi } from '@/api/project/node';
import { TASK_CALCULATE_STATUS, TASK_PROCESS_STATUS } from '@/utils/enum/task';
import dayjs from 'dayjs';
import loadCaseTable from '@/components/common/treeCaseTable/loadCaseTable.vue';
import { getMemberListStr } from '@/utils/task';
import { getUserTenantId } from '@/utils/user';
const props = defineProps<{
showTaskDialog: boolean;
@@ -485,6 +487,8 @@ const addOrEditTaskFun = async() => {
const insertList = cloneDeep(insertRecords).map((item: any) => {
return {
...item,
tenantId: getUserTenantId(),
eMemberList: getMemberListStr(item.eMemberList),
analyseSoftware: Array.isArray(item.analyseSoftware) ? item.analyseSoftware?.join() : item.analyseSoftware,
};
});
@@ -492,6 +496,8 @@ const addOrEditTaskFun = async() => {
const updateList = cloneDeep(updateRecords).map((item: any) => {
return {
...item,
tenantId: getUserTenantId(),
eMemberList: getMemberListStr(item.eMemberList),
analyseSoftware: Array.isArray(item.analyseSoftware) ? item.analyseSoftware?.join() : item.analyseSoftware,
};
});
@@ -523,6 +529,7 @@ const updateTreeDataApi = async (insertList: any[], deleteList: any[], updateLis
// });
const deleteNodeList = getDeleteListIds(deleteList, []);
const addList = disposeTreeTagKey(insertList);
console.log('updateTreeDataApi', {
addNodeList: addList,
editNodeList: updateList,

View File

@@ -91,15 +91,19 @@ const loadingInterface = ref(false);
// ]);
const changeTaskStatus = async(row: any, status: string) => {
const params:{
taskId: string;
exeStatus: string;
finishTime?: string;
} = {
taskId: row.uuid,
exeStatus: status,
taskIds: any[];
req: {
exeStatus: string;
finishTime?:string;
};
} = {
taskIds: [row.uuid],
req: {
exeStatus: status,
},
};
if (status === TASK_PROCESS_STATUS.COMPLETED) {
params.finishTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
params.req.finishTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
}
const res:any = await updateTaskStatusApi(params);
if (res.code === 200) {
@@ -182,19 +186,23 @@ const confirmFun = async() => {
const fromData:any = tableFormRef.value.getFormDataFun();
// console.log('tableFormRef.value.getFormDataFun()', fromData);
const params:{
taskId: string;
exeStatus: string;
achieveStatus: string;
process: number;
finishTime?: string;
taskIds: string[];
req:{
exeStatus: string;
achieveStatus: string;
progress: number;
finishTime?: string;
}
} = {
taskId: fromData.uuid,
exeStatus: fromData.exeStatus,
achieveStatus: fromData.achieveStatus,
process: fromData.progress,
taskIds: [fromData.uuid],
req: {
exeStatus: fromData.exeStatus,
achieveStatus: fromData.achieveStatus,
progress: fromData.progress,
},
};
if (fromData.exeStatus === TASK_PROCESS_STATUS.COMPLETED) {
params.finishTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
params.req.finishTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
}
const res:any = await updateTaskStatusApi(params);
if (res.code === 200) {

View File

@@ -161,21 +161,17 @@ const confirmFun = async() => {
loadingInterface.value = true;
const fromData:any = tableFormRef.value.getFormDataFun();
// console.log('tableFormRef.value.getFormDataFun()', fromData);
const params:{
taskId: string;
exeStatus: string;
achieveStatus: string;
process: number;
finishTime?: string;
} = {
const params:any = {
...fromData,
taskId: fromData.uuid,
exeStatus: fromData.exeStatus,
achieveStatus: fromData.achieveStatus,
process: fromData.progress,
taskIds: [fromData.uuid],
req: {
exeStatus: fromData.exeStatus,
achieveStatus: fromData.achieveStatus,
process: fromData.progress,
},
};
if (fromData.exeStatus === TASK_PROCESS_STATUS.COMPLETED) {
params.finishTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
params.req.finishTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
}
const res:any = await updateTaskStatusApi(params);
if (res.code === 200) {