| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog v-model="visible" title="è¿åº¦æ±æ¥" width="900px" top="8vh" append-to-body destroy-on-close @close="handleClose"> |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-position="top" label-width="120px"> |
| | | <el-form-item label="项ç®é¶æ®µ" prop="planNodeId"> |
| | | <el-select v-model="form.planNodeId" placeholder="è¯·éæ©" clearable style="width: 100%"> |
| | | <el-option v-for="opt in stageOptions" :key="opt.value" :label="opt.label" :value="opt.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="计åå¼å§æ¶é´" prop="planStartTime"> |
| | | <el-date-picker |
| | | v-model="form.planStartTime" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="计åå®å·¥æ¶é´" prop="planEndTime"> |
| | | <el-date-picker |
| | | v-model="form.planEndTime" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="å®é
å¼å·¥æ¥æ" prop="actualStartTime"> |
| | | <el-date-picker |
| | | v-model="form.actualStartTime" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="æ¬æ¬¡è¿åº¦æ¥æ" prop="reportDate"> |
| | | <el-date-picker |
| | | v-model="form.reportDate" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="䏿¬¡è¿åº¦(%)" prop="lastProgress"> |
| | | <el-input-number v-model="form.lastProgress" :min="0" :max="100" controls-position="right" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="宿è¿åº¦(%)" prop="completionProgress"> |
| | | <div style="display: flex; gap: 8px; width: 100%;"> |
| | | <el-input-number v-model="form.completionProgress" :min="0" :max="100" controls-position="right" style="flex: 1" /> |
| | | <el-button type="danger" @click="markDone">宿</el-button> |
| | | </div> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="累计è¿åº¦(%)" prop="totalProgress"> |
| | | <el-input-number v-model="form.totalProgress" :min="0" :max="100" controls-position="right" style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="å®é
å®å·¥æ¥æ" prop="actualEndTime"> |
| | | <el-date-picker |
| | | v-model="form.actualEndTime" |
| | | type="date" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="è¯·éæ©" |
| | | style="width: 100%" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="8"> |
| | | <el-form-item label="è´è´£äºº" prop="managerName"> |
| | | <el-input v-model="form.managerName" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="é¨é¨" prop="departmentName"> |
| | | <el-input v-model="form.departmentName" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="16"> |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input v-model="form.remark" placeholder="请è¾å
¥" clearable /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-form-item label="éä»¶" prop="attachmentIds"> |
| | | <FileUpload v-model:file-list="form.storageBlobDTOs" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submit">ç¡®å®</el-button> |
| | | <el-button @click="visible = false">åæ¶</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup name="ProgressReportDialog"> |
| | | import { computed, reactive, ref, watch } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { getToken } from '@/utils/auth' |
| | | import FileUpload from "@/components/AttachmentUpload/file/index.vue"; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { type: Boolean, default: false }, |
| | | projectId: { type: [Number, String], default: undefined }, |
| | | projectInfo: { type: Object, default: () => ({}) }, |
| | | planNodes: { type: Array, default: () => [] }, |
| | | defaultPlanNodeId: { type: [Number, String], default: undefined } |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:modelValue', 'submitted']) |
| | | |
| | | const visible = computed({ |
| | | get: () => props.modelValue, |
| | | set: v => emit('update:modelValue', v) |
| | | }) |
| | | |
| | | const formRef = ref() |
| | | |
| | | const form = ref({ |
| | | planNodeId: undefined, |
| | | planStartTime: '', |
| | | planEndTime: '', |
| | | actualStartTime: '', |
| | | actualEndTime: '', |
| | | reportDate: '', |
| | | lastProgress: 0, |
| | | completionProgress: 0, |
| | | totalProgress: 0, |
| | | managerName: '', |
| | | departmentName: '', |
| | | remark: '', |
| | | storageBlobDTOs: [] |
| | | }) |
| | | |
| | | const rules = { |
| | | planNodeId: [{ required: true, message: 'è¯·éæ©', trigger: 'change' }], |
| | | planStartTime: [{ required: true, message: 'è¯·éæ©è®¡åå¼å§æ¶é´', trigger: 'change' }], |
| | | planEndTime: [{ required: true, message: 'è¯·éæ©è®¡åå®å·¥æ¶é´', trigger: 'change' }], |
| | | reportDate: [{ required: true, message: 'è¯·éæ©', trigger: 'change' }], |
| | | completionProgress: [{ required: true, message: '请è¾å
¥', trigger: 'change' }] |
| | | } |
| | | |
| | | const stageOptions = computed(() => { |
| | | const list = Array.isArray(props.planNodes) ? props.planNodes : [] |
| | | const sorted = [...list].sort((a, b) => Number(a.sort ?? 0) - Number(b.sort ?? 0)) |
| | | return sorted |
| | | .map(n => ({ |
| | | label: n.name || n.workContent || n.title || String(n.id ?? ''), |
| | | value: n.id |
| | | })) |
| | | .filter(i => i.value !== undefined && i.value !== null && i.value !== '') |
| | | }) |
| | | |
| | | function resetFromProject() { |
| | | const info = props.projectInfo || {} |
| | | form.value = { |
| | | planNodeId: props.defaultPlanNodeId ?? stageOptions.value[0]?.value, |
| | | planStartTime: info.planStartTime || '', |
| | | planEndTime: info.planEndTime || '', |
| | | actualStartTime: info.actualStartTime || '', |
| | | actualEndTime: info.actualEndTime || '', |
| | | reportDate: '', |
| | | lastProgress: Number(info.lastProgress ?? 0) || 0, |
| | | completionProgress: 0, |
| | | totalProgress: Number(info.totalProgress ?? info.progress ?? 0) || 0, |
| | | managerName: info.managerName || '', |
| | | departmentName: info.departmentName || '', |
| | | remark: '', |
| | | storageBlobDTOs: [] |
| | | } |
| | | } |
| | | |
| | | watch( |
| | | () => props.modelValue, |
| | | v => { |
| | | if (v) resetFromProject() |
| | | } |
| | | ) |
| | | |
| | | function handleClose() { |
| | | formRef.value?.resetFields?.() |
| | | } |
| | | |
| | | function markDone() { |
| | | form.value.completionProgress = 100 |
| | | form.value.totalProgress = 100 |
| | | if (!form.value.actualEndTime) form.value.actualEndTime = form.value.reportDate || '' |
| | | } |
| | | |
| | | async function submit() { |
| | | await formRef.value?.validate?.() |
| | | emit('submitted', { |
| | | projectId: props.projectId, |
| | | ...form.value |
| | | }) |
| | | visible.value = false |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | } |
| | | </style> |