@@ -14,7 +14,7 @@
< a-descriptions-item label = "提案编号" > { { proposal . proposalNo || '-' } } < / a-descriptions-item >
< a-descriptions-item label = "提案名称" > { { proposal . proposalName || '-' } } < / a-descriptions-item >
< a-descriptions-item label = "提出日期" > { { formatDate ( proposal . proposalDate ) } } < / a-descriptions-item >
< a-descriptions-item label = "提案类别" > { { proposal . proposalCategory || '-' } } < / a-descriptions-item >
< a-descriptions-item label = "提案类别" > { { proposal . proposalCategory _dictText || proposal . proposalCategory || '-' } } < / a-descriptions-item >
< a-descriptions-item label = "提案部门" > { { proposal . proposalDeptName || '-' } } < / a-descriptions-item >
< a-descriptions-item label = "申请人" > { { proposal . applicantName || '-' } } < / a-descriptions-item >
< a-descriptions-item label = "团队名称" :span = "2" > { { proposal . teamName || '-' } } < / a-descriptions-item >
@@ -152,49 +152,101 @@
/ >
< / template >
< a-modal v -model :open = "scoreModalVisible" title = "部门评议打分" :footer = "null" :mask-closable = "false" : body -style = " { padding : ' 24px 28px 20px ' } " >
< a-modal
v -model :open = "scoreModalVisible"
title = "部门评议打分"
:footer = "null"
:mask-closable = "false"
:width = "820"
: style = "{ maxWidth: 'calc(100vw - 24px)' }"
: body -style = " { padding : ' 24px 28px 20px ' } "
>
< a-form layout = "vertical" >
< a-alert class = "score-range-alert" type = "info" show -icon message = "所有评分项均须填写 0-100 分,最多可保留两位小数。" / >
< a-row :gutter = "12 ">
< a-col :span = "24" >
< a-form-item label = "改善效果(权重 50%) " required >
< a-row :gutter = "12" >
< a-col :xs = "24" :sm = "12" :lg = "6" >
< a-form-item label = "安全" required > < a-input-number v -model :value = "scoreForm.safetyScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / > < / a-form-item >
< / a-col >
< a-col :xs = "24" :sm = "12" :lg = "6" >
< a-form-item label = "质量" required > < a-input-number v -model :value = "scoreForm.qualityScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / > < / a-form-item >
< / a-col >
< a-col :xs = "24" :sm = "12" :lg = "6 ">
< a-form-item label = "效率" required > < a-input-number v -model :value = "scoreForm.efficiencyScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / > < / a-form-item >
< / a-col >
< a-col :xs = "24" :sm = "12" :lg = "6" >
< a-form-item label = "成本" required > < a-input-number v -model :value = "scoreForm.costScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / > < / a-form-item >
< / a-col >
< / a-row >
< div class = "form-help" > 改善效果得分按安全 、 质量 、 效率 、 成本四项的平均分自动计算 : { { effectAveragePreview } } < / div >
< / a-form-item >
< / a-col >
< a-col :xs = "24" :sm = "12" >
< a-form-item label = "可推广度(权重 20%) " required >
< a-input-number v -model :value = "scoreForm.promotion Score" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / >
< / a-form-item >
< / a-col >
< a-col :xs = "24 " :sm = "12" >
< a-form-item label = "独创性(权重 20%) " required >
< a-input-number v -model :value = "scoreForm.originalityScore" :min = "0 " :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / >
< / a-form-item >
< / a-col >
< a-col :xs = "24" :sm = "12" >
< a-form-item label = "努力度(权重 10%) " required >
< a-input-number v -model :value = "scoreForm.effortScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / >
< / a-form-item >
< / a-col >
< / a-row >
< a-space >
< div class = "score-standard-entry ">
< span > 评分标准附件 : < / span >
< a-button type = "link" size = "small" class = "score-standard-button" @click ="openScoreStandard" >
< Icon icon = "ant-design:paper-clip-outlined" / >
查看评分标准
< / a-button >
< / div >
<!-- 四个大类维度仅调整展示层级 , 字段绑定和加权计算规则保持不变 。 -- >
< div class = "score-dimension-list" >
< section class = "score-dimension score-effect-dimension" >
< div class = "score-dimension-header ">
< div class = "score-dimension-name" >
< span class = "score-dimension-index" > 01 < / span >
< strong class = "score-dimension-title" > 改善效果 < / strong >
< / div >
< span class = "score-dimension-weight" > 权重 50 % < / span >
< / div >
< a-row : gutter = "[12, 12]" >
< a-col :xs = "24" :sm = "12" :lg = "6" >
< a-form-item class = "score-subitem" label = "安全" required > < a-input-number v -model :value = "scoreForm.safetyScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / > < / a-form-item >
< / a-col >
< a-col :xs = "24" :sm = "12" :lg = "6" >
< a-form-item class = "score-subitem" label = "质量" required > < a-input-number v -model :value = "scoreForm.quality Score" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / > < / a-form-item >
< / a-col >
< a-col :xs = "24" :sm = "12" :lg = "6" >
< a-form-item class = "score-subitem" label = "效率" required > < a-input-number v -model :value = "scoreForm.efficiencyScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / > < / a-form-item >
< / a-col >
< a-col :xs = "24 " :s m = "12" :lg = "6" >
< a-form-item class = "score-subitem" label = "成本" required > < a-input-number v -model :value = "scoreForm.costScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / > < / a-form-item >
< / a-col >
< / a-row >
< div class = "effect-score-summary" >
< span > 安全 、 质量 、 效率 、 成本四项平均分 < / span >
< strong > { { effectAveragePreview } } < / strong >
< / div >
< / section >
< a-row class = "score-dimension-grid" : gutter = "[12, 12]" >
< a-col :xs = "24" :md = "8" >
< section class = "score-dimension" >
< div class = "score-dimension-header" >
< div class = "score-dimension-name" >
< span class = "score-dimension-index" > 02 < / span >
< strong class = "score-dimension-title" > 可推广度 < / strong >
< / div >
< span class = "score-dimension-weight" > 权重 20 % < / span >
< / div >
< a-input-number v -model :value = "scoreForm.promotionScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / >
< / section >
< / a-col >
< a-col :xs = "24" :md = "8" >
< section class = "score-dimension" >
< div class = "score-dimension-header" >
< div class = "score-dimension-name" >
< span class = "score-dimension-index" > 03 < / span >
< strong class = "score-dimension-title" > 独创性 < / strong >
< / div >
< span class = "score-dimension-weight" > 权重 20 % < / span >
< / div >
< a-input-number v -model :value = "scoreForm.originalityScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / >
< / section >
< / a-col >
< a-col :xs = "24" :md = "8" >
< section class = "score-dimension" >
< div class = "score-dimension-header" >
< div class = "score-dimension-name" >
< span class = "score-dimension-index" > 04 < / span >
< strong class = "score-dimension-title" > 努力度 < / strong >
< / div >
< span class = "score-dimension-weight" > 权重 10 % < / span >
< / div >
< a-input-number v -model :value = "scoreForm.effortScore" :min = "0" :max = "100" :precision = "2" :disabled = "scoreLocked" style = "width: 100%" / >
< / section >
< / a-col >
< / a-row >
< / div >
< div class = "weighted-total-summary" >
< span > 加权总分 < / span >
< strong > { { weightedTotalPreview } } < / strong >
< / div >
< div class = "score-form-actions" >
< a-button v-if = "!scoreLocked" type="primary" :loading="savingScore" @click="saveScoreDraft" > 保存评分 < / a -button >
< span v-else > 评分已锁定 , 请重新提交流程办理 < / span >
< / a -space >
< / div >
< / a -form >
< / a-modal >
< / section >
@@ -255,6 +307,8 @@
{ value : '2' , label : '铜奖' } ,
{ value : '3' , label : '参与奖' } ,
] ;
// 评分标准由 MinIO 提供,使用完整地址避免受前端部署路径影响。
const scoreStandardUrl = 'http://localhost:9000/api/v1/buckets/supervision/objects/download?prefix=%E6%94%B9%E5%96%84%E6%8F%90%E6%A1%88%E8%AF%84%E5%88%86%E6%A0%87%E5%87%86(1).xlsx&version_id=null' ;
// 流程引擎在运行待办中提供 taskDefKey,节点功能只依赖该稳定标识而非表单 URL 参数。
const processInfo = computed ( ( ) => ( {
mainId : props . formData ? . processDataId || props . formData ? . dataId ,
@@ -275,10 +329,18 @@
if ( ! currentScore . value ) return '未保存' ;
return scoreLocked . value ? '评分已锁定' : '已保存,待流程提交' ;
} ) ;
const effectAveragePreview = computed ( ( ) => {
const values = [ 'safetyScore' , 'qualityScore' , 'efficiencyScore' , 'costScore' ] . map ( ( key ) => scoreForm . value [ key ] ) ;
if ( values . some ( ( value ) => value === undefined || value === null ) ) return '-' ;
return ( values . reduce ( ( sum , value ) => sum + Number ( value ) , 0 ) / values . length ) . toFixed ( 2 ) ;
// 以前端“分”的整数计算,匹配服务端 BigDecimal 在改善效果和总分阶段的两次 HALF_UP 取整。
const effectScoreCents = computed ( ( ) => calculateEffectScoreCents ( ) ) ;
const effectAveragePreview = computed ( ( ) => formatScoreCents ( effectScoreCents . value ) ) ;
const weightedTotalPreview = computed ( ( ) => {
const effectCents = effectScoreCents . value ;
const promotionCents = getScoreCents ( scoreForm . value . promotionScore ) ;
const originalityCents = getScoreCents ( scoreForm . value . originalityScore ) ;
const effortCents = getScoreCents ( scoreForm . value . effortScore ) ;
if ( effectCents === null || promotionCents === null || originalityCents === null || effortCents === null ) return '-' ;
const totalCents = Math . round ( effectCents * 0.5 + promotionCents * 0.2 + originalityCents * 0.2 + effortCents * 0.1 ) ;
return formatScoreCents ( totalCents ) ;
} ) ;
// 部门评分完成后必须以系统建议奖项为基准,不能回退到初审奖项。
const leaderAwardBase = computed ( ( ) => {
@@ -448,6 +510,10 @@
scoreModalVisible . value = true ;
}
function openScoreStandard ( ) {
window . open ( scoreStandardUrl , '_blank' , 'noopener,noreferrer' ) ;
}
async function saveScoreDraft ( ) {
if ( ! hasValidScoreValues ( ) ) {
createMessage . warning ( '请完整填写 0 到 100 分之间的七项评分' ) ;
@@ -478,6 +544,22 @@
} ;
}
function calculateEffectScoreCents ( ) : number | null {
const values = [ 'safetyScore' , 'qualityScore' , 'efficiencyScore' , 'costScore' ] . map ( ( key ) => getScoreCents ( scoreForm . value [ key ] ) ) ;
if ( values . some ( ( value ) => value === null ) ) return null ;
return Math . round ( ( values as number [ ] ) . reduce ( ( sum , value ) => sum + value , 0 ) / values . length ) ;
}
function getScoreCents ( value : unknown ) : number | null {
if ( value === undefined || value === null || value === '' ) return null ;
const score = Number ( value ) ;
return Number . isFinite ( score ) ? Math . round ( score * 100 ) : null ;
}
function formatScoreCents ( scoreCents : number | null ) : string {
return scoreCents === null ? '-' : ( scoreCents / 100 ) . toFixed ( 2 ) ;
}
function hasValidScoreValues ( ) {
return [ 'safetyScore' , 'qualityScore' , 'efficiencyScore' , 'costScore' , 'promotionScore' , 'originalityScore' , 'effortScore' ] . every ( ( key ) => {
const value = scoreForm . value [ key ] ;
@@ -594,6 +676,145 @@
margin - bottom : 16 px ;
}
. score - standard - entry {
display : flex ;
flex - wrap : wrap ;
align - items : center ;
gap : 2 px ;
margin : - 8 px 0 16 px ;
color : # 64748 b ;
font - size : 14 px ;
}
. score - standard - button {
display : inline - flex ;
align - items : center ;
gap : 4 px ;
padding - inline : 2 px ;
}
. score - dimension - list {
display : flex ;
flex - direction : column ;
gap : 12 px ;
}
. score - dimension {
min - height : 126 px ;
padding : 14 px ;
border - left : 3 px solid # 1677 ff ;
background : # f6fbff ;
}
. score - dimension - header {
display : flex ;
align - items : center ;
justify - content : space - between ;
gap : 12 px ;
margin - bottom : 14 px ;
}
. score - dimension - name {
display : inline - flex ;
align - items : center ;
min - width : 0 ;
gap : 8 px ;
}
. score - dimension - index {
flex : 0 0 auto ;
color : # 1677 ff ;
font - size : 14 px ;
font - weight : 700 ;
line - height : 1 ;
}
. score - dimension - title {
color : # 1 f2937 ;
font - size : 17 px ;
font - weight : 700 ;
line - height : 1.4 ;
}
. score - dimension - weight {
flex : 0 0 auto ;
color : # 1677 ff ;
font - size : 14 px ;
font - weight : 600 ;
white - space : nowrap ;
}
. score - effect - dimension {
min - height : auto ;
}
. score - subitem {
margin - bottom : 0 ;
: deep ( . ant - form - item - label > label ) {
color : # 334155 ;
font - size : 15 px ;
font - weight : 600 ;
}
}
. score - dimension {
: deep ( . ant - input - number - input ) {
color : # 1 f2937 ;
font - size : 16 px ;
font - weight : 600 ;
}
}
. effect - score - summary {
display : flex ;
align - items : baseline ;
justify - content : space - between ;
gap : 12 px ;
margin - top : 14 px ;
padding - top : 10 px ;
border - top : 1 px solid # d6e8ff ;
color : # 475569 ;
font - size : 14 px ;
strong {
flex : 0 0 auto ;
color : # 1677 ff ;
font - size : 20 px ;
font - weight : 700 ;
}
}
. weighted - total - summary {
display : flex ;
align - items : baseline ;
justify - content : space - between ;
gap : 12 px ;
margin - top : 16 px ;
padding : 14 px 16 px ;
border - left : 4 px solid # 1677 ff ;
background : # e6f4ff ;
color : # 1 f2937 ;
font - size : 16 px ;
font - weight : 700 ;
strong {
flex : 0 0 auto ;
color : # 0958 d9 ;
font - size : 28 px ;
font - weight : 700 ;
line - height : 1 ;
}
}
. score - form - actions {
display : flex ;
align - items : center ;
justify - content : flex - end ;
min - height : 32 px ;
margin - top : 20 px ;
}
. institute - vote - summary {
display : flex ;
flex - wrap : wrap ;
@@ -702,6 +923,37 @@
}
@ media ( max - width : 576 px ) {
. score - dimension {
min - height : auto ;
padding : 12 px ;
}
. score - dimension - header {
margin - bottom : 12 px ;
}
. effect - score - summary {
align - items : flex - start ;
flex - direction : column ;
gap : 2 px ;
}
. weighted - total - summary {
padding : 12 px ;
strong {
font - size : 24 px ;
}
}
. score - form - actions {
justify - content : stretch ;
: deep ( . ant - btn ) {
width : 100 % ;
}
}
. base - info - header {
align - items : flex - start ;
}