| | |
| | | <el-form :model="{ activities }" |
| | | ref="formRef" |
| | | label-position="top"> |
| | | <el-steps :active="getActiveStep()" |
| | | <el-steps :active="getActiveStep()" |
| | | finish-status="success" |
| | | process-status="process" |
| | | align-center |
| | | direction="vertical"> |
| | | <el-step v-for="(activity, index) in activities" |
| | | :key="index" |
| | | <el-step v-for="(group, gIndex) in stepGroups" |
| | | :key="gIndex" |
| | | finish-status="success" |
| | | :title="getNodeTitle(index, activities.length)" |
| | | :description="activity.approveNodeUser" |
| | | :icon="getNodeIcon(activity, index)"> |
| | | :title="getNodeTitle(gIndex, stepGroups.length)" |
| | | :icon="getNodeIcon(group)"> |
| | | <template #icon> |
| | | <el-icon v-if="activity.approveNodeStatus === 2" |
| | | <el-icon v-if="group.some(a => a.approveNodeStatus === 2)" |
| | | color="red" |
| | | :size="22"> |
| | | <WarningFilled /> |
| | | </el-icon> |
| | | <el-icon v-else-if="activity.isShen" |
| | | <el-icon v-else-if="group.some(a => a.isShen)" |
| | | color="#1890ff" |
| | | :size="22"> |
| | | <Edit /> |
| | | </el-icon> |
| | | <el-icon v-else-if="activity.approveNodeStatus === 1" |
| | | <el-icon v-else-if="group.every(a => a.approveNodeStatus === 1)" |
| | | color="#67C23A" |
| | | :size="26"> |
| | | <Check /> |
| | |
| | | </el-icon> |
| | | </template> |
| | | <template #title> |
| | | <span style="color: #000000">{{ getNodeTitle(index, activities.length) }}</span> |
| | | <span style="color: #000000">{{ getNodeTitle(gIndex, stepGroups.length) }}</span> |
| | | <el-tag :type="group[0]?.approveMode === 'and' ? 'warning' : 'info'" size="small" style="margin-left: 6px">{{ group[0]?.approveMode === 'and' ? '会签' : '或签' }}</el-tag> |
| | | </template> |
| | | <template #description> |
| | | <div class="node-user"> |
| | | <div class="avatar-wrapper"> |
| | | <img :src="userStore.avatar" |
| | | class="user-avatar" |
| | | alt="" /> |
| | | <div v-for="(activity, aIndex) in group" |
| | | :key="activity.id || aIndex" |
| | | class="node-user-item"> |
| | | <div class="node-user"> |
| | | <div class="avatar-wrapper"> |
| | | <img :src="userStore.avatar" |
| | | class="user-avatar" |
| | | alt="" /> |
| | | </div> |
| | | <span style="color: #000000">{{ activity.approveNodeUser }}({{ activity.isApproval }})</span> |
| | | </div> |
| | | <span style="color: #000000">{{ activity.approveNodeUser }}-{{activity.isApproval}}</span> |
| | | </div> |
| | | <div v-if="!activity.isShen" |
| | | class="node-reason"> |
| | | <span>审批意见:</span>{{ activity.approveNodeReason }} |
| | | </div> |
| | | <div v-if="!activity.isShen" |
| | | class="node-reason"> |
| | | <span>签名:</span> |
| | | <img :src="activity.urlTem" |
| | | class="signImg" |
| | | alt="" |
| | | v-if="activity.urlTem" /> |
| | | </div> |
| | | <div v-else-if="activity.isShen"> |
| | | <el-form-item :prop="'activities.' + index + '.approveNodeReason'" |
| | | :rules="[{ required: true, message: '审批意见不能为空', trigger: 'blur' }]"> |
| | | <el-input v-model="activity.approveNodeReason" |
| | | clearable |
| | | type="textarea" |
| | | :disabled="operationType === 'view'"></el-input> |
| | | </el-form-item> |
| | | <div v-if="!activity.isShen" |
| | | class="node-reason"> |
| | | <span>审批意见:</span>{{ activity.approveNodeReason }} |
| | | </div> |
| | | <div v-if="!activity.isShen" |
| | | class="node-reason"> |
| | | <span>签名:</span> |
| | | <img :src="activity.urlTem" |
| | | class="signImg" |
| | | alt="" |
| | | v-if="activity.urlTem" /> |
| | | </div> |
| | | <div v-else-if="activity.isShen"> |
| | | <el-form-item :prop="'activities.' + getActivityIndex(activity) + '.approveNodeReason'" |
| | | :rules="[{ required: true, message: '审批意见不能为空', trigger: 'blur' }]"> |
| | | <el-input v-model="activity.approveNodeReason" |
| | | clearable |
| | | type="textarea" |
| | | :disabled="operationType === 'view'"></el-input> |
| | | </el-form-item> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-step> |
| | |
| | | return "审批"; |
| | | }; |
| | | |
| | | // 获取当前激活步骤 |
| | | // 获取当前激活步骤(分组后:含待审人的组下标,无则返回组总数=全部完成) |
| | | const getActiveStep = () => { |
| | | // 如果所有 isShen 都为 false,返回最后一个步骤(全部完成) |
| | | const hasActive = activities.value.some(a => a.isShen === true); |
| | | if (!hasActive) return activities.value.length; |
| | | // 当前节点索引 |
| | | return activities.value.findIndex(a => a.isShen == true); |
| | | const idx = stepGroups.value.findIndex(group => group.some(a => a.isShen === true)); |
| | | if (idx === -1) return stepGroups.value.length; |
| | | return idx; |
| | | }; |
| | | // 步骤icon |
| | | const getNodeIcon = (activity, index) => { |
| | | if (activity.approveNodeStatus === 2) return "el-icon-warning"; // 不通过 |
| | | if (activity.isShen) return "Edit"; |
| | | // 步骤icon(组级:任一人驳回/任一人待审) |
| | | const getNodeIcon = group => { |
| | | if (group.some(a => a.approveNodeStatus === 2)) return "el-icon-warning"; // 不通过 |
| | | if (group.some(a => a.isShen)) return "Edit"; |
| | | return ""; |
| | | }; |
| | | // 审批节点分组(同一 approveNodeOrder = 同一节点的多名审批人,或签/会签) |
| | | const stepGroups = computed(() => { |
| | | const map = new Map(); |
| | | (activities.value || []).forEach(a => { |
| | | const order = a.approveNodeOrder; |
| | | if (!map.has(order)) map.set(order, []); |
| | | map.get(order).push(a); |
| | | }); |
| | | return Array.from(map.values()); |
| | | }); |
| | | // 获取节点成员在 activities 中的索引(用于表单校验 prop) |
| | | const getActivityIndex = activity => { |
| | | return activities.value.findIndex(a => a.id === activity.id); |
| | | }; |
| | | |
| | | // 打开弹框 |
| | |
| | | return; |
| | | } |
| | | currentActivity.approveNodeStatus = status; |
| | | // 判断是否为最后一步 |
| | | const isLast = |
| | | activities.value.findIndex(a => a.isShen) === activities.value.length - 1; |
| | | updateApproveNode({ ...currentActivity, isLast }).then(() => { |
| | | // 流转判断由后端完成,前端不再传 isLast |
| | | updateApproveNode({ ...currentActivity }).then(() => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | }); |
| | |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | .node-user-item { |
| | | margin: 8px 0; |
| | | } |
| | | .node-status { |
| | | color: #1890ff; |
| | | margin-left: 8px; |