feat: 任务时效累计计算,支持分钟级精度和工作日计算

This commit is contained in:
Your Name 2026-01-05 23:30:51 +08:00
parent e6308b937f
commit a2fd8a63b0
3 changed files with 133 additions and 5 deletions

View File

@ -47,10 +47,10 @@ public class DemandType implements Serializable {
private Integer unitScoreInt;
/**
* 预估工时(小时)
* 预估工时(小时,支持小数如0.05表示3分钟)
*/
@TableField("estimated_hours")
private Integer estimatedHours;
private BigDecimal estimatedHours;
/**
* 类型描述

View File

@ -15,6 +15,22 @@
<span v-if="isAdmin" style="color: #31f08b;">管理员身份,无需积分即可发起任务</span>
<span v-else>本次任务预估消耗: <strong>{{ estimatedScore }}</strong> 分</span>
</div>
<!-- 时效预估 -->
<div v-if="estimatedTotalHours > 0" class="time-estimate">
<div class="estimate-item">
<span class="estimate-label">累计时效:</span>
<span class="estimate-value">{{ formattedEstimatedTime }}</span>
</div>
<div class="estimate-item">
<span class="estimate-label">预计完成:</span>
<span class="estimate-value">{{ estimatedDeadline }}</span>
</div>
<div class="estimate-tip">
<Icon name="info" size="xs" style="margin-right: 4px; opacity: 0.6;" />
<span>按8小时/工作日计算,超过8小时按整天计算</span>
</div>
</div>
</div>
<!-- 任务表单 -->
@ -884,6 +900,75 @@ const estimatedScore = computed(() => {
return totalScore
})
// 计算累计时效(小时)
const estimatedTotalHours = computed(() => {
let totalHours = 0
for (const item of formData.taskTypes) {
if (item.taskTypeId && item.quantity > 0) {
const selectedType = taskTypes.value.find(t => t.id === item.taskTypeId)
if (selectedType && selectedType.estimatedHours) {
totalHours += selectedType.estimatedHours * item.quantity
}
}
}
return totalHours
})
// 格式化时效显示
const formattedEstimatedTime = computed(() => {
const hours = estimatedTotalHours.value
if (hours === 0) return '--'
if (hours < 1) {
return `${Math.round(hours * 60)}分钟`
} else if (hours < 24) {
const h = Math.floor(hours)
const m = Math.round((hours - h) * 60)
return m > 0 ? `${h}小时${m}分钟` : `${h}小时`
} else {
const days = Math.floor(hours / 24)
const remainHours = Math.round(hours % 24)
return remainHours > 0 ? `${days}天${remainHours}小时` : `${days}天`
}
})
// 计算预计完成时间(考虑8小时工作制)
const estimatedDeadline = computed(() => {
const totalHours = estimatedTotalHours.value
if (totalHours === 0) return '--'
const now = new Date()
const WORK_HOURS_PER_DAY = 8 // 每天工作8小时
// 计算需要多少个工作日
let workDays = Math.floor(totalHours / WORK_HOURS_PER_DAY)
let remainingHours = totalHours % WORK_HOURS_PER_DAY
// 如果超过8小时,按24小时(1天)计算
if (totalHours > WORK_HOURS_PER_DAY) {
// 超过8小时的部分,每8小时算1个工作日,但日历上是24小时
const totalDays = Math.ceil(totalHours / WORK_HOURS_PER_DAY)
const deadline = new Date(now.getTime() + totalDays * 24 * 60 * 60 * 1000)
return formatDeadline(deadline)
} else {
// 8小时内,当天完成
const deadline = new Date(now.getTime() + totalHours * 60 * 60 * 1000)
return formatDeadline(deadline)
}
})
// 格式化截止时间
const formatDeadline = (date: Date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hour = String(date.getHours()).padStart(2, '0')
const minute = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hour}:${minute}`
}
// 计算流水线分组
const pipelineGroups = computed(() => {
if (formData.taskTypes.length === 0) {
@ -1621,6 +1706,44 @@ onUnmounted(() => {
strong {
color: $brand-primary;
}
}
.time-estimate {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: $space-md;
margin-top: $space-md;
padding: $space-md;
background: rgba(255, 255, 255, 0.03);
border-radius: $radius-small;
border: 1px solid rgba(255, 255, 255, 0.06);
.estimate-item {
display: flex;
align-items: center;
gap: $space-xs;
.estimate-label {
color: $text-tertiary;
font-size: $font-size-sm;
}
.estimate-value {
color: $brand-primary;
font-weight: 600;
font-size: $font-size-base;
}
}
.estimate-tip {
flex-basis: 100%;
display: flex;
align-items: center;
font-size: $font-size-xs;
color: $text-tertiary;
margin-top: $space-xs;
font-weight: $font-weight-semibold;
}
}

View File

@ -1265,14 +1265,19 @@
</a-form-item>
<!-- 预计时长(二级和三级分类都可以设置) -->
<a-form-item v-if="formData.demandType.addLevel === 'level2' || formData.demandType.addLevel === 'level3'" label="预计时长(小时)">
<a-form-item v-if="formData.demandType.addLevel === 'level2' || formData.demandType.addLevel === 'level3'" label="单个任务耗时(小时)">
<a-input-number
v-model:value="formData.demandType.estimatedHours"
:min="0"
:precision="1"
:precision="2"
:step="0.05"
style="width: 100%"
placeholder="请输入预计时长"
placeholder="单个任务耗时(如0.05=3分钟,1=1小时)"
/>
<div class="form-tip">
<Icon name="info" size="sm" color="#6B6B6B" style="margin-right: 4px;" />
时效会根据任务数量累计计算(如5个任务×0.05小时=0.25小时)
</div>
</a-form-item>
<a-form-item label="描述">