From b58f53769f004fb0ba6b5aedf55ef602850c7c97 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期日, 20 九月 2026 15:46:05 +0800
Subject: [PATCH] 天津豹鸣app 1.部署修改

---
 src/pages/projectManagement/project/progress.vue |  326 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 326 insertions(+), 0 deletions(-)

diff --git a/src/pages/projectManagement/project/progress.vue b/src/pages/projectManagement/project/progress.vue
new file mode 100644
index 0000000..6931be4
--- /dev/null
+++ b/src/pages/projectManagement/project/progress.vue
@@ -0,0 +1,326 @@
+<!--
+  杩涘害姹囨姤锛堚啋 POST /projectManagement/info/saveStage锛�
+
+  瀛楁浠ョ鐞嗙 projectDetail.vue 鐨� handleProgressSubmitted 缁勮鍑虹殑璇锋眰浣撲负鍑嗐��
+  绠$悊绔� ProgressReportDialog 閲岃繕鏈夈�屼笂娆¤繘搴� / 璐熻矗浜� / 閮ㄩ棬銆嶄笁涓緭鍏ユ锛屼絾瀹冧滑
+  骞朵笉鍦ㄨ姹備綋閲岋紙璐熻矗浜哄彇鐨勬槸褰撳墠鐧诲綍浜猴紝涓嶆槸琛ㄥ崟鍊硷級锛屾墍浠ヨ繖閲屼笉鍋氬彲缂栬緫鎺т欢锛�
+  銆屼笂娆¤繘搴︺�嶇敱宸叉湁姹囨姤璁板綍鎺ㄧ畻鍚庡彧璇诲睍绀猴紝銆岃礋璐d汉銆嶆樉绀哄皢琚褰曠殑褰撳墠鐧诲綍浜猴紝
+  閬垮厤鍑虹幇濉簡涓嶇敓鏁堢殑瀛楁銆�
+-->
+<template>
+  <view class="project-progress">
+    <PageHeader title="杩涘害姹囨姤"
+                @back="goBack" />
+
+    <view v-if="projectName"
+          class="project-banner">
+      <text class="banner-label">椤圭洰鍚嶇О</text>
+      <text class="banner-value">{{ projectName }}</text>
+    </view>
+
+    <view class="form-container">
+      <up-form ref="formRef"
+               :model="form"
+               :rules="rules"
+               label-width="120"
+               input-align="right"
+               error-message-align="right">
+        <u-cell-group title="闃舵淇℃伅"
+                      class="form-section">
+          <up-form-item label="椤圭洰闃舵"
+                        prop="planNodeId"
+                        required>
+            <FormPicker v-model="form.planNodeId"
+                        type="select"
+                        title="閫夋嫨椤圭洰闃舵"
+                        :options="stageOptions"
+                        placeholder="璇烽�夋嫨椤圭洰闃舵" />
+          </up-form-item>
+          <up-form-item label="璁″垝寮�濮嬫椂闂�"
+                        prop="planStartTime"
+                        required>
+            <FormPicker v-model="form.planStartTime"
+                        type="date"
+                        placeholder="璇烽�夋嫨璁″垝寮�濮嬫椂闂�" />
+          </up-form-item>
+          <up-form-item label="璁″垝瀹屽伐鏃堕棿"
+                        prop="planEndTime"
+                        required>
+            <FormPicker v-model="form.planEndTime"
+                        type="date"
+                        placeholder="璇烽�夋嫨璁″垝瀹屽伐鏃堕棿" />
+          </up-form-item>
+          <up-form-item label="瀹為檯寮�宸ユ棩鏈�"
+                        prop="actualStartTime">
+            <FormPicker v-model="form.actualStartTime"
+                        type="date"
+                        placeholder="璇烽�夋嫨瀹為檯寮�宸ユ棩鏈�" />
+          </up-form-item>
+        </u-cell-group>
+
+        <u-cell-group title="杩涘害鎯呭喌"
+                      class="form-section">
+          <up-form-item label="鏈杩涘害鏃ユ湡"
+                        prop="reportDate"
+                        required>
+            <FormPicker v-model="form.reportDate"
+                        type="date"
+                        placeholder="璇烽�夋嫨鏈杩涘害鏃ユ湡" />
+          </up-form-item>
+          <up-form-item label="涓婃杩涘害(%)">
+            <up-input :model-value="String(lastProgress)"
+                      disabled />
+          </up-form-item>
+          <up-form-item label="瀹屾垚杩涘害(%)"
+                        prop="completionProgress"
+                        required>
+            <view class="progress-input">
+              <up-input v-model="form.completionProgress"
+                        type="number"
+                        placeholder="璇疯緭鍏ュ畬鎴愯繘搴�" />
+              <up-button type="primary"
+                         size="small"
+                         text="瀹屾垚"
+                         @click="markDone" />
+            </view>
+          </up-form-item>
+          <up-form-item label="绱杩涘害(%)"
+                        prop="totalProgress">
+            <up-input v-model="form.totalProgress"
+                      type="number"
+                      placeholder="鐣欑┖鍒欏彇瀹屾垚杩涘害" />
+          </up-form-item>
+          <up-form-item label="瀹為檯瀹屽伐鏃ユ湡"
+                        prop="actualEndTime">
+            <FormPicker v-model="form.actualEndTime"
+                        type="date"
+                        placeholder="璇烽�夋嫨瀹為檯瀹屽伐鏃ユ湡" />
+          </up-form-item>
+        </u-cell-group>
+
+        <u-cell-group title="姹囨姤淇℃伅"
+                      class="form-section">
+          <up-form-item label="璐熻矗浜�">
+            <up-input :model-value="leaderName || '褰撳墠鐧诲綍浜�'"
+                      disabled />
+          </up-form-item>
+          <up-form-item label="澶囨敞"
+                        prop="remark">
+            <up-textarea v-model="form.remark"
+                         placeholder="璇疯緭鍏ュ娉�"
+                         :maxlength="200"
+                         count
+                         auto-height />
+          </up-form-item>
+          <view class="upload-block">
+            <CommonUpload v-model="attachmentFiles"
+                          accept="all" />
+          </view>
+        </u-cell-group>
+      </up-form>
+    </view>
+
+    <FooterButtons :loading="loading"
+                   confirm-text="鎻愪氦姹囨姤"
+                   @cancel="goBack"
+                   @confirm="handleSubmit" />
+  </view>
+</template>
+
+<script setup>
+  import { computed, ref } from "vue";
+  import { onLoad } from "@dcloudio/uni-app";
+  import PageHeader from "@/components/PageHeader.vue";
+  import FooterButtons from "@/components/FooterButtons.vue";
+  import FormPicker from "@/components/FormPicker.vue";
+  import CommonUpload from "@/components/CommonUpload.vue";
+  import useUserStore from "@/store/modules/user";
+  import { getProject, saveStage } from "@/api/projectManagement/project";
+  import {
+    buildStagePayload,
+    defaultPlanNodeId,
+    fetchPlanNodeList,
+    fetchProjectStages,
+    planNodeOptions,
+  } from "./_utils/stage";
+
+  const userStore = useUserStore();
+
+  const formRef = ref();
+  const loading = ref(false);
+  const projectId = ref("");
+  const info = ref({});
+  const projectName = ref("");
+  const planNodes = ref([]);
+  const lastProgress = ref(0);
+  const attachmentFiles = ref([]);
+
+  const leaderName = computed(() => userStore.nickName || "");
+
+  const stageOptions = computed(() => planNodeOptions(planNodes.value));
+
+  const form = ref({
+    planNodeId: "",
+    planStartTime: "",
+    planEndTime: "",
+    actualStartTime: "",
+    actualEndTime: "",
+    reportDate: "",
+    completionProgress: "",
+    totalProgress: "",
+    remark: "",
+  });
+
+  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: "blur" },
+    ],
+  };
+
+  const goBack = () => uni.navigateBack();
+
+  // 鍚庣涓嶆帴鏀躲�屼笂娆¤繘搴︺�嶏紝鐢卞凡姹囨姤璁板綍閲岀殑鏈�澶� progress 鎺ㄧ畻锛屽彧浣滃~鍐欏弬鑰�
+  const deriveLastProgress = stages =>
+    (Array.isArray(stages) ? stages : []).reduce((max, row) => {
+      const value = Number(row?.progress ?? 0) || 0;
+      return value > max ? value : max;
+    }, 0);
+
+  const markDone = () => {
+    form.value.completionProgress = "100";
+    form.value.totalProgress = "100";
+    if (!form.value.actualEndTime) form.value.actualEndTime = form.value.reportDate;
+  };
+
+  const loadDetail = async () => {
+    try {
+      const res = await getProject(projectId.value);
+      const raw = res?.data?.data ?? res?.data ?? res;
+      info.value = raw?.info || {};
+      projectName.value = info.value.title || "";
+
+      const [nodes, stages] = await Promise.all([
+        fetchPlanNodeList(info.value.projectManagementPlanId),
+        fetchProjectStages(projectId.value),
+      ]);
+      planNodes.value = nodes;
+      lastProgress.value = deriveLastProgress(stages);
+
+      form.value.planNodeId = defaultPlanNodeId(info.value.stage, nodes);
+      form.value.planStartTime = info.value.planStartTime || "";
+      form.value.planEndTime = info.value.planEndTime || "";
+      form.value.actualStartTime = info.value.actualStartTime || "";
+      form.value.actualEndTime = info.value.actualEndTime || "";
+      // 鏈夊巻鍙叉眹鎶ユ墠棰勫~绱杩涘害锛涙病鏈夊垯鐣欑┖锛屾彁浜ゆ椂閫�鍥炪�屽畬鎴愯繘搴︺��
+      form.value.totalProgress = lastProgress.value
+        ? String(lastProgress.value)
+        : "";
+    } catch (error) {
+      uni.showToast({ title: "鑾峰彇椤圭洰璇︽儏澶辫触", icon: "none" });
+    }
+  };
+
+  const handleSubmit = async () => {
+    const valid = await formRef.value.validate().catch(() => false);
+    if (!valid) return;
+
+    loading.value = true;
+    saveStage(
+      buildStagePayload({
+        projectId: projectId.value,
+        projectInfo: info.value,
+        planNodes: planNodes.value,
+        form: form.value,
+        attachments: attachmentFiles.value,
+        leaderId: userStore.id,
+        leaderName: userStore.nickName,
+      })
+    )
+      .then(() => {
+        uni.showToast({ title: "姹囨姤鎴愬姛", icon: "success" });
+        setTimeout(() => uni.navigateBack(), 300);
+      })
+      .catch(() => {
+        uni.showToast({ title: "姹囨姤澶辫触", icon: "none" });
+      })
+      .finally(() => {
+        loading.value = false;
+      });
+  };
+
+  onLoad(options => {
+    projectId.value = options?.id || "";
+    if (projectId.value) loadDetail();
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "@/static/scss/form-common.scss";
+
+  .project-progress {
+    min-height: 100vh;
+    background: #f8f9fa;
+    padding-bottom: 100px;
+  }
+
+  .project-banner {
+    margin: 12px 12px 0;
+    padding: 14px 16px;
+    border-radius: 12px;
+    background: #ffffff;
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+  }
+
+  .banner-label {
+    font-size: 13px;
+    color: #909399;
+  }
+
+  .banner-value {
+    font-size: 16px;
+    font-weight: 600;
+    color: #22324d;
+  }
+
+  .form-container {
+    padding: 12px 12px 0;
+  }
+
+  .form-section {
+    margin-bottom: 12px;
+    border-radius: 12px;
+    overflow: hidden;
+    box-shadow: 0 2px 10px rgba(15, 35, 95, 0.05);
+  }
+
+  .progress-input {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    width: 100%;
+  }
+
+  .upload-block {
+    padding: 12px;
+  }
+
+  :deep(.u-cell-group__title) {
+    padding: 14px 18px 10px !important;
+    font-size: 15px !important;
+    font-weight: 600 !important;
+    color: #22324d !important;
+    background: #f8fbff !important;
+  }
+</style>

--
Gitblit v1.9.3