diff --git a/src/views/gh/improvementProposal/ImprovementProposal.data.ts b/src/views/gh/improvementProposal/ImprovementProposal.data.ts index 98f1beb..1f01377 100644 --- a/src/views/gh/improvementProposal/ImprovementProposal.data.ts +++ b/src/views/gh/improvementProposal/ImprovementProposal.data.ts @@ -86,6 +86,12 @@ export const columns: BasicColumn[] = [ dataIndex: 'bpmStatus_dictText', width:90 + dataIndex: 'finalAward_dictText' + }, + { + title: '流程状态', + align:"center", + dataIndex: 'bpmStatus_dictText' }, ]; @@ -325,7 +331,7 @@ export const superQuerySchema = { problemDesc: {title: '问题描述',order: 9,view: 'textarea', type: 'string',}, improvementMethod: {title: '改善方法及措施',order: 10,view: 'textarea', type: 'string',}, finalAward: {title: '最终奖项',order: 14,view: 'text', type: 'string',}, - bpmStatus: {title: '业务展示状态',order: 15,view: 'text', type: 'string',}, + bpmStatus: {title: '流程状态',order: 15,view: 'text', type: 'string',}, //子表高级查询 improvementDeptScore: { title: '部门评议评分表', diff --git a/src/views/gh/improvementProposal/ImprovementProposalList.vue b/src/views/gh/improvementProposal/ImprovementProposalList.vue index fecce76..676c462 100644 --- a/src/views/gh/improvementProposal/ImprovementProposalList.vue +++ b/src/views/gh/improvementProposal/ImprovementProposalList.vue @@ -20,6 +20,8 @@ + + diff --git a/src/views/gh/improvementProposal/components/ImprovementProposalBPMForm.vue b/src/views/gh/improvementProposal/components/ImprovementProposalBPMForm.vue index 9936aaf..2ff8be8 100644 --- a/src/views/gh/improvementProposal/components/ImprovementProposalBPMForm.vue +++ b/src/views/gh/improvementProposal/components/ImprovementProposalBPMForm.vue @@ -14,7 +14,7 @@ {{ proposal.proposalNo || '-' }} {{ proposal.proposalName || '-' }} {{ formatDate(proposal.proposalDate) }} - {{ proposal.proposalCategory || '-' }} + {{ proposal.proposalCategory_dictText || proposal.proposalCategory || '-' }} {{ proposal.proposalDeptName || '-' }} {{ proposal.applicantName || '-' }} {{ proposal.teamName || '-' }} @@ -152,49 +152,101 @@ /> - + - - - - - - - - - - - - - - - - - -
改善效果得分按安全、质量、效率、成本四项的平均分自动计算:{{ effectAveragePreview }}
-
-
- - - - - - - - - - - - - - - -
- +
+ 评分标准附件: + + + 查看评分标准 + +
+ +
+
+
+
+ 01 + 改善效果 +
+ 权重 50% +
+ + + + + + + + + + + + + + +
+ 安全、质量、效率、成本四项平均分 + {{ effectAveragePreview }} +
+
+ + + +
+
+
+ 02 + 可推广度 +
+ 权重 20% +
+ +
+
+ +
+
+
+ 03 + 独创性 +
+ 权重 20% +
+ +
+
+ +
+
+
+ 04 + 努力度 +
+ 权重 10% +
+ +
+
+
+
+
+ 加权总分 + {{ weightedTotalPreview }} +
+
保存评分 评分已锁定,请重新提交流程办理 - +
@@ -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: 16px; } + .score-standard-entry { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 2px; + margin: -8px 0 16px; + color: #64748b; + font-size: 14px; + } + + .score-standard-button { + display: inline-flex; + align-items: center; + gap: 4px; + padding-inline: 2px; + } + + .score-dimension-list { + display: flex; + flex-direction: column; + gap: 12px; + } + + .score-dimension { + min-height: 126px; + padding: 14px; + border-left: 3px solid #1677ff; + background: #f6fbff; + } + + .score-dimension-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 14px; + } + + .score-dimension-name { + display: inline-flex; + align-items: center; + min-width: 0; + gap: 8px; + } + + .score-dimension-index { + flex: 0 0 auto; + color: #1677ff; + font-size: 14px; + font-weight: 700; + line-height: 1; + } + + .score-dimension-title { + color: #1f2937; + font-size: 17px; + font-weight: 700; + line-height: 1.4; + } + + .score-dimension-weight { + flex: 0 0 auto; + color: #1677ff; + font-size: 14px; + 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: 15px; + font-weight: 600; + } + } + + .score-dimension { + :deep(.ant-input-number-input) { + color: #1f2937; + font-size: 16px; + font-weight: 600; + } + } + + .effect-score-summary { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 12px; + margin-top: 14px; + padding-top: 10px; + border-top: 1px solid #d6e8ff; + color: #475569; + font-size: 14px; + + strong { + flex: 0 0 auto; + color: #1677ff; + font-size: 20px; + font-weight: 700; + } + } + + .weighted-total-summary { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 12px; + margin-top: 16px; + padding: 14px 16px; + border-left: 4px solid #1677ff; + background: #e6f4ff; + color: #1f2937; + font-size: 16px; + font-weight: 700; + + strong { + flex: 0 0 auto; + color: #0958d9; + font-size: 28px; + font-weight: 700; + line-height: 1; + } + } + + .score-form-actions { + display: flex; + align-items: center; + justify-content: flex-end; + min-height: 32px; + margin-top: 20px; + } + .institute-vote-summary { display: flex; flex-wrap: wrap; @@ -702,6 +923,37 @@ } @media (max-width: 576px) { + .score-dimension { + min-height: auto; + padding: 12px; + } + + .score-dimension-header { + margin-bottom: 12px; + } + + .effect-score-summary { + align-items: flex-start; + flex-direction: column; + gap: 2px; + } + + .weighted-total-summary { + padding: 12px; + + strong { + font-size: 24px; + } + } + + .score-form-actions { + justify-content: stretch; + + :deep(.ant-btn) { + width: 100%; + } + } + .base-info-header { align-items: flex-start; } diff --git a/src/views/gh/improvementProposal/components/ImprovementProposalForm.vue b/src/views/gh/improvementProposal/components/ImprovementProposalForm.vue index 73cd63d..45f88c9 100644 --- a/src/views/gh/improvementProposal/components/ImprovementProposalForm.vue +++ b/src/views/gh/improvementProposal/components/ImprovementProposalForm.vue @@ -134,6 +134,7 @@ import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue'; import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue'; import { useUserStore } from '/@/store/modules/user'; + import { useMessage } from '/@/hooks/web/useMessage'; const useForm = Form.useForm; export default defineComponent({ @@ -154,6 +155,7 @@ }, emits:['success'], setup(props, {emit}) { + const { createMessage } = useMessage(); const loading = ref(false); const formRef = ref(); const beforeFileRef = ref(); @@ -417,12 +419,15 @@ const result = await saveOrUpdate(values, isUpdate); // 新增阶段附件暂不绑定;主表生成 ID 后统一关联,保证普通表单和 BPM 使用同一查询键。 if (!isUpdate) { - const newId = typeof result === 'string' ? result : (result?.id || result); - if (newId) { - formData.id = newId; - await beforeFileRef.value?.bindBussinessId(`${formData.id}:before`); - await afterFileRef.value?.bindBussinessId(`${formData.id}:after`); + const newId = typeof result?.id === 'string' ? result.id.trim() : ''; + if (!newId) { + // 阻断成功文案等非业务 ID 写入附件表,避免后续 BPM 无法按主表 ID 查询附件。 + createMessage.error('新增成功但未返回提案ID,附件未关联,请刷新后重试'); + return; } + formData.id = newId; + await beforeFileRef.value?.bindBussinessId(`${formData.id}:before`); + await afterFileRef.value?.bindBussinessId(`${formData.id}:after`); } emit('success'); }