feat: 合规员(manager) 自建会议可自传自审, 现场照片支持直接上传
- 后端 submitMaterial: manager 跳过执行方/已执行/已冻结三道门槛 - 前端提交按钮门槛同步放宽 (manager 不受已执行/已冻结限制) - 现场照片(签到表/前全景/后全景) 新增直接上传入口, 与扫码拍照并存 - 劳务材料 tab 增加"劳务明细表"小标题 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -63,8 +63,9 @@
|
||||
<div class="section-title">材料管理</div>
|
||||
<el-tabs v-model="activeTab" class="material-tabs">
|
||||
<el-tab-pane label="劳务材料" name="labor">
|
||||
<!-- 参会人管理 (放在 劳务明细 + 劳务协议 上面, 无表题直接出表) -->
|
||||
<!-- 参会人管理 (放在 劳务明细 + 劳务协议 上面) -->
|
||||
<div class="attendee-section">
|
||||
<div class="attendee-subtitle">劳务明细表</div>
|
||||
<div v-if="!isSponsor && !isReadonly" class="attendee-toolbar">
|
||||
<el-button type="primary" size="small" :loading="attendeeLoading" :disabled="!attendeeEditable" @click="openAttendeeDialog()">+ 新增参会人</el-button>
|
||||
<el-button size="small" :disabled="!attendeeEditable" @click="openAttendeeImport">批量导入</el-button>
|
||||
@@ -195,7 +196,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<CameraQrUpload v-if="isCameraSubType(r.subType)" v-model="r.url" :meeting-id="meetingId" :sub-type="r.subType" :label="r.label" :readonly="isSponsor || isReadonly || !laborEditable || laborLocked" class="file-uploader" />
|
||||
<div v-if="isCameraSubType(r.subType)" class="camera-upload-row">
|
||||
<CameraQrUpload v-model="r.url" :meeting-id="meetingId" :sub-type="r.subType" :label="r.label" :readonly="isSponsor || isReadonly || !laborEditable || laborLocked" class="file-uploader" />
|
||||
<OssFileUploader v-model="r.url" v-model:name="r.fileName" :dir="`ry8080/meeting/${meetingId}/labor/`" accept=".png,.jpg,.jpeg,.gif,.webp,.bmp" placeholder="直接上传(本地图片)" :readonly="isSponsor || isReadonly || !laborEditable || laborLocked" class="file-uploader" />
|
||||
</div>
|
||||
<template v-else-if="r.subType === 'L_ESIGN_IN'">
|
||||
<div class="esign-actions">
|
||||
<OssFileUploader v-model="r.url" :dir="`ry8080/meeting/${meetingId}/labor/`" :accept="r.accept" multiple block :max-size="20" placeholder="点击上传多个 Excel (xls/xlsx/csv)" :readonly="isSponsor || isReadonly || !laborEditable || laborLocked" />
|
||||
@@ -898,10 +902,10 @@ const frozen = computed(() => isOne(row.value.isFrozen))
|
||||
/** 单轨是否处于「可提交/可编辑」态: 执行方仅未提交/被驳回可编辑; 合规(manager)/管理员 永远可编辑 */
|
||||
const laborEditable = computed(() => isManager.value || isAdmin.value || ['NOT_SUBMITTED', 'REJECTED'].includes(row.value.laborAuditStage))
|
||||
const serviceEditable = computed(() => isManager.value || isAdmin.value || ['NOT_SUBMITTED', 'REJECTED'].includes(row.value.serviceAuditStage))
|
||||
// 提交审核后该轨锁定 (manager/admin 也跟着锁), 让 OssFileUploader 移除按钮消失
|
||||
// 提交审核后该轨锁定执行方 (executor), 让 OssFileUploader 移除按钮消失; 合规/管理员 (manager/admin) 任何时候都可编辑保存, 故不锁
|
||||
// (仅控制 OssFileUploader 的 readonly; 不影响 laborEditable (下载模板/参会人列表的判定))
|
||||
const laborLocked = computed(() => ['SUBMITTED', 'APPROVED'].includes(row.value.laborAuditStage))
|
||||
const serviceLocked = computed(() => ['SUBMITTED', 'APPROVED'].includes(row.value.serviceAuditStage))
|
||||
const laborLocked = computed(() => !isManager.value && !isAdmin.value && ['SUBMITTED', 'APPROVED'].includes(row.value.laborAuditStage))
|
||||
const serviceLocked = computed(() => !isManager.value && !isAdmin.value && ['SUBMITTED', 'APPROVED'].includes(row.value.serviceAuditStage))
|
||||
// 参会人名单是否可改 (新增/导入/编辑/删除): sponsor/只读不可; executor 劳务提交后(SUBMITTED/APPROVED)锁定, 未提交/退回可改; manager/admin 恒可改
|
||||
const attendeeEditable = computed(() => {
|
||||
if (isSponsor.value || isReadonly.value) return false
|
||||
@@ -911,11 +915,17 @@ const attendeeEditable = computed(() => {
|
||||
/** 单轨状态判定: C0=已提交待合规审 / C1=已提交待支持方审 */
|
||||
function isC0(stage, compliance) { return stage === 'SUBMITTED' && !isOne(compliance) }
|
||||
function isC1(stage, compliance) { return stage === 'SUBMITTED' && isOne(compliance) }
|
||||
// 材料可提交: 已执行 + 未冻结 + 对应轨可编辑 (三按钮按轨显隐)
|
||||
const executorSubmitReady = computed(() => isExecutor.value && executed.value && !frozen.value)
|
||||
const canSubmitLabor = computed(() => executorSubmitReady.value && laborEditable.value)
|
||||
const canSubmitService = computed(() => executorSubmitReady.value && serviceEditable.value)
|
||||
const canSubmitAll = computed(() => executorSubmitReady.value && laborEditable.value && serviceEditable.value)
|
||||
// 材料可提交: 执行方需"已执行"+未冻结; 合规员(manager 自建会议自传自审)不受"已执行"与"已冻结"两道时间门槛限制
|
||||
const executorSubmitReady = computed(() => {
|
||||
if (isManager.value) return true
|
||||
return isExecutor.value && executed.value && !frozen.value
|
||||
})
|
||||
// 单轨"可提交"门槛 (区别于 laborEditable 的"可编辑"): 仅未提交/被驳回可提交, 提交后按钮隐藏
|
||||
const laborSubmittable = computed(() => ['NOT_SUBMITTED', 'REJECTED'].includes(row.value.laborAuditStage))
|
||||
const serviceSubmittable = computed(() => ['NOT_SUBMITTED', 'REJECTED'].includes(row.value.serviceAuditStage))
|
||||
const canSubmitLabor = computed(() => executorSubmitReady.value && laborSubmittable.value)
|
||||
const canSubmitService = computed(() => executorSubmitReady.value && serviceSubmittable.value)
|
||||
const canSubmitAll = computed(() => executorSubmitReady.value && laborSubmittable.value && serviceSubmittable.value)
|
||||
// 材料可编辑 (保存修改): 任一轨未提交/被驳回 时可保存; 两轨都进入审核后锁定
|
||||
const materialsEditable = computed(() => laborEditable.value || serviceEditable.value)
|
||||
// 合规审核 (合规方/manager): 三按钮按轨 C0 显隐
|
||||
@@ -2590,6 +2600,10 @@ onBeforeUnmount(() => {
|
||||
.agreement-actions { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
||||
/* 电子签到表 多文件上传 + zip 按钮 纵向排列 */
|
||||
.esign-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; min-width: 0; }
|
||||
/* 现场照片 (签到表/前全景/后全景): 扫码拍照 + 直接上传 左右并存 */
|
||||
.camera-upload-row { display: flex; align-items: flex-start; gap: 8px; min-width: 0; }
|
||||
.camera-upload-row :deep(.camera-qr-upload) { width: auto; flex: 0 0 auto; }
|
||||
.camera-upload-row :deep(.ht-file-upload) { flex: 1 1 0; min-width: 0; }
|
||||
.upload-progress { grid-column: 2; display: flex; align-items: center; gap: 8px; max-width: 400px; }
|
||||
.upload-progress .el-progress { flex: 1; }
|
||||
.upload-progress-status { font-size: 12px; color: #8c8c8c; white-space: nowrap; }
|
||||
@@ -2638,6 +2652,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
/* 参会人管理 (放在 劳务材料 tab 顶部, 在 劳务明细 + 劳务协议 上面) */
|
||||
.attendee-section { padding-bottom: 16px; border-bottom: 1px dashed #e8e8e8; }
|
||||
.attendee-subtitle { font-size: 14px; font-weight: 600; color: #1a1a1a; margin-bottom: 12px; }
|
||||
.attendee-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; flex-wrap: wrap; }
|
||||
.form-hint { font-size: 12px; color: #909399; margin-top: -8px; margin-bottom: 8px; line-height: 1.5; grid-column: 1 / -1; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user