Merge branch 'refs/heads/master' into wsq-20260727

# Conflicts:
#	src/views/gh/improvementProposal/components/ImprovementProposalForm.vue
This commit is contained in:
王淑勤
2026-07-29 10:54:07 +08:00
8 changed files with 349 additions and 120 deletions
@@ -17,7 +17,13 @@ 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',
bpmInstituteVoteCandidates = '/improvementproposal/improvementProposal/bpm/instituteVoteCandidates',
bpmInstituteVoteMembers = '/improvementproposal/improvementProposal/bpm/instituteVoteMembers',
bpmSubmitInstituteVote = '/improvementproposal/improvementProposal/bpm/submitInstituteVote',
bpmArchiveAward = '/improvementproposal/improvementProposal/bpm/archiveAward',
bpmInstituteVote = '/improvementproposal/improvementProposal/bpm/instituteVote',
}
@@ -102,6 +108,12 @@ 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 queryInstituteVoteCandidates = (params) => defHttp.get({ url: Api.bpmInstituteVoteCandidates, params });
export const saveInstituteVoteMembers = (params) => defHttp.post({ url: Api.bpmInstituteVoteMembers, params });
export const saveArchiveAward = (params) => defHttp.post({ url: Api.bpmArchiveAward, params });
export const castInstituteVote = (params) => defHttp.post({ url: Api.bpmInstituteVote, params });
export const submitInstituteVote = (params) => defHttp.post({ url: Api.bpmSubmitInstituteVote, params });
@@ -131,6 +131,38 @@ export const improvementDeptScoreColumns: JVxeColumn[] = [
{ required: true, message: '${title}不能为空' },
],
},
{
title: '安全得分',
key: 'safetyScore',
type: JVxeTypes.inputNumber,
width:"200px",
placeholder: '请输入${title}',
defaultValue:'',
},
{
title: '质量得分',
key: 'qualityScore',
type: JVxeTypes.inputNumber,
width:"200px",
placeholder: '请输入${title}',
defaultValue:'',
},
{
title: '效率得分',
key: 'efficiencyScore',
type: JVxeTypes.inputNumber,
width:"200px",
placeholder: '请输入${title}',
defaultValue:'',
},
{
title: '成本得分',
key: 'costScore',
type: JVxeTypes.inputNumber,
width:"200px",
placeholder: '请输入${title}',
defaultValue:'',
},
{
title: '改善效果得分',
key: 'effectScore',
@@ -292,13 +324,17 @@ export const superQuerySchema = {
scorerId: {title: '评委ID',order: 2,view: 'text', type: 'string',},
scorerName: {title: '评委姓名',order: 3,view: 'text', type: 'string',},
scoreStatus: {title: '评分状态:pending/completed',order: 4,view: 'text', type: 'string',},
effectScore: {title: '改善效果得分',order: 5,view: 'number', type: 'number',},
promotionScore: {title: '可推广度得分',order: 6,view: 'number', type: 'number',},
originalityScore: {title: '独创性得分',order: 7,view: 'number', type: 'number',},
effortScore: {title: '努力度得分',order: 8,view: 'number', type: 'number',},
totalScore: {title: '加权总分',order: 9,view: 'number', type: 'number',},
scoreOpinion: {title: '评分意见',order: 10,view: 'textarea', type: 'string',},
scoreTime: {title: '评分时间',order: 11,view: 'datetime', type: 'string',},
safetyScore: {title: '安全得分',order: 5,view: 'number', type: 'number',},
qualityScore: {title: '质量得分',order: 6,view: 'number', type: 'number',},
efficiencyScore: {title: '效率得分',order: 7,view: 'number', type: 'number',},
costScore: {title: '成本得分',order: 8,view: 'number', type: 'number',},
effectScore: {title: '改善效果平均得分',order: 9,view: 'number', type: 'number',},
promotionScore: {title: '可推广度得分',order: 10,view: 'number', type: 'number',},
originalityScore: {title: '独创性得分',order: 11,view: 'number', type: 'number',},
effortScore: {title: '努力度得分',order: 12,view: 'number', type: 'number',},
totalScore: {title: '加权总分',order: 13,view: 'number', type: 'number',},
scoreOpinion: {title: '评分意见',order: 14,view: 'textarea', type: 'string',},
scoreTime: {title: '评分时间',order: 15,view: 'datetime', type: 'string',},
}
},
improvementInstituteVote: {
@@ -27,10 +27,6 @@
<div class="attachment-label">改善后附件</div>
<SUploadFile :bussiness-id="`${proposal.id}:after`" :disabled="true" :multiple="true" />
</div>
<div>
<div class="attachment-label">其他附件</div>
<SUploadFile :bussiness-id="`${proposal.id}:other`" :disabled="true" :multiple="true" />
</div>
</div>
<span v-else>-</span>
</a-descriptions-item>
@@ -44,50 +40,56 @@
<a-form class="process-form" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item v-if="isTask('Task_improve001')" label="初步评定奖项" required>
<a-radio-group v-model:value="initialAward">
<a-radio v-for="item in awardOptions" :key="item.value" :value="item.value">{{ item.label }}</a-radio>
</a-radio-group>
<JDictSelectTag v-model:value="initialAward" dictCode="proposal_prize" type="radio" />
<div class="form-help">金奖银奖进入部门评议小组打分铜奖参与奖直接进入部门负责人审核</div>
</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="1">
<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>
<template v-if="isTask('Task_improve003')">
<a-form-item label="初步评定奖项">{{ awardLabel(proposal.initialAward) }}</a-form-item>
<a-form-item v-if="proposal.needDeptScore === '1'" label="部门评议结果">
平均分{{ proposal.deptAvgScore ?? '-' }}系统建议奖项{{ awardLabel(proposal.suggestedAward) }}
<a-form-item v-if="proposal.needDeptScore === '1'" label="部门评议小组打分结果">
平均分{{ proposal.deptAvgScore ?? '-' }}建议奖项{{ awardLabel(proposal.suggestedAward) }}
</a-form-item>
<a-form-item label="负责人确认奖项" required>
<a-radio-group v-model:value="leaderFinalAward">
<a-radio v-for="item in awardOptions" :key="item.value" :value="item.value">{{ item.label }}</a-radio>
<a-radio-group v-if="leaderAwardBase" v-model:value="leaderFinalAward">
<a-radio v-for="item in leaderAwardOptions" :key="item.value" :value="item.value">{{ item.label }}</a-radio>
</a-radio-group>
<div v-if="leaderAwardBase" class="form-help">评定基准{{ awardLabel(leaderAwardBase) }}仅可选择当前档位或降档奖项</div>
<div v-else class="form-help">部门评议建议奖项尚未生成暂不能确认负责人奖项</div>
</a-form-item>
</template>
<template v-if="isTask('Task_improve004')">
<a-form-item label="所级评议人员">
<a-table row-key="id" size="small" bordered :columns="voteMemberColumns" :data-source="voteRows" :pagination="false" />
<div class="form-help">所级评议人员选择和投票记录初始化将在后续功能中接入</div>
<a-form-item label="负责人确认奖项">{{ awardLabel(proposal.leaderFinalAward) }}</a-form-item>
<a-form-item label="所级评议人员" required>
<a-checkbox-group v-model:value="selectedInstituteVoterIds" class="vote-member-grid">
<a-checkbox v-for="member in instituteVoteCandidates" :key="member.id" :value="member.id" class="vote-member-option">
<span class="vote-member-name">{{ member.realname || member.username }}</span>
<span class="vote-member-username">{{ member.username }}</span>
</a-checkbox>
</a-checkbox-group>
<div v-if="!instituteVoteCandidates.length" class="form-help">未查询到所级评议小组人员</div>
<div v-else class="form-help">请选择本次所级评议投票人员提交办理后将生成对应投票待办</div>
</a-form-item>
</template>
<template v-if="isTask('Task_improve005')">
<a-form-item label="我的投票">
<a-space v-if="currentVote">
<a-button type="primary" :disabled="currentVote.voteStatus === 'voted'" :loading="savingVote" @click="castVote('agree')">同意</a-button>
<a-button danger :disabled="currentVote.voteStatus === 'voted'" :loading="savingVote" @click="castVote('disagree')">不同意</a-button>
<a-button :type="currentVote.voteResult === 'agree' ? 'primary' : 'default'" :disabled="voteLocked" :loading="savingVote" @click="saveVoteDraft('agree')">投票通过</a-button>
<a-button danger :type="currentVote.voteResult === 'disagree' ? 'primary' : 'default'" :disabled="voteLocked" :loading="savingVote" @click="saveVoteDraft('disagree')">投票反对</a-button>
<span>{{ voteResultLabel(currentVote) }}</span>
</a-space>
<span v-else class="form-help">当前用户不在所级评议投票名单中</span>
@@ -116,12 +118,53 @@
/>
</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-modal v-model:open="scoreModalVisible" title="部门评议打分" :footer="null" :mask-closable="false" :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.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,16 +174,22 @@
import { computed, ref, watch } from 'vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { useUserStore } from '/@/store/modules/user';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
import {
castInstituteVote,
queryDataById,
queryImprovementDeptScoreListByMainId,
queryInstituteVoteCandidates,
queryImprovementInstituteVoteListByMainId,
queryMyDeptScore,
saveArchiveAward,
saveDeptScore,
saveInitialReview,
saveInstituteVoteMembers,
saveLeaderAward,
submitInstituteVote,
submitDeptScore,
} from '../ImprovementProposal.api';
const props = defineProps({
@@ -152,14 +201,18 @@
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 instituteVoteCandidates = ref<Recordable[]>([]);
const selectedInstituteVoterIds = ref<string[]>([]);
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 } };
@@ -169,16 +222,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 },
];
// 流程引擎在运行待办中提供 taskDefKey,节点功能只依赖该稳定标识而非表单 URL 参数。
const processInfo = computed(() => ({
mainId: props.formData?.processDataId || props.formData?.dataId,
@@ -194,11 +237,30 @@
}));
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 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);
});
// 部门评分完成后必须以系统建议奖项为基准,不能回退到初审奖项。
const leaderAwardBase = computed(() => {
const award = proposal.value.needDeptScore === '1' ? proposal.value.suggestedAward : proposal.value.initialAward;
return awardOptions.some((item) => item.value === String(award)) ? String(award) : '';
});
const leaderAwardOptions = computed(() => {
if (!leaderAwardBase.value) return [];
return awardOptions.filter((item) => Number(item.value) >= Number(leaderAwardBase.value));
});
const currentVote = computed(() => voteRows.value.find((item) => String(item.voterId) === currentUserId.value));
const voteLocked = computed(() => currentVote.value?.voteStatus === 'voted');
watch(
() => [processInfo.value.mainId, processInfo.value.processInstanceId],
() => [processInfo.value.mainId, processInfo.value.processInstanceId, processInfo.value.taskId, processInfo.value.taskDefKey],
() => loadProposal(),
{ immediate: true }
);
@@ -207,23 +269,38 @@
const mainId = processInfo.value.mainId;
if (!mainId) {
proposal.value = {};
scoreRows.value = [];
currentScore.value = null;
voteRows.value = [];
instituteVoteCandidates.value = [];
selectedInstituteVoterIds.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 || '';
finalAward.value = proposal.value.finalAward || '';
if (isTask('Task_improve004') && processInfo.value.processInstanceId && processInfo.value.taskId) {
instituteVoteCandidates.value = await queryInstituteVoteCandidates(processInfo.value);
const candidateIds = new Set(instituteVoteCandidates.value.map((item) => String(item.id)));
// 流程提交失败后重新打开 Task4 时,回显本次尚未流转的有效名单。
selectedInstituteVoterIds.value = voteRows.value
.map((item) => String(item.voterId))
.filter((voterId) => candidateIds.has(voterId));
} else {
instituteVoteCandidates.value = [];
selectedInstituteVoterIds.value = [];
}
} catch (error) {
createMessage.warning('提案数据加载失败');
} finally {
@@ -248,16 +325,38 @@
}
await saveInitialReview(actionPayload({ initialAward: initialAward.value }));
}
if (isTask('Task_improve002')) {
// 弹窗仅保存草稿;通用流程办理前才锁定评分,确保评委在此之前仍可修改。
await submitDeptScore(actionPayload());
}
if (isTask('Task_improve003')) {
if (!leaderAwardBase.value) {
createMessage.warning('部门评议建议奖项尚未生成,不能确认负责人奖项');
return false;
}
if (!leaderFinalAward.value) {
createMessage.warning('请选择负责人确认奖项');
return false;
}
if (!leaderAwardOptions.value.some((item) => item.value === leaderFinalAward.value)) {
createMessage.warning('负责人确认奖项只能选择当前档位或降档奖项');
return false;
}
await saveLeaderAward(actionPayload({ leaderFinalAward: leaderFinalAward.value }));
}
if (isTask('Task_improve005') && currentVote.value?.voteStatus !== 'voted') {
createMessage.warning('请先完成投票');
return false;
if (isTask('Task_improve004')) {
if (!selectedInstituteVoterIds.value.length) {
createMessage.warning('请至少选择一名所级评议人员');
return false;
}
await saveInstituteVoteMembers(actionPayload({ voterIds: selectedInstituteVoterIds.value }));
}
if (isTask('Task_improve005')) {
if (!currentVote.value?.voteResult) {
createMessage.warning('请先选择投票通过或投票反对');
return false;
}
await submitInstituteVote(actionPayload());
}
if (isTask('Task_improve006')) {
if (!finalAward.value) {
@@ -273,11 +372,11 @@
}
}
async function castVote(voteResult: string) {
async function saveVoteDraft(voteResult: string) {
savingVote.value = true;
try {
await castInstituteVote(actionPayload({ voteResult }));
createMessage.success('投票成功');
createMessage.success('投票已保存,提交办理前可修改');
await loadProposal();
} catch (error: any) {
createMessage.warning(error?.message || '投票失败');
@@ -287,16 +386,68 @@
}
function openScoreModal() {
// 评分字段、权重计算和完成条件将由下一阶段单独实现,避免当前占位入口误写评分结果。
const score = currentScore.value;
scoreForm.value = {
safetyScore: score?.safetyScore ?? undefined,
qualityScore: score?.qualityScore ?? undefined,
efficiencyScore: score?.efficiencyScore ?? undefined,
costScore: score?.costScore ?? 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('评分保存成功,可在流程提交前继续修改');
scoreModalVisible.value = false;
await loadProposal();
} catch (error: any) {
createMessage.warning(error?.message || '评分保存失败');
} finally {
savingScore.value = false;
}
}
function createEmptyScoreForm() {
return {
safetyScore: undefined,
qualityScore: undefined,
efficiencyScore: undefined,
costScore: undefined,
promotionScore: undefined,
originalityScore: undefined,
effortScore: undefined,
scoreOpinion: '',
};
}
function hasValidScoreValues() {
return ['safetyScore', 'qualityScore', 'efficiencyScore', 'costScore', '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 || '-';
}
function voteResultLabel(vote: Recordable) {
if (vote.voteStatus !== 'voted') return '待投票';
if (vote.voteStatus !== 'voted') {
if (vote.voteResult === 'agree') return '已选择投票通过,待提交办理';
if (vote.voteResult === 'disagree') return '已选择投票反对,待提交办理';
return '待投票';
}
return vote.voteResult === 'agree' ? '已投同意票' : '已投不同意票';
}
@@ -304,6 +455,10 @@
return value ? String(value).slice(0, 10) : '-';
}
function formatDateTime(value?: string) {
return value ? String(value) : '-';
}
function handleSuccess() {
emit('success');
}
@@ -333,7 +488,7 @@
.attachment-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
@@ -353,6 +508,40 @@
font-size: 12px;
}
.score-range-alert {
margin-bottom: 16px;
}
.vote-member-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 10px 12px;
width: 100%;
}
.vote-member-option {
display: flex;
align-items: center;
min-height: 42px;
margin-inline-start: 0;
padding: 8px 10px;
border: 1px solid #d9e2ec;
border-radius: 4px;
background: #fff;
}
.vote-member-name {
margin-left: 6px;
color: #1f2937;
font-weight: 500;
}
.vote-member-username {
margin-left: 6px;
color: #64748b;
font-size: 12px;
}
@media (max-width: 768px) {
.attachment-grid {
grid-template-columns: 1fr;
@@ -34,8 +34,8 @@
<a-input v-model:value="formData.applicantName" placeholder="请输入申请人姓名" allow-clear ></a-input>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="团队名称" v-bind="validateInfos.teamName" id="ImprovementProposalForm-teamName" name="teamName" :labelCol="longLabelCol" :wrapperCol="longWrapperCol">
<a-col :span="12">
<a-form-item label="团队名称" v-bind="validateInfos.teamName" id="ImprovementProposalForm-teamName" name="teamName">
<a-input v-model:value="formData.teamName" placeholder="请输入团队名称" allow-clear ></a-input>
</a-form-item>
</a-col>
@@ -50,24 +50,24 @@
<a-textarea v-model:value="formData.improvementMethod" :rows="4" placeholder="请输入改善方法及措施" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="改善前情况描述" v-bind="validateInfos.beforeDesc" id="ImprovementProposalForm-beforeDesc" name="beforeDesc" :labelCol="longLabelCol" :wrapperCol="longWrapperCol">
<a-col :span="12">
<a-form-item label="改善前情况描述" v-bind="validateInfos.beforeDesc" id="ImprovementProposalForm-beforeDesc" name="beforeDesc" :labelCol="halfLabelCol" :wrapperCol="halfWrapperCol">
<a-textarea v-model:value="formData.beforeDesc" :rows="4" placeholder="请输入改善前情况描述" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="改善前图片" :labelCol="longLabelCol" :wrapperCol="longWrapperCol">
<SUploadFile ref="beforeFileRef" :bussiness-id="formData.proposalNo ? `${formData.proposalNo}:before` : ''" :multiple="true" :disabled="disabled" />
<a-col :span="12">
<a-form-item label="改善前图片" :labelCol="halfLabelCol" :wrapperCol="halfWrapperCol">
<SUploadFile ref="beforeFileRef" :bussiness-id="formData.id ? `${formData.id}:before` : ''" :multiple="true" :disabled="disabled" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="改善后情况描述" v-bind="validateInfos.afterDesc" id="ImprovementProposalForm-afterDesc" name="afterDesc" :labelCol="longLabelCol" :wrapperCol="longWrapperCol">
<a-col :span="12">
<a-form-item label="改善后情况描述" v-bind="validateInfos.afterDesc" id="ImprovementProposalForm-afterDesc" name="afterDesc" :labelCol="halfLabelCol" :wrapperCol="halfWrapperCol">
<a-textarea v-model:value="formData.afterDesc" :rows="4" placeholder="请输入改善后情况描述" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="改善后图片" :labelCol="longLabelCol" :wrapperCol="longWrapperCol">
<SUploadFile ref="afterFileRef" :bussiness-id="formData.proposalNo ? `${formData.proposalNo}:after` : ''" :multiple="true" :disabled="disabled" />
<a-col :span="12">
<a-form-item label="改善后图片" :labelCol="halfLabelCol" :wrapperCol="halfWrapperCol">
<SUploadFile ref="afterFileRef" :bussiness-id="formData.id ? `${formData.id}:after` : ''" :multiple="true" :disabled="disabled" />
</a-form-item>
</a-col>
<a-col :span="24">
@@ -204,19 +204,19 @@
};
const longLabelCol = {
xs: {span: 24},
sm: {span: 5},
sm: {span: 4},
};
const longWrapperCol = {
xs: {span: 24},
sm: {span: 16},
sm: {span: 20},
};
const halfLabelCol = {
xs: {span: 24},
sm: {span: 5},
sm: {span: 8},
};
const halfWrapperCol = {
xs: {span: 24},
sm: {span: 18},
sm: {span: 16},
};
const disabled = computed(()=>{
@@ -230,8 +230,8 @@
return props.formDisabled;
});
// 普通新增没有业务 ID,不展示评分投票子表;已有数据及流程表单正常展示
const showSubTables = computed(() => Boolean(formData.id || props.formData?.dataId));
// 子表由流程节点维护,普通新增、编辑和详情弹窗均不展示,避免误改评分投票数据
const showSubTables = computed(() => props.formBpm === true && Boolean(formData.id || props.formData?.dataId));
function getCurrentDate() {
const now = new Date();
@@ -330,12 +330,6 @@
improvementDeptScoreTable.dataSource = [...improvementDeptScoreDataList];
const improvementInstituteVoteDataList = await queryImprovementInstituteVoteListByMainId(row['id']);
improvementInstituteVoteTable.dataSource = [...improvementInstituteVoteDataList];
// 加载改善前/后图片文件列表
await nextTick();
if (formData.id) {
beforeFileRef.value?.loadFileList();
afterFileRef.value?.loadFileList();
}
}
}
@@ -360,11 +354,9 @@
async function edit(row) {
//主表数据
await queryMainData(row.id);
//子表数据
const improvementDeptScoreDataList = await queryImprovementDeptScoreListByMainId(row['id']);
improvementDeptScoreTable.dataSource = [...improvementDeptScoreDataList];
const improvementInstituteVoteDataList = await queryImprovementInstituteVoteListByMainId(row['id']);
improvementInstituteVoteTable.dataSource = [...improvementInstituteVoteDataList];
// 普通编辑不加载子表,并清理弹窗复用时可能残留的表格数据
improvementDeptScoreTable.dataSource = [];
improvementInstituteVoteTable.dataSource = [];
// 加载改善前/后图片文件列表
await nextTick();
if (formData.id) {
@@ -411,8 +403,8 @@
const mainData = await getFormData();
const isUpdate = Boolean(mainData.id);
const values = Object.assign({}, dbData, mainData);
// 新增提案只保存主表;已有提案编辑或流程提交时才采集子表数据
if (isUpdate) {
// 仅流程表单提交子表,普通编辑不能覆盖后续流程维护的评分和投票记录
if (isUpdate && showSubTables.value) {
const subData = await getSubFormAndTableData();
Object.assign(values, subData);
}