From 4f8ccd4b18baa9301c2e389bc0e53d499be54802 Mon Sep 17 00:00:00 2001
From: ZN <zhang_12370@163.com>
Date: 星期三, 11 三月 2026 18:01:39 +0800
Subject: [PATCH] Merge branch 'dev_New' of http://114.132.189.42:9002/r/product-inventory-management into dev_New
---
src/components/ProjectManagement/ProgressReportDialog.vue | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 282 insertions(+), 0 deletions(-)
diff --git a/src/components/ProjectManagement/ProgressReportDialog.vue b/src/components/ProjectManagement/ProgressReportDialog.vue
new file mode 100644
index 0000000..d9402c2
--- /dev/null
+++ b/src/components/ProjectManagement/ProgressReportDialog.vue
@@ -0,0 +1,282 @@
+<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">
+ <el-upload
+ v-model:file-list="fileList"
+ :action="upload.url"
+ :headers="upload.headers"
+ multiple
+ name="files"
+ :on-success="handleUploadSuccess"
+ :on-error="handleUploadError"
+ :on-remove="handleRemove"
+ >
+ <el-button type="primary">涓婁紶鏂囦欢</el-button>
+ </el-upload>
+ </el-form-item>
+ </el-form>
+
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button @click="visible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="submit">纭畾</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'
+
+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 fileList = ref([])
+const upload = reactive({
+ url: import.meta.env.VITE_APP_BASE_API + '/basic/customer-follow/upload',
+ headers: { Authorization: 'Bearer ' + getToken() }
+})
+
+const form = ref({
+ planNodeId: undefined,
+ planStartTime: '',
+ planEndTime: '',
+ actualStartTime: '',
+ actualEndTime: '',
+ reportDate: '',
+ lastProgress: 0,
+ completionProgress: 0,
+ totalProgress: 0,
+ managerName: '',
+ departmentName: '',
+ remark: '',
+ attachmentIds: []
+})
+
+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: '',
+ attachmentIds: []
+ }
+ fileList.value = []
+}
+
+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 || ''
+}
+
+function handleUploadError() {
+ ElMessage.error('涓婁紶鏂囦欢澶辫触')
+}
+
+function handleUploadSuccess(res, file) {
+ if (res?.code !== 200) {
+ ElMessage.error(res?.msg || '涓婁紶澶辫触')
+ return
+ }
+ const attachmentId = res?.data?.id ?? res?.data?.tempId ?? ''
+ if (!attachmentId) return
+ form.value.attachmentIds.push(attachmentId)
+ try {
+ file.attachmentId = attachmentId
+ } catch (e) {}
+ ElMessage.success('涓婁紶鎴愬姛')
+}
+
+function handleRemove(file) {
+ const attachmentId = file?.attachmentId
+ if (!attachmentId) return
+ form.value.attachmentIds = (form.value.attachmentIds || []).filter(id => id !== attachmentId)
+}
+
+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>
--
Gitblit v1.9.3