merge
This commit is contained in:
242
src/views/task/workLoad/detailDia.vue
Normal file
242
src/views/task/workLoad/detailDia.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<Dialog v-model="visible" diaTitle="工作负载详情" width="80%" height="70%" @close="closeFun">
|
||||
<div class="table-content">
|
||||
<div class="filter-box">
|
||||
<el-radio-group v-model="radioData" @change="changeRadioDataFun">
|
||||
<el-radio-button label="表格" value="table" />
|
||||
<el-radio-button label="图表" value="chart" />
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="content-inner" v-if="radioData === 'table'">
|
||||
<BaseTable
|
||||
:fullHeight="true"
|
||||
ref="workloadTableRef"
|
||||
:tableName="'PROJECT_TASK_WORKLOAD'"
|
||||
:hidePagination="true"
|
||||
>
|
||||
<template #exeStatus="{ row }">
|
||||
<StatusDot
|
||||
:status="getTaskExeStyleClass(row.exeStatus)"
|
||||
:title="
|
||||
TASK_PROCESS_STATUS_OBJ[row.exeStatus as keyof typeof TASK_PROCESS_STATUS_OBJ]
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #achieveStatus="{ row }">
|
||||
<StatusDot
|
||||
:status="getTaskAchieveStyleClass(row.achieveStatus)"
|
||||
:title="
|
||||
TASK_CALCULATE_STATUS_OBJ[
|
||||
row.achieveStatus as keyof typeof TASK_CALCULATE_STATUS_OBJ
|
||||
] || '未分析'
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #progress="{ row }"> {{ row.progress || 0 }}% </template>
|
||||
<template #approvalStatus="{ row }">
|
||||
<StatusDot
|
||||
class="clcik-approval"
|
||||
:status="
|
||||
getApproveStyleClass(row.approvalStatus || TASK_APPROVE_STATUS_ENUM.NOT_APPROVED)
|
||||
"
|
||||
:title="
|
||||
TASK_APPROVE_STATUS.O[row.approvalStatus || TASK_APPROVE_STATUS_ENUM.NOT_APPROVED]
|
||||
"
|
||||
>
|
||||
</StatusDot>
|
||||
</template>
|
||||
</BaseTable>
|
||||
</div>
|
||||
<div class="content-inner" v-else>
|
||||
<EchartCard
|
||||
title=""
|
||||
ref="workloadChartRef"
|
||||
:barType="'barChart'"
|
||||
:chartsId="'workload-chart'"
|
||||
@refresh="initWorkloadChartFun"
|
||||
></EchartCard>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, nextTick } from 'vue';
|
||||
import Dialog from '@/components/common/dialog/index.vue';
|
||||
import { listTaskWorkDaysApi } from '@/api/project/task';
|
||||
import BaseTable from '@/components/common/table/baseTable.vue';
|
||||
import {
|
||||
getApproveStyleClass,
|
||||
getTaskAchieveStyleClass,
|
||||
getTaskExeStyleClass,
|
||||
} from '@/components/common/statusDot/statusMap';
|
||||
import {
|
||||
TASK_APPROVE_STATUS_ENUM,
|
||||
TASK_CALCULATE_STATUS_OBJ,
|
||||
TASK_PROCESS_STATUS_OBJ,
|
||||
} from '@/utils/enum/task';
|
||||
import StatusDot from '@/components/common/statusDot/index.vue';
|
||||
import { useDict } from '@/utils/useDict';
|
||||
import EchartCard from '@/components/common/echartCard/index.vue';
|
||||
|
||||
const { TASK_APPROVE_STATUS } = useDict('TASK_APPROVE_STATUS');
|
||||
const emit = defineEmits(['update:modelValue', 'confirm']);
|
||||
|
||||
interface Props {
|
||||
modelValue: boolean;
|
||||
list: any[];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: false,
|
||||
list: () => [],
|
||||
});
|
||||
const visible = ref(false);
|
||||
const tableData = ref<any>([]);
|
||||
const radioData = ref('table');
|
||||
const workloadTableRef = ref();
|
||||
const workloadChartRef = ref();
|
||||
|
||||
const changeRadioDataFun = async () => {
|
||||
if (radioData.value === 'table') {
|
||||
nextTick(() => {
|
||||
workloadTableRef.value.setDataFun(tableData.value);
|
||||
});
|
||||
} else {
|
||||
await initWorkloadChartFun();
|
||||
}
|
||||
};
|
||||
const getUserWorkLoadDataFun = async () => {
|
||||
const taskIds = tableData.value.map((item: any) => {
|
||||
return item.uuid;
|
||||
});
|
||||
const param = {
|
||||
taskIds,
|
||||
};
|
||||
|
||||
const xData: any = [];
|
||||
const seriesData: any = [];
|
||||
const datas: any = [];
|
||||
|
||||
const res: any = await listTaskWorkDaysApi(param);
|
||||
if (res && res.code === 200) {
|
||||
res.data.forEach((item: any) => {
|
||||
xData.push(item.taskName);
|
||||
datas.push(item.days);
|
||||
});
|
||||
seriesData.push({
|
||||
type: 'bar',
|
||||
barWidth: '30%',
|
||||
// name: res.data[i].userName,
|
||||
data: datas,
|
||||
});
|
||||
|
||||
return {
|
||||
xData,
|
||||
seriesData,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
xData,
|
||||
seriesData,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const initWorkloadChartFun = async () => {
|
||||
const { xData, seriesData } = await getUserWorkLoadDataFun();
|
||||
|
||||
workloadChartRef.value.commonChartRef.disposeEchartsByKey('chart-1');
|
||||
workloadChartRef.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',
|
||||
axisLabel: {
|
||||
formatter: function (value: any) {
|
||||
return value + 'h';
|
||||
},
|
||||
},
|
||||
},
|
||||
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: seriesData,
|
||||
};
|
||||
workloadChartRef.value.commonChartRef.initChart();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: boolean) => {
|
||||
radioData.value = 'table';
|
||||
tableData.value = [...props.list];
|
||||
visible.value = val;
|
||||
changeRadioDataFun();
|
||||
}
|
||||
);
|
||||
|
||||
const closeFun = () => {
|
||||
visible.value = false;
|
||||
tableData.value = [];
|
||||
emit('update:modelValue', false);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// height: 600px;
|
||||
|
||||
.filter-box {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.content-inner {
|
||||
width: 100%;
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -87,66 +87,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<Dialog v-model="visible" diaTitle="工作负载详情" width="80%" height="70%" @close="closeFun">
|
||||
<div class="table-content">
|
||||
<div class="filter-box">
|
||||
<el-radio-group v-model="radioData" @change="changeRadioDataFun">
|
||||
<el-radio-button label="表格" value="table" />
|
||||
<el-radio-button label="图表" value="chart" />
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="content-inner" v-if="radioData === 'table'">
|
||||
<BaseTable
|
||||
:fullHeight="true"
|
||||
ref="workloadTableRef"
|
||||
:tableName="'PROJECT_TASK_WORKLOAD'"
|
||||
:hidePagination="true"
|
||||
>
|
||||
<template #exeStatus="{ row }">
|
||||
<StatusDot
|
||||
:status="getTaskExeStyleClass(row.exeStatus)"
|
||||
:title="
|
||||
TASK_PROCESS_STATUS_OBJ[row.exeStatus as keyof typeof TASK_PROCESS_STATUS_OBJ]
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #achieveStatus="{ row }">
|
||||
<StatusDot
|
||||
:status="getTaskAchieveStyleClass(row.achieveStatus)"
|
||||
:title="
|
||||
TASK_CALCULATE_STATUS_OBJ[
|
||||
row.achieveStatus as keyof typeof TASK_CALCULATE_STATUS_OBJ
|
||||
] || '未分析'
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template #progress="{ row }"> {{ row.progress || 0 }}% </template>
|
||||
<template #approvalStatus="{ row }">
|
||||
<StatusDot
|
||||
class="clcik-approval"
|
||||
:status="
|
||||
getApproveStyleClass(row.approvalStatus || TASK_APPROVE_STATUS_ENUM.NOT_APPROVED)
|
||||
"
|
||||
:title="
|
||||
TASK_APPROVE_STATUS.O[row.approvalStatus || TASK_APPROVE_STATUS_ENUM.NOT_APPROVED]
|
||||
"
|
||||
>
|
||||
</StatusDot>
|
||||
</template>
|
||||
</BaseTable>
|
||||
</div>
|
||||
<div class="content-inner" v-else>
|
||||
<EchartCard
|
||||
title=""
|
||||
ref="workloadChartRef"
|
||||
:barType="'barChart'"
|
||||
:chartsId="'workload-chart'"
|
||||
@refresh="initWorkloadChartFun"
|
||||
></EchartCard>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
<DetailDia v-model="visible" :list="tableData"></DetailDia>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -159,24 +100,10 @@ import isBetween from 'dayjs/plugin/isBetween';
|
||||
import isoWeek from 'dayjs/plugin/isoWeek';
|
||||
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
||||
import { userQueryGroupApi, userQueryGroupDetailApi } from '@/api/system/user';
|
||||
import { getListUserWorkloadsApi, listTaskWorkDaysApi } from '@/api/project/task';
|
||||
import { getListUserWorkloadsApi } from '@/api/project/task';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import type { ChineseWorkday } from 'chinese-workday';
|
||||
import Dialog from '@/components/common/dialog/index.vue';
|
||||
import BaseTable from '@/components/common/table/baseTable.vue';
|
||||
import {
|
||||
getApproveStyleClass,
|
||||
getTaskAchieveStyleClass,
|
||||
getTaskExeStyleClass,
|
||||
} from '@/components/common/statusDot/statusMap';
|
||||
import {
|
||||
TASK_APPROVE_STATUS_ENUM,
|
||||
TASK_CALCULATE_STATUS_OBJ,
|
||||
TASK_PROCESS_STATUS_OBJ,
|
||||
} from '@/utils/enum/task';
|
||||
import StatusDot from '@/components/common/statusDot/index.vue';
|
||||
import { useDict } from '@/utils/useDict';
|
||||
import EchartCard from '@/components/common/echartCard/index.vue';
|
||||
import DetailDia from '@/views/task/workLoad/detailDia.vue';
|
||||
|
||||
const props = defineProps({
|
||||
// 维度:工作负载以人person为维度, 人力负载以项目project为维度
|
||||
@@ -200,6 +127,11 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 字段映射方法
|
||||
fieldMapFun: {
|
||||
type: Function,
|
||||
default: (data: any) => data,
|
||||
},
|
||||
api: {
|
||||
type: Function,
|
||||
default: getListUserWorkloadsApi,
|
||||
@@ -210,8 +142,6 @@ dayjs.extend(isBetween);
|
||||
dayjs.extend(weekOfYear);
|
||||
dayjs.extend(isoWeek);
|
||||
|
||||
const { TASK_APPROVE_STATUS } = useDict('TASK_APPROVE_STATUS');
|
||||
|
||||
let Gantt: any = gantt;
|
||||
const ganttEvents = ref<any>([]);
|
||||
const colorList: string[] = ['#ffffff', '#d9ecff', '#c6e2ff', '#c6e2ff', '#79bbff', '#409eff']; // 格子颜色
|
||||
@@ -220,9 +150,7 @@ const loading = ref<boolean>(false);
|
||||
const workDaysRange = ref<number>(0);
|
||||
const ww = ref<ChineseWorkday>();
|
||||
const visible = ref(false);
|
||||
const radioData = ref('table');
|
||||
const workloadTableRef = ref();
|
||||
const workloadChartRef = ref();
|
||||
|
||||
const filterFprmData = reactive<any>({
|
||||
userGroupId: '',
|
||||
userIds: [],
|
||||
@@ -454,16 +382,8 @@ const getWorkLoadDataFun = async () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
const dzh = res1.data.map((item: any) => {
|
||||
return {
|
||||
userName: item.projectName,
|
||||
userId: item.projectId,
|
||||
taskNum: item.taskNum,
|
||||
workNum: item.personNum,
|
||||
taskList: item.taskList,
|
||||
};
|
||||
});
|
||||
res.data = dzh;
|
||||
const list = props.fieldMapFun(res1.data);
|
||||
res.data = list;
|
||||
taskOriginData.value = res.data;
|
||||
Gantt.config.start_date = filterFprmData.beginTime;
|
||||
Gantt.config.end_date = filterFprmData.endTime;
|
||||
@@ -542,12 +462,12 @@ const filterWorkLoadFun = async (flag?: any) => {
|
||||
await refreshGanttFun();
|
||||
};
|
||||
|
||||
const clearParamFun = async () => {
|
||||
await getUserGroupFun();
|
||||
filterFprmData.beginTime = dayjs().subtract(3, 'month').format('YYYY-MM-DD HH:mm:ss');
|
||||
filterFprmData.endTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
await filterWorkLoadFun();
|
||||
};
|
||||
// const clearParamFun = async () => {
|
||||
// await getUserGroupFun();
|
||||
// filterFprmData.beginTime = dayjs().subtract(3, 'month').format('YYYY-MM-DD HH:mm:ss');
|
||||
// filterFprmData.endTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
// await filterWorkLoadFun();
|
||||
// };
|
||||
|
||||
// 计算时间段中的工作日天数
|
||||
const calculateWorkDay = ([startDate, endDate]: any) => {
|
||||
@@ -806,107 +726,6 @@ const monitorCellClickFun = () => {
|
||||
return obj.currentIdTasksId.includes(item.id);
|
||||
});
|
||||
visible.value = true;
|
||||
radioData.value = 'table';
|
||||
nextTick(() => {
|
||||
workloadTableRef.value.setDataFun(tableData.value);
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const initWorkloadChartFun = async () => {
|
||||
const { xData, seriesData } = await getUserWorkLoadDataFun();
|
||||
|
||||
workloadChartRef.value.commonChartRef.disposeEchartsByKey('chart-1');
|
||||
workloadChartRef.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',
|
||||
axisLabel: {
|
||||
formatter: function (value: any) {
|
||||
return value + 'h';
|
||||
},
|
||||
},
|
||||
},
|
||||
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: seriesData,
|
||||
};
|
||||
workloadChartRef.value.commonChartRef.initChart();
|
||||
};
|
||||
|
||||
const getUserWorkLoadDataFun = async () => {
|
||||
const taskIds = tableData.value.map((item: any) => {
|
||||
return item.uuid;
|
||||
});
|
||||
const param = {
|
||||
taskIds,
|
||||
};
|
||||
|
||||
const xData: any = [];
|
||||
const seriesData: any = [];
|
||||
const datas: any = [];
|
||||
|
||||
const res: any = await listTaskWorkDaysApi(param);
|
||||
if (res && res.code === 200) {
|
||||
res.data.forEach((item: any) => {
|
||||
xData.push(item.taskName);
|
||||
datas.push(item.days);
|
||||
});
|
||||
seriesData.push({
|
||||
type: 'bar',
|
||||
barWidth: '30%',
|
||||
// name: res.data[i].userName,
|
||||
data: datas,
|
||||
});
|
||||
|
||||
return {
|
||||
xData,
|
||||
seriesData,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
xData,
|
||||
seriesData,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -918,21 +737,6 @@ const refreshGanttFun = async () => {
|
||||
await getWorkLoadDataFun();
|
||||
};
|
||||
|
||||
const closeFun = () => {
|
||||
visible.value = false;
|
||||
tableData.value = [];
|
||||
};
|
||||
|
||||
const changeRadioDataFun = async () => {
|
||||
if (radioData.value === 'table') {
|
||||
nextTick(() => {
|
||||
workloadTableRef.value.setDataFun(tableData.value);
|
||||
});
|
||||
} else {
|
||||
await initWorkloadChartFun();
|
||||
}
|
||||
};
|
||||
|
||||
const focusUserFun = () => {
|
||||
if (!filterFprmData.userGroupId) {
|
||||
ElMessage.warning('请选择用户组后再选择用户进行筛选!');
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
import { reactive } from 'vue';
|
||||
import workLoad from '@/views/task/workLoad/index.vue';
|
||||
import ProjectSelect from '@/components/common/projectSelect/index.vue';
|
||||
import { getListUserWorkloadsApi, getListUserWorkloadsApi1 } from '@/api/project/task';
|
||||
import { getListUserWorkloadsApi1 } from '@/api/project/task';
|
||||
|
||||
// 筛选条件:项目
|
||||
const filterData = reactive<{
|
||||
|
||||
Reference in New Issue
Block a user