400 lines
9.9 KiB
Vue
400 lines
9.9 KiB
Vue
<template>
|
||
<view class="container">
|
||
<u-navbar
|
||
class="u-navbar"
|
||
title="日计划管理"
|
||
placeholder
|
||
@leftClick="leftClick"
|
||
leftIconColor="#fff"
|
||
bgColor="#00337A"
|
||
:titleStyle="{ color: '#FFF', fontSize: '32rpx' }"
|
||
/>
|
||
<!-- Header -->
|
||
<view class="header">
|
||
<view class="date-section">
|
||
<picker mode="date" :value="currentDate" @change="onDateChange">
|
||
<view class="date-picker">
|
||
<image src="@/static/realName/attendance.png" class="calendar-icon"></image>
|
||
<text>{{ currentDate }}</text>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
<view class="progress-section">
|
||
<text class="reminder-text">请于每日18:00点前提交明日计划</text>
|
||
<button class="primary-btn" @click="developDailyPlan">制定日计划</button>
|
||
</view>
|
||
<view class="project-title">
|
||
<text>{{ proName }}</text>
|
||
</view>
|
||
<view class="completion-rate">
|
||
<text>作业计划完成率:{{ totalComplete }}%</text>
|
||
<text>月完成计划完成率:{{ monthComplete }}%</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="task-list">
|
||
<block v-for="(task, index) in taskList" :key="index">
|
||
<view class="task-item">
|
||
<view class="task-header">
|
||
<text class="timestamp">{{ task.day }}</text>
|
||
<view :class="['status', getStatusClass(task)]">{{ getTaskStatusText(task) }}</view>
|
||
</view>
|
||
<view class="metrics-container">
|
||
<view class="metric-row">
|
||
<text class="metric-item">总计划数量:{{ task.totalNum }}</text>
|
||
<text class="metric-item">月计划数量:{{ task.completeNumMonth }}</text>
|
||
<text class="metric-item">计划数量:{{ task.planCompleteNumDay }}</text>
|
||
</view>
|
||
<view class="metric-row">
|
||
<text class="metric-item">累计完成作业量:{{ task.completeNumDay }}</text>
|
||
<text class="metric-item">月累计完成作业量:{{ task.completeNumAllMonth }}</text>
|
||
<text class="metric-item">作业人数:{{ task.planWorkNum }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="actions">
|
||
<button class="action-btn" @click="detailsPlan(task)">详情</button>
|
||
<button class="action-btn" @click="uploadPlan(task)" v-if="task.path == null || task.path === ''">
|
||
完成电子上传
|
||
</button>
|
||
<button class="action-btn" @click="editPlan(task)" v-if="task.path == null || task.path === ''">
|
||
完成情况修改
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</block>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// 定义一个不依赖于Vue实例的日期格式化函数
|
||
function formatDate(date) {
|
||
const year = date.getFullYear()
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
return `${year}-${month}-${day}`
|
||
}
|
||
|
||
import config from '@/config'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
currentDate: formatDate(new Date()),
|
||
proId: uni.getStorageSync('realNameUser').proId,
|
||
proName: '',
|
||
taskList: [],
|
||
totalComplete: '0.00',
|
||
monthComplete: '0.00',
|
||
id: '',
|
||
dayId: ''
|
||
}
|
||
},
|
||
created() {
|
||
// 如果需要在组件创建后再次设置currentDate为最新的当前日期,可以在这里做
|
||
this.currentDate = formatDate(new Date())
|
||
this.getListData()
|
||
},
|
||
onLoad() {
|
||
// this.getListData()
|
||
},
|
||
methods: {
|
||
onDateChange(e) {
|
||
this.currentDate = e.detail.value
|
||
this.getListData()
|
||
},
|
||
|
||
developDailyPlan() {
|
||
uni.navigateTo({
|
||
//制定日计划
|
||
url: `/pages/realName/workbench/dailyPlanManagement/develop?proId=${encodeURIComponent(
|
||
this.proId
|
||
)}&proName=${encodeURIComponent(this.proName)}&id=${encodeURIComponent(this.id)}`
|
||
})
|
||
},
|
||
|
||
editPlan() {
|
||
uni.navigateTo({
|
||
//修改日计划
|
||
url: `/pages/realName/workbench/dailyPlanManagement/edit?dayId=${encodeURIComponent(
|
||
this.dayId
|
||
)}&id=${encodeURIComponent(this.id)}&proName=${encodeURIComponent(this.proName)}`
|
||
})
|
||
},
|
||
|
||
uploadPlan() {
|
||
uni.navigateTo({
|
||
//完成情况上传
|
||
url: `/pages/realName/workbench/dailyPlanManagement/upload?dayId=${encodeURIComponent(
|
||
this.dayId
|
||
)}&id=${encodeURIComponent(this.id)}&proName=${encodeURIComponent(this.proName)}`
|
||
})
|
||
},
|
||
detailsPlan() {
|
||
uni.navigateTo({
|
||
//日计划详情
|
||
url: `/pages/realName/workbench/dailyPlanManagement/details?dayId=${encodeURIComponent(
|
||
this.dayId
|
||
)}&id=${encodeURIComponent(this.id)}&proName=${encodeURIComponent(this.proName)}`
|
||
})
|
||
},
|
||
|
||
// 返回
|
||
leftClick() {
|
||
console.log('返回')
|
||
uni.navigateBack({
|
||
delta: 1 // 返回
|
||
})
|
||
},
|
||
getListData() {
|
||
let param = {
|
||
proId: this.proId,
|
||
date: this.currentDate
|
||
}
|
||
|
||
console.log('param查询参数', param)
|
||
uni.request({
|
||
url: config.realAppUrl + '/AppPlan/selectDayPlan',
|
||
method: 'post',
|
||
data: JSON.stringify(param),
|
||
header: {
|
||
'content-type': 'application/json',
|
||
Authorization: uni.getStorageSync('realNameToken')
|
||
},
|
||
success: res => {
|
||
console.log('日计划管理', res)
|
||
if (res.data.code == 200) {
|
||
this.taskList = res.data.data
|
||
const firstProName = res.data.data.length > 0 ? res.data.data[0].proName : null
|
||
this.proName = firstProName
|
||
this.totalComplete = this.overallCompletionRate()
|
||
this.monthComplete = this.monthlyCompletionRate()
|
||
this.id = res.data.data[0].id
|
||
this.dayId = res.data.data[0].dayId
|
||
} else {
|
||
uni.$u.toast(res.data.msg)
|
||
this.taskList = ''
|
||
}
|
||
},
|
||
fail: err => {
|
||
console.log(err)
|
||
}
|
||
})
|
||
},
|
||
getStatusClass(task) {
|
||
var planCompleteNumDay = task.planCompleteNumDay
|
||
var completeNumDay = task.completeNumDay
|
||
var status = ''
|
||
if (planCompleteNumDay > completeNumDay) {
|
||
status = '进行中'
|
||
} else {
|
||
status = '已结束'
|
||
}
|
||
switch (status) {
|
||
case '进行中':
|
||
return 'in-progress'
|
||
case '已结束':
|
||
return 'completed'
|
||
default:
|
||
return 'not-started'
|
||
}
|
||
},
|
||
|
||
getTaskStatusText(task) {
|
||
var planCompleteNumDay = task.planCompleteNumDay
|
||
var completeNumDay = task.completeNumDay
|
||
var status = ''
|
||
if (planCompleteNumDay > completeNumDay) {
|
||
status = '进行中'
|
||
} else {
|
||
status = '已结束'
|
||
}
|
||
|
||
switch (status) {
|
||
case '未开始':
|
||
return '未开始'
|
||
case '进行中':
|
||
return '进行中'
|
||
case '已结束':
|
||
return '已结束'
|
||
default:
|
||
return '未开始'
|
||
}
|
||
},
|
||
|
||
// 作业计划完成率 = (累计完成作业量 / 总计划数量) * 100%
|
||
overallCompletionRate() {
|
||
let totalPlan = 0
|
||
let completedWork = 0
|
||
|
||
this.taskList.forEach(task => {
|
||
if (task.completeNumDay && task.planCompleteNumDay) {
|
||
totalPlan += parseFloat(task.planCompleteNumDay)
|
||
completedWork += parseFloat(task.completeNumDay)
|
||
}
|
||
})
|
||
|
||
return totalPlan > 0 ? ((completedWork / totalPlan) * 100).toFixed(2) : '0.00'
|
||
},
|
||
|
||
// 月完成计划完成率 = (月累计完成作业量 / 总计划数量) * 100%
|
||
monthlyCompletionRate() {
|
||
let totalPlan = 0
|
||
let completedWorkInMonth = 0
|
||
|
||
this.taskList.forEach(task => {
|
||
if (task.completeNumAllMonth && task.completeNumMonth) {
|
||
totalPlan += parseFloat(task.completeNumAllMonth)
|
||
completedWorkInMonth += parseFloat(task.completeNumMonth)
|
||
}
|
||
})
|
||
return totalPlan > 0 ? ((completedWorkInMonth / totalPlan) * 100).toFixed(2) : '0.00'
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.container {
|
||
padding: 20rpx;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.header {
|
||
background-color: #fff;
|
||
padding: 20rpx;
|
||
border-radius: 10rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.date-section {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.date-picker {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.calendar-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
.progress-section {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin: 20rpx 0;
|
||
}
|
||
|
||
.reminder-text {
|
||
color: #ff4d4f;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.primary-btn {
|
||
background-color: #0099ff;
|
||
color: #fff;
|
||
padding: 0rpx 20rpx;
|
||
border-radius: 20rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.project-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
margin: 20rpx 0;
|
||
}
|
||
|
||
.completion-rate {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
color: #666;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.task-item {
|
||
background-color: #fff;
|
||
padding: 20rpx;
|
||
border-radius: 10rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.task-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.timestamp {
|
||
color: #999;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.status {
|
||
display: inline-block;
|
||
padding: 4rpx 20rpx;
|
||
border-radius: 4rpx;
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.not-started {
|
||
background-color: #e6f7ff;
|
||
color: #1890ff;
|
||
}
|
||
|
||
.in-progress {
|
||
background-color: #fff7e6;
|
||
color: #fa8c16;
|
||
}
|
||
|
||
.completed {
|
||
background-color: #f6ffed;
|
||
color: #52c41a;
|
||
}
|
||
|
||
.metrics-container {
|
||
margin: 20rpx 0;
|
||
}
|
||
|
||
.metric-row {
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.metric-item {
|
||
flex: 1;
|
||
text-align: left;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.actions {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.action-btn {
|
||
font-size: 26rpx;
|
||
padding: 0rpx 22rpx;
|
||
border: 1px solid #ddd;
|
||
border-radius: 20rpx;
|
||
background-color: #f5f5f5;
|
||
color: #333;
|
||
}
|
||
|
||
.action-btn.danger {
|
||
color: #fff;
|
||
background-color: #ff4d4f;
|
||
border-color: #ff4d4f;
|
||
}
|
||
</style>
|