<template>
|
<div class="main-layout">
|
<div class="header">
|
<el-tabs v-model="formData.type" @tab-click="handleTabClick">
|
<el-tab-pane label="按月" name="month"></el-tab-pane>
|
<el-tab-pane label="按日" name="day"></el-tab-pane>
|
</el-tabs>
|
|
<div class="search-section">
|
<el-form :model="formData" :inline="true" label-width="120" ref="form" @submit.prevent>
|
<el-form-item label="所属项目" prop="project_id">
|
<el-select v-model="formData.project_id" placeholder="所属项目" clearable filterable class="selectClass">
|
<el-option v-for="item in projectInfoArr" :key="item.id" :label="item.project_name" :value="item.id" />
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="开始" prop="startDate">
|
<el-date-picker
|
v-model="formData.startDate"
|
type="date"
|
placeholder="请选择开始时间"
|
:picker-options="startPickerOptions"
|
value-format="YYYY-MM-DD"
|
></el-date-picker>
|
</el-form-item>
|
<el-form-item label="结束" prop="endDate">
|
<el-date-picker
|
v-model="formData.endDate"
|
type="date"
|
placeholder="请选择结束时间"
|
:picker-options="endPickerOptions"
|
value-format="YYYY-MM-DD"
|
></el-date-picker>
|
</el-form-item>
|
|
<el-form-item label="用户" prop="create_user_id">
|
<el-select
|
v-model="formData.create_user_id"
|
placeholder="请选择用户"
|
clearable
|
filterable
|
class="selectClass"
|
>
|
<el-option v-for="item in userList" :key="item.id" :label="item.nickname" :value="item.id" />
|
</el-select>
|
</el-form-item>
|
|
<el-form-item label="确认状态">
|
<el-select v-model="formData.confirm_state" placeholder="确认状态" clearable filterable class="selectClass">
|
<el-option v-for="item in dailyConfirmArr" :key="item.value" :label="item.label" :value="item.value" />
|
</el-select>
|
</el-form-item>
|
|
<!-- <el-form-item class="submit">
|
<el-button type="primary" @click="search">查询</el-button>
|
<el-button @click="resetForm">重 置</el-button>
|
</el-form-item> -->
|
</el-form>
|
</div>
|
</div>
|
<!-- <div class="flex-container"> -->
|
|
<div class="main-content">
|
<!-- 右侧主要内容 -->
|
<!-- 使用 Element Plus 的组件填充内容 -->
|
|
<div ref="totalHourChart" style="width: 100%; height: 400px"></div>
|
|
<div ref="userHourChart" style="width: 100%; height: 400px"></div>
|
<div ref="userHourChart2" style="width: 100%; height: 400px"></div>
|
|
<div ref="groupHourChart" style="width: 100%; height: 400px"></div>
|
<div ref="groupHourChart2" style="width: 100%; height: 400px"></div>
|
|
<!-- </div> -->
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import { reactive, ref, onMounted, watch } from 'vue'
|
import { ElMessage } from 'element-plus'
|
|
import ProjectInfo from '@/model/projectInfo'
|
import SysDictItemModel from '@/model/sysDictItem'
|
|
import * as echarts from 'echarts'
|
|
import { getDefaultDate, getYesterdayDate, getStartAndEndOfWeek, getPreviousWeekDates } from '@/utils/dateUtils'
|
import ProjectDailyStatistics from '@/model/projectDailyStatistics'
|
import ProjectDaily from '@/model/projectDaily'
|
|
export default {
|
components: {},
|
props: {},
|
setup(props, context) {
|
const loading = ref(false)
|
|
const dailyTypeArr = ref([])
|
const dailyStatusArr = ref([])
|
const projectInfoArr = ref([])
|
const dailyCommitArr = ref([])
|
const dailyConfirmArr = ref([])
|
|
const form = ref({})
|
|
const activeTab = ref('month')
|
const userList = ref([])
|
const firstColumnWidth = '80px'
|
const minColumnWidth = '350px'
|
|
const formData = reactive({
|
type: 'month',
|
startDate: '',
|
endDate: '',
|
project_id: '',
|
confirm_state: '',
|
create_user_id: '',
|
})
|
|
// 监听日历日期的变化
|
watch(
|
() => formData,
|
(newData, oldData) => {
|
// totalHoursStatistics()
|
// userHoursStatistics()
|
// groupHoursStatistics()
|
initData()
|
},
|
{ deep: true }, // 深度监听
|
)
|
|
const listAssign = (a, b) => Object.keys(a).forEach(key => {
|
a[key] = b[key] || a[key]
|
})
|
|
onMounted(() => {
|
loadDictDitems()
|
// formData.startDate = getDefaultDate()
|
// formData.endDate = getDefaultDate()
|
const { startOfWeek, endOfWeek } = getStartAndEndOfWeek()
|
formData.startDate = startOfWeek
|
formData.endDate = endOfWeek
|
initData()
|
})
|
|
const handleTabClick = tab => {
|
switch (tab.props.name) {
|
case 'default':
|
formData.startDate = getDefaultDate()
|
formData.endDate = getDefaultDate()
|
break
|
case 'today':
|
formData.startDate = getDefaultDate()
|
formData.endDate = getDefaultDate()
|
break
|
case 'yesterday':
|
formData.startDate = getYesterdayDate()
|
formData.endDate = getYesterdayDate()
|
break
|
case 'this_week':
|
const { startOfWeek, endOfWeek } = getStartAndEndOfWeek()
|
formData.startDate = startOfWeek
|
formData.endDate = endOfWeek
|
break
|
case 'last_week':
|
const { startDate, endDate } = getPreviousWeekDates()
|
|
formData.startDate = startDate
|
formData.endDate = endDate
|
break
|
}
|
|
// getDiaryStatisticsList();
|
}
|
|
const startPickerOptions = {
|
disabledDate: time => {
|
if (formData.endDate) {
|
return time.getTime() > formData.endDate
|
}
|
return false
|
},
|
}
|
|
const endPickerOptions = {
|
disabledDate: time => {
|
if (formData.startDate) {
|
return time.getTime() < formData.startDate
|
}
|
return false
|
},
|
}
|
|
// 重置表单
|
const resetForm = () => {
|
form.value.resetFields()
|
}
|
|
const search = formName => {
|
form.value.validate(valid => {
|
if (valid) {
|
if (formData.startDate > formData.endDate) {
|
ElMessage.error('开始时间不能大于结束时间')
|
return
|
}
|
// 200000
|
|
if (res.code < window.MAX_SUCCESS_CODE) {
|
ElMessage.success(`${res.message}`)
|
}
|
} else {
|
console.error('error submit!!')
|
ElMessage.error('请将信息填写完整')
|
}
|
})
|
}
|
|
// 根据字典类型查询字典
|
const loadDictDitems = async () => {
|
userList.value = await ProjectDaily.getAllUsers()
|
dailyTypeArr.value = await SysDictItemModel.getSysDictItemListByType('daily_type')
|
dailyStatusArr.value = await SysDictItemModel.getSysDictItemListByType('daily_status')
|
projectInfoArr.value = await ProjectInfo.getProjectInfoList('')
|
dailyCommitArr.value = await SysDictItemModel.getSysDictItemListByType('daily_commit')
|
dailyConfirmArr.value = await SysDictItemModel.getSysDictItemListByType('daily_confirm')
|
|
// 给项目一个初始值
|
if (projectInfoArr.value && projectInfoArr.value[0]) {
|
formData.project_id = projectInfoArr.value[0].id
|
}
|
}
|
|
const initData = () => {
|
totalHoursStatistics()
|
userHoursStatistics()
|
groupHoursStatistics()
|
|
userHoursStatistics2()
|
groupHoursStatistics2()
|
}
|
|
const totalHourChart = ref(null)
|
const totalHoursStatistics = async () => {
|
let xdata = []
|
let estimatedHoursData = []
|
let actualHoursData = []
|
if (formData.project_id) {
|
const res = await ProjectDailyStatistics.getEvaluateiActualHourStatistics(formData)
|
|
if (res) {
|
xdata = res.map(item => item.year_mon)
|
estimatedHoursData = res.map(item => item.estimated_hours)
|
actualHoursData = res.map(item => item.actual_hours)
|
|
const option = {
|
title: {
|
text: '预估实际工时图',
|
},
|
legend: {
|
data: ['估计工时', '实际工时'],
|
},
|
tooltip: {
|
trigger: 'axis',
|
},
|
xAxis: {
|
type: 'category',
|
boundaryGap: false,
|
data: xdata,
|
},
|
yAxis: {
|
type: 'value',
|
},
|
series: [
|
{
|
name: '估计工时',
|
// stack: 'Total',
|
type: 'line',
|
data: estimatedHoursData,
|
},
|
{
|
name: '实际工时',
|
// stack: 'Total',
|
type: 'line',
|
data: actualHoursData,
|
},
|
],
|
}
|
const chartInstance = echarts.init(totalHourChart.value)
|
chartInstance.setOption(option)
|
}
|
}
|
}
|
|
const userHourChart = ref(null)
|
const userHoursStatistics = async () => {
|
const xdata = []
|
|
if (formData.project_id) {
|
const res = await ProjectDailyStatistics.getUserActualHourStatistics(formData)
|
|
if (res) {
|
let userCount = 0
|
let userId = ''
|
let username = ''
|
const yearmon = ''
|
let tmpArray = []
|
const seriesArray = []
|
const legend = []
|
res.forEach((item, index) => {
|
if (index == 0) {
|
userId = item.create_user_id
|
username = item.user_name
|
legend.push(username)
|
}
|
if (item.create_user_id == userId) {
|
tmpArray.push(item.estimated_hours)
|
} else {
|
seriesArray.push({
|
name: username,
|
// stack: 'Total',
|
type: 'line',
|
data: tmpArray,
|
})
|
userId = item.create_user_id
|
username = item.user_name
|
legend.push(username)
|
tmpArray = []
|
tmpArray.push(item.estimated_hours)
|
userCount++
|
}
|
if (res.length - 1 == index) {
|
seriesArray.push({
|
name: username,
|
// stack: 'Total',
|
type: 'line',
|
data: tmpArray,
|
})
|
}
|
if (userCount == 0) {
|
xdata.push(item.year_mon)
|
}
|
})
|
console.log('用户工时图-评估')
|
console.log(legend)
|
console.log(xdata)
|
console.log(seriesArray)
|
const option = {
|
title: {
|
text: '用户工时图-评估',
|
},
|
legend: {
|
data: legend,
|
},
|
tooltip: {
|
trigger: 'axis',
|
},
|
xAxis: {
|
type: 'category',
|
boundaryGap: false,
|
data: xdata,
|
},
|
yAxis: {
|
type: 'value',
|
},
|
series: seriesArray,
|
}
|
const chartInstance = echarts.init(userHourChart.value)
|
chartInstance.setOption(option)
|
}
|
}
|
}
|
|
const userHourChart2 = ref(null)
|
const userHoursStatistics2 = async () => {
|
const xdata = []
|
|
if (formData.project_id) {
|
const res = await ProjectDailyStatistics.getUserActualHourStatistics(formData)
|
|
if (res) {
|
let userCount = 0
|
let userId = ''
|
let username = ''
|
const yearmon = ''
|
let tmpArray = []
|
const seriesArray = []
|
const legend = []
|
res.forEach((item, index) => {
|
if (index == 0) {
|
userId = item.create_user_id
|
username = item.user_name
|
legend.push(username)
|
}
|
if (item.create_user_id == userId) {
|
tmpArray.push(item.actual_hours)
|
} else {
|
seriesArray.push({
|
name: username,
|
// stack: 'Total',
|
type: 'line',
|
data: tmpArray,
|
})
|
userId = item.create_user_id
|
username = item.user_name
|
legend.push(username)
|
tmpArray = []
|
tmpArray.push(item.actual_hours)
|
userCount++
|
}
|
if (res.length - 1 == index) {
|
seriesArray.push({
|
name: username,
|
// stack: 'Total',
|
type: 'line',
|
data: tmpArray,
|
})
|
}
|
if (userCount == 0) {
|
xdata.push(item.year_mon)
|
}
|
})
|
console.log('用户工时图-实际')
|
console.log(legend)
|
console.log(xdata)
|
console.log(seriesArray)
|
const option = {
|
title: {
|
text: '用户工时图-实际',
|
},
|
legend: {
|
data: legend,
|
},
|
tooltip: {
|
trigger: 'axis',
|
},
|
xAxis: {
|
type: 'category',
|
boundaryGap: false,
|
data: xdata,
|
},
|
yAxis: {
|
type: 'value',
|
},
|
series: seriesArray,
|
}
|
const chartInstance = echarts.init(userHourChart2.value)
|
chartInstance.setOption(option)
|
}
|
}
|
}
|
|
const groupHourChart = ref(null)
|
const groupHoursStatistics = async () => {
|
if (formData.project_id) {
|
const res = await ProjectDailyStatistics.getRoleHourStatistics(formData)
|
|
if (res) {
|
let userCount = 0
|
let userId = ''
|
let username = ''
|
const xdata = []
|
let tmpArray = []
|
const seriesArray = []
|
const legend = []
|
res.forEach((item, index) => {
|
if (index == 0) {
|
userId = item.group_id
|
username = item.group_name
|
legend.push(username)
|
}
|
if (item.group_id == userId) {
|
tmpArray.push(item.estimated_hours)
|
} else {
|
seriesArray.push({
|
name: username,
|
// stack: 'Total',
|
type: 'line',
|
data: tmpArray,
|
})
|
userId = item.group_id
|
username = item.group_name
|
legend.push(username)
|
tmpArray = []
|
tmpArray.push(item.estimated_hours)
|
userCount++
|
}
|
if (res.length - 1 == index) {
|
seriesArray.push({
|
name: username,
|
// stack: 'Total',
|
type: 'line',
|
data: tmpArray,
|
})
|
}
|
if (userCount == 0) {
|
xdata.push(item.year_mon)
|
}
|
})
|
console.log(legend)
|
console.log(xdata)
|
console.log(seriesArray)
|
const option = {
|
title: {
|
text: '分组工时图-评估',
|
},
|
legend: {
|
data: legend,
|
},
|
tooltip: {
|
trigger: 'axis',
|
},
|
xAxis: {
|
type: 'category',
|
boundaryGap: false,
|
data: xdata,
|
},
|
yAxis: {
|
type: 'value',
|
},
|
series: seriesArray,
|
}
|
const chartInstance = echarts.init(groupHourChart.value)
|
chartInstance.setOption(option)
|
}
|
}
|
}
|
|
const groupHourChart2 = ref(null)
|
const groupHoursStatistics2 = async () => {
|
if (formData.project_id) {
|
const res = await ProjectDailyStatistics.getRoleHourStatistics(formData)
|
|
if (res) {
|
let userCount = 0
|
let userId = ''
|
let username = ''
|
const xdata = []
|
let tmpArray = []
|
const seriesArray = []
|
const legend = []
|
res.forEach((item, index) => {
|
if (index == 0) {
|
userId = item.group_id
|
username = item.group_name
|
legend.push(username)
|
}
|
if (item.group_id == userId) {
|
tmpArray.push(item.actual_hours)
|
} else {
|
seriesArray.push({
|
name: username,
|
// stack: 'Total',
|
type: 'line',
|
data: tmpArray,
|
})
|
userId = item.group_id
|
username = item.group_name
|
legend.push(username)
|
tmpArray = []
|
tmpArray.push(item.actual_hours)
|
userCount++
|
}
|
if (res.length - 1 == index) {
|
seriesArray.push({
|
name: username,
|
// stack: 'Total',
|
type: 'line',
|
data: tmpArray,
|
})
|
}
|
if (userCount == 0) {
|
xdata.push(item.year_mon)
|
}
|
})
|
console.log(legend)
|
console.log(xdata)
|
console.log(seriesArray)
|
const option = {
|
title: {
|
text: '分组工时图-实际',
|
},
|
legend: {
|
data: legend,
|
},
|
tooltip: {
|
trigger: 'axis',
|
},
|
xAxis: {
|
type: 'category',
|
boundaryGap: false,
|
data: xdata,
|
},
|
yAxis: {
|
type: 'value',
|
},
|
series: seriesArray,
|
}
|
const chartInstance = echarts.init(groupHourChart2.value)
|
chartInstance.setOption(option)
|
}
|
}
|
}
|
|
return {
|
projectInfoArr,
|
dailyTypeArr,
|
dailyStatusArr,
|
dailyCommitArr,
|
dailyConfirmArr,
|
|
activeTab,
|
formData,
|
form,
|
|
handleTabClick,
|
search,
|
resetForm,
|
userList,
|
|
startPickerOptions,
|
endPickerOptions,
|
|
firstColumnWidth,
|
minColumnWidth,
|
|
// 统计图表
|
totalHourChart,
|
userHourChart,
|
groupHourChart,
|
|
userHourChart2,
|
groupHourChart2,
|
}
|
},
|
}
|
</script>
|
|
<style scoped>
|
.main-layout {
|
display: flex;
|
flex-direction: column;
|
height: 100vh;
|
}
|
|
.header {
|
padding-left: 10px;
|
/* background-color: #f0f0f0; */
|
text-align: left;
|
}
|
|
.flex-container {
|
flex: 1;
|
display: flex;
|
overflow: hidden;
|
}
|
|
.sidebar {
|
flex: 0 0 300px;
|
/* 左侧宽度固定为300px */
|
background-color: #f7f7f7;
|
overflow-y: auto;
|
padding: 20px;
|
}
|
|
.main-content {
|
flex: 1;
|
background-color: #ffffff;
|
overflow-x: auto;
|
padding: 10px;
|
/* overflow-y: auto; */
|
}
|
|
.dynamic_table {
|
overflow-x: auto;
|
width: 100%;
|
}
|
|
.wrap-text {
|
white-space: pre-line !important;
|
/* 使用 pre-line 属性实现自动换行 */
|
}
|
|
.search-section {
|
/* margin-bottom: 10px; */
|
padding: 10px;
|
/* background-color: #f5f5f5; */
|
border-radius: 4px;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
display: flex;
|
justify-content: flex-start;
|
}
|
|
.selectClass {
|
width: 220px;
|
}
|
.el-form-item {
|
width: 260px !important;
|
}
|
</style>
|