yxk-20260728
改善提案 实现部门评议小组打分功能
This commit is contained in:
@@ -17,6 +17,9 @@ enum Api {
|
||||
getLatestProposalSequence = '/improvementproposal/improvementProposal/getLatestProposalSequence',
|
||||
startProcess = '/improvementproposal/improvementProposal/startProcess',
|
||||
bpmInitialReview = '/improvementproposal/improvementProposal/bpm/initialReview',
|
||||
bpmMyDeptScore = '/improvementproposal/improvementProposal/bpm/myDeptScore',
|
||||
bpmDeptScore = '/improvementproposal/improvementProposal/bpm/deptScore',
|
||||
bpmSubmitDeptScore = '/improvementproposal/improvementProposal/bpm/submitDeptScore',
|
||||
bpmLeaderAward = '/improvementproposal/improvementProposal/bpm/leaderAward',
|
||||
bpmArchiveAward = '/improvementproposal/improvementProposal/bpm/archiveAward',
|
||||
bpmInstituteVote = '/improvementproposal/improvementProposal/bpm/instituteVote',
|
||||
@@ -102,6 +105,9 @@ export const startProposalProcess = (id: string) => defHttp.post({ url: Api.star
|
||||
|
||||
/** 流程节点专用接口会在任务提交前保存业务字段和网关变量。 */
|
||||
export const saveInitialReview = (params) => defHttp.post({ url: Api.bpmInitialReview, params });
|
||||
export const queryMyDeptScore = (params) => defHttp.get({ url: Api.bpmMyDeptScore, params });
|
||||
export const saveDeptScore = (params) => defHttp.post({ url: Api.bpmDeptScore, params });
|
||||
export const submitDeptScore = (params) => defHttp.post({ url: Api.bpmSubmitDeptScore, params });
|
||||
export const saveLeaderAward = (params) => defHttp.post({ url: Api.bpmLeaderAward, params });
|
||||
export const saveArchiveAward = (params) => defHttp.post({ url: Api.bpmArchiveAward, params });
|
||||
export const castInstituteVote = (params) => defHttp.post({ url: Api.bpmInstituteVote, params });
|
||||
|
||||
@@ -45,16 +45,15 @@
|
||||
</a-form-item>
|
||||
|
||||
<template v-if="isTask('Task_improve002')">
|
||||
<a-form-item label="部门评分记录">
|
||||
<a-table row-key="id" size="small" bordered :columns="scoreColumns" :data-source="scoreRows" :pagination="false">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">{{ record.scoreStatus === 'completed' ? '已评分' : '待评分' }}</template>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-form-item label="我的评分">
|
||||
<a-descriptions bordered size="small" :column="2">
|
||||
<a-descriptions-item label="评分状态">{{ scoreStatusLabel }}</a-descriptions-item>
|
||||
<a-descriptions-item label="加权总分">{{ currentScore?.totalScore ?? '-' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="评分时间" :span="2">{{ formatDateTime(currentScore?.scoreTime) }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-form-item>
|
||||
<a-form-item label="评分操作">
|
||||
<a-button type="primary" :disabled="!currentScore" @click="openScoreModal">打分</a-button>
|
||||
<span v-if="!currentScore" class="form-help">当前用户不在部门评分名单中。</span>
|
||||
<a-button type="primary" :disabled="scoreLocked" @click="openScoreModal">{{ scoreLocked ? '评分已锁定' : '打分' }}</a-button>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
@@ -111,11 +110,37 @@
|
||||
</template>
|
||||
|
||||
<a-modal v-model:open="scoreModalVisible" title="部门评议打分" :footer="null" :mask-closable="false">
|
||||
<a-descriptions v-if="currentScore" bordered size="small" :column="1">
|
||||
<a-descriptions-item label="评委">{{ currentScore.scorerName || '-' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="评分状态">{{ currentScore.scoreStatus === 'completed' ? '已评分' : '待评分' }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<div class="form-help">四个评分维度录入、加权计算和评分提交将在下一阶段开发。</div>
|
||||
<a-form layout="vertical">
|
||||
<a-row :gutter="12">
|
||||
<a-col :xs="24" :sm="12">
|
||||
<a-form-item label="改善效果(权重 50%)" required>
|
||||
<a-input-number v-model:value="scoreForm.effectScore" :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.promotionScore" :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-form-item label="评分意见">
|
||||
<a-textarea v-model:value="scoreForm.scoreOpinion" :rows="3" :disabled="scoreLocked" />
|
||||
</a-form-item>
|
||||
<a-space>
|
||||
<a-button v-if="!scoreLocked" type="primary" :loading="savingScore" @click="saveScoreDraft">保存评分</a-button>
|
||||
<span v-else>评分已锁定,请重新提交流程办理</span>
|
||||
</a-space>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</section>
|
||||
</a-spin>
|
||||
@@ -131,11 +156,13 @@
|
||||
import {
|
||||
castInstituteVote,
|
||||
queryDataById,
|
||||
queryImprovementDeptScoreListByMainId,
|
||||
queryImprovementInstituteVoteListByMainId,
|
||||
queryMyDeptScore,
|
||||
saveArchiveAward,
|
||||
saveDeptScore,
|
||||
saveInitialReview,
|
||||
saveLeaderAward,
|
||||
submitDeptScore,
|
||||
} from '../ImprovementProposal.api';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -147,14 +174,16 @@
|
||||
const { createMessage } = useMessage();
|
||||
const userStore = useUserStore();
|
||||
const loading = ref(false);
|
||||
const savingScore = ref(false);
|
||||
const savingVote = ref(false);
|
||||
const scoreModalVisible = ref(false);
|
||||
const proposal = ref<Recordable>({});
|
||||
const scoreRows = ref<Recordable[]>([]);
|
||||
const currentScore = ref<Recordable | null>(null);
|
||||
const voteRows = ref<Recordable[]>([]);
|
||||
const initialAward = ref('');
|
||||
const leaderFinalAward = ref('');
|
||||
const finalAward = ref('');
|
||||
const scoreForm = ref<Recordable>(createEmptyScoreForm());
|
||||
const labelCol = { xs: { span: 24 }, sm: { span: 5 } };
|
||||
const wrapperCol = { xs: { span: 24 }, sm: { span: 19 } };
|
||||
|
||||
@@ -164,11 +193,6 @@
|
||||
{ value: '2', label: '铜奖' },
|
||||
{ value: '3', label: '参与奖' },
|
||||
];
|
||||
const scoreColumns = [
|
||||
{ title: '评委', dataIndex: 'scorerName', width: 180 },
|
||||
{ title: '状态', key: 'status', width: 120 },
|
||||
{ title: '总分', dataIndex: 'totalScore', width: 120 },
|
||||
];
|
||||
const voteMemberColumns = [
|
||||
{ title: '评委', dataIndex: 'voterName', width: 180 },
|
||||
{ title: '状态', dataIndex: 'voteStatus', width: 120 },
|
||||
@@ -189,11 +213,15 @@
|
||||
}));
|
||||
const showProcessHandle = computed(() => props.formBpm && props.formData?.PROCESS_TAB_TYPE === 'run' && !!processInfo.value.taskId);
|
||||
const currentUserId = computed(() => String(userStore.getUserInfo?.id || ''));
|
||||
const currentScore = computed(() => scoreRows.value.find((item) => String(item.scorerId) === currentUserId.value));
|
||||
const scoreLocked = computed(() => currentScore.value?.scoreStatus === 'completed');
|
||||
const scoreStatusLabel = computed(() => {
|
||||
if (!currentScore.value) return '未保存';
|
||||
return scoreLocked.value ? '评分已锁定' : '已保存,待流程提交';
|
||||
});
|
||||
const currentVote = computed(() => voteRows.value.find((item) => String(item.voterId) === currentUserId.value));
|
||||
|
||||
watch(
|
||||
() => [processInfo.value.mainId, processInfo.value.processInstanceId],
|
||||
() => [processInfo.value.mainId, processInfo.value.processInstanceId, processInfo.value.taskId, processInfo.value.taskDefKey],
|
||||
() => loadProposal(),
|
||||
{ immediate: true }
|
||||
);
|
||||
@@ -202,19 +230,21 @@
|
||||
const mainId = processInfo.value.mainId;
|
||||
if (!mainId) {
|
||||
proposal.value = {};
|
||||
scoreRows.value = [];
|
||||
currentScore.value = null;
|
||||
voteRows.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const [main, scores, votes] = await Promise.all([
|
||||
const [main, votes, myScore] = await Promise.all([
|
||||
queryDataById(mainId),
|
||||
queryImprovementDeptScoreListByMainId(mainId),
|
||||
queryImprovementInstituteVoteListByMainId(mainId),
|
||||
isTask('Task_improve002') && processInfo.value.processInstanceId && processInfo.value.taskId
|
||||
? queryMyDeptScore(processInfo.value)
|
||||
: Promise.resolve(null),
|
||||
]);
|
||||
proposal.value = main || {};
|
||||
scoreRows.value = scores || [];
|
||||
currentScore.value = myScore || null;
|
||||
voteRows.value = votes || [];
|
||||
initialAward.value = proposal.value.initialAward || '';
|
||||
leaderFinalAward.value = proposal.value.leaderFinalAward || '';
|
||||
@@ -243,6 +273,10 @@
|
||||
}
|
||||
await saveInitialReview(actionPayload({ initialAward: initialAward.value }));
|
||||
}
|
||||
if (isTask('Task_improve002')) {
|
||||
// 弹窗仅保存草稿;通用流程办理前才锁定评分,确保评委在此之前仍可修改。
|
||||
await submitDeptScore(actionPayload());
|
||||
}
|
||||
if (isTask('Task_improve003')) {
|
||||
if (!leaderFinalAward.value) {
|
||||
createMessage.warning('请选择负责人确认奖项');
|
||||
@@ -282,10 +316,51 @@
|
||||
}
|
||||
|
||||
function openScoreModal() {
|
||||
// 评分字段、权重计算和完成条件将由下一阶段单独实现,避免当前占位入口误写评分结果。
|
||||
const score = currentScore.value;
|
||||
scoreForm.value = {
|
||||
effectScore: score?.effectScore ?? undefined,
|
||||
promotionScore: score?.promotionScore ?? undefined,
|
||||
originalityScore: score?.originalityScore ?? undefined,
|
||||
effortScore: score?.effortScore ?? undefined,
|
||||
scoreOpinion: score?.scoreOpinion || '',
|
||||
};
|
||||
scoreModalVisible.value = true;
|
||||
}
|
||||
|
||||
async function saveScoreDraft() {
|
||||
if (!hasValidScoreValues()) {
|
||||
createMessage.warning('请完整填写 0 到 100 分之间的四项评分');
|
||||
return;
|
||||
}
|
||||
savingScore.value = true;
|
||||
try {
|
||||
await saveDeptScore(actionPayload(scoreForm.value));
|
||||
createMessage.success('评分保存成功,可在流程提交前继续修改');
|
||||
await loadProposal();
|
||||
} catch (error: any) {
|
||||
createMessage.warning(error?.message || '评分保存失败');
|
||||
} finally {
|
||||
savingScore.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function createEmptyScoreForm() {
|
||||
return {
|
||||
effectScore: undefined,
|
||||
promotionScore: undefined,
|
||||
originalityScore: undefined,
|
||||
effortScore: undefined,
|
||||
scoreOpinion: '',
|
||||
};
|
||||
}
|
||||
|
||||
function hasValidScoreValues() {
|
||||
return ['effectScore', 'promotionScore', 'originalityScore', 'effortScore'].every((key) => {
|
||||
const value = scoreForm.value[key];
|
||||
return value !== undefined && value !== null && Number(value) >= 0 && Number(value) <= 100;
|
||||
});
|
||||
}
|
||||
|
||||
function awardLabel(value: string) {
|
||||
return awardOptions.find((item) => item.value === String(value))?.label || '-';
|
||||
}
|
||||
@@ -299,6 +374,10 @@
|
||||
return value ? String(value).slice(0, 10) : '-';
|
||||
}
|
||||
|
||||
function formatDateTime(value?: string) {
|
||||
return value ? String(value) : '-';
|
||||
}
|
||||
|
||||
function handleSuccess() {
|
||||
emit('success');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user