Просмотр исходного кода

处理服务工单-单人接单改为多人接单;完成服务汇总页面开发及对接

aiwenzhu 1 месяц назад
Родитель
Сommit
5b6ace8963

Разница между файлами не показана из-за своего большого размера
+ 981 - 638
src/views/approvalManagement/businessTravelApproval/index.vue


Разница между файлами не показана из-за своего большого размера
+ 585 - 381
src/views/productManagement/installationCompletedSummary/components/InstallationStatistics.vue


+ 4 - 0
src/views/productManagement/installationOrder/components/AddDialog.vue

@@ -431,6 +431,10 @@ export default {
   methods: {
     // 处理客户输入
     handleCustomerInput(value) {
+      if (!value) {
+        this.customerOptions = [];
+        return;
+      }
       this.loading = true;
       const send_select_list = {
         name: "getCustomerNameFuzzy",

+ 10 - 18
src/views/productManagement/installationOrder/components/DailyWriteDialog.vue

@@ -63,6 +63,7 @@
           {{ scope.row.goodsName }}
         </template>
       </el-table-column>
+      <el-table-column prop="userName" label="服务人员" align="center" />
       <el-table-column
         prop="isWrite"
         label="是否填写"
@@ -77,18 +78,8 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column
-        prop="orderQuantity"
-        label="计划总量"
-        width="100"
-        align="center"
-      />
-      <el-table-column
-        prop="installQuantity"
-        label="累计完成量"
-        width="100"
-        align="center"
-      />
+      <el-table-column prop="orderQuantity" label="计划总量" align="center" />
+      <el-table-column prop="installQuantity" label="已完成量" align="center" />
 
       <el-table-column
         prop="todayQuantity"
@@ -109,12 +100,7 @@
           />
         </template>
       </el-table-column>
-      <el-table-column
-        prop="unshippedQuantity"
-        label="剩余量"
-        width="100"
-        align="center"
-      />
+      <el-table-column prop="unshippedQuantity" label="剩余量" align="center" />
       <el-table-column prop="installRemark" label="备注" align="center">
         <template slot-scope="scope">
           <el-input
@@ -174,6 +160,10 @@ export default {
       type: Object,
       default: () => ({}),
     },
+    currentUser: {
+      type: Object,
+      default: () => ({}),
+    },
   },
   data() {
     return {
@@ -226,6 +216,7 @@ export default {
         returntype: "Map",
         parammaps: {
           orderId: this.rowData.id,
+          userId: this.currentUser.id,
           dates: dates.join(","),
         },
       };
@@ -284,6 +275,7 @@ export default {
             installRemark: item.installRemark || "",
             isWrite: item.isWrite || "未填写",
             orderId: item.orderId,
+            userName: item.userName,
           };
           return processedItem;
         });

+ 64 - 1
src/views/productManagement/installationOrder/components/DispatchDialog.vue

@@ -16,6 +16,7 @@
           placeholder="请选择服务人员"
           style="width: 100%"
           filterable
+          @change="handleServiceStaffChange"
         >
           <el-option
             v-for="item in installerOptions"
@@ -59,7 +60,7 @@
 </template>
 
 <script>
-import { ExecDataByConfig } from "@/api/common";
+import { ExecDataByConfig, GetDataByName } from "@/api/common";
 
 export default {
   name: "DispatchDialog",
@@ -91,6 +92,7 @@ export default {
         dispatcherId: "",
         dispatcherDate: new Date(),
       },
+      previousServiceStaffIds: [],
       rules: {
         serviceStaffIds: [
           { required: true, message: "请选择服务人员", trigger: "change" },
@@ -111,15 +113,76 @@ export default {
         this.$nextTick(() => {
           this.form.dispatcherId = parseInt(this.currentUser.id);
           this.form.dispatcherDate = new Date();
+          // 设置默认选中的服务人员
+          if (this.rowData.serviceStaffIds) {
+            this.form.serviceStaffIds = this.rowData.serviceStaffIds
+              .split(",")
+              .map((id) => parseInt(id.trim()));
+            this.previousServiceStaffIds = [...this.form.serviceStaffIds]; // 保存初始状态
+          }
         });
       }
     },
   },
   methods: {
+    // 处理服务人员选择变化
+    async handleServiceStaffChange(newValue) {
+      // 找出被删除的 ID(只会有一个)
+      const removedId = this.previousServiceStaffIds.find(
+        (id) => !newValue.includes(id)
+      );
+
+      // 如果有被删除的 ID
+      if (removedId) {
+        // 获取被删除人员的名称
+        const removedStaff = this.installerOptions.find(
+          (item) => item.value === removedId
+        );
+
+        const params = {
+          name: "checkIsDoOrderByUserId",
+          returntype: "Map",
+          parammaps: {
+            orderId: this.rowData.id,
+            userId: removedId,
+          },
+        };
+
+        try {
+          const response = await GetDataByName(params);
+          if (!response) {
+            throw new Error("接口返回数据为空");
+          }
+          if (response.data.list && response.data.list[0].result === "1") {
+            this.$message.warning(
+              `不能移除, ${removedStaff?.label || "当前服务人员"} 已开始服务`
+            );
+            // 恢复选中状态
+            this.$nextTick(() => {
+              this.form.serviceStaffIds = [...this.previousServiceStaffIds];
+            });
+            return;
+          }
+        } catch (error) {
+          console.error("数据处理失败:", error);
+        }
+      }
+
+      // 如果检查通过,更新上一次的选择状态
+      this.previousServiceStaffIds = [...newValue];
+    },
+
     // 关闭弹窗
     handleDialogClose() {
       this.$emit("update:visible", false);
       this.$refs.form && this.$refs.form.resetFields();
+      // 重置表单数据
+      this.form = {
+        serviceStaffIds: [],
+        dispatcherId: "",
+        dispatcherDate: new Date(),
+      };
+      this.previousServiceStaffIds = []; // 重置上一次的选择状态
     },
 
     // 取消

+ 191 - 12
src/views/productManagement/installationOrder/components/ViewDialog.vue

@@ -166,7 +166,7 @@
                     prop="goodsCode"
                     label="货品编号"
                     align="center"
-                    width="100"
+                    width="80"
                   />
                   <el-table-column
                     prop="goodsName"
@@ -380,7 +380,7 @@
       </el-tab-pane>
 
       <!-- 服务进度标签页 -->
-      <el-tab-pane label="服务进度" name="progress">
+      <el-tab-pane label="服务记录" name="progress">
         <div class="service-progress">
           <div class="progress-header">
             <div class="header-item">
@@ -410,31 +410,46 @@
               color: '#606266',
               fontWeight: 500,
             }"
+            :span-method="handleSpanMethod"
           >
             <el-table-column
               prop="installDate"
               label="日期"
               align="center"
-              width="120"
+              width="100"
+            />
+            <el-table-column
+              prop="goodsCode"
+              label="货品编号"
+              align="center"
+              min-width="80"
             />
             <el-table-column
               prop="goodsName"
-              label="货品"
+              label="货品名称"
               align="center"
-              min-width="150"
+              min-width="120"
             />
+
             <el-table-column
               prop="orderQuantity"
               label="服务总量"
               align="center"
-              width="100"
+              width="80"
             />
             <el-table-column
-              prop="shippedQuantity"
-              label="累计服务量"
+              prop="unshippedQuantity"
+              label="剩余量"
               align="center"
-              width="100"
+              width="80"
             />
+            <el-table-column
+              prop="installUserName"
+              label="服务人员"
+              align="center"
+              min-width="80"
+            />
+
             <el-table-column
               prop="installQuantity"
               label="当日服务量"
@@ -442,8 +457,8 @@
               width="100"
             />
             <el-table-column
-              prop="unshippedQuantity"
-              label="剩余量"
+              prop="shippedQuantity"
+              label="累计服务量"
               align="center"
               width="100"
             />
@@ -598,7 +613,18 @@ export default {
       if (val) {
         // 弹窗打开时初始化数据
         this.$nextTick(() => {
-          this.getOrderData();
+          this.initData();
+        });
+      } else {
+        // 弹窗关闭时重置数据
+        this.resetData();
+      }
+    },
+    activeTab(val) {
+      if (val === "progress") {
+        // 切换到服务记录标签页时重新初始化合并状态
+        this.$nextTick(() => {
+          this.mergeGroups = [];
         });
       }
     },
@@ -638,9 +664,53 @@ export default {
         serviceProgressData: [],
       },
       activeTab: "basic",
+      spanCount: 1,
+      mergeGroups: [],
+      dateMergeGroups: [],
     };
   },
   methods: {
+    // 初始化数据
+    initData() {
+      this.isTableVisible = true;
+      this.mergeGroups = [];
+      this.activeTab = "basic";
+      this.getOrderData();
+    },
+    // 重置数据
+    resetData() {
+      this.form = {
+        orderNo: "",
+        orderTime: "",
+        orderer: "",
+        serviceProject: "",
+        serviceStaffNames: "",
+        projectName: "",
+        estimatedCompleteTime: "",
+        deliveryNo: "",
+        contractNo: "",
+        customerName: "",
+        remark: "",
+        products: [],
+        totalQuantity: 0,
+        installedQuantity: 0,
+        uninstalledQuantity: 0,
+        progress: 0,
+        acceptTime: "",
+        actualCompleteTime: "",
+        remainingTime: null,
+        acceptanceImagePath: "",
+        currentStep: 2,
+        isRejected: true,
+        rejectReason: "",
+        startTime: "",
+        acceptanceTime: "",
+        createName: "",
+        serviceProgressData: [],
+      };
+      this.mergeGroups = [];
+      this.isTableVisible = true;
+    },
     // 获取订单数据
     async getOrderData() {
       try {
@@ -721,6 +791,7 @@ export default {
               shippedQuantity: item.shippedQuantity || 0,
               unshippedQuantity: item.unshippedQuantity || 0,
               remark: item.remark || "",
+              installQuantity: item.installQuantity || 0,
             })),
             processData: processData,
             acceptanceImagePath: mainData.acceptanceImagePath || "",
@@ -733,6 +804,7 @@ export default {
             dispatcherName: mainData.dispatcherName || "",
             contactName: mainData.contactName || "",
             contactPhone: mainData.contactPhone || "",
+            goodsCode: mainData.goodsCode || "",
           };
         } else {
           this.$message.error("获取数据失败");
@@ -777,6 +849,113 @@ export default {
       }
       return "wait"; // 其他步骤显示为等待状态
     },
+
+    // 处理单元格合并方法
+    handleSpanMethod({ row, column, rowIndex, columnIndex }) {
+      const data = this.form.serviceProgressData;
+      if (!data || !data.length) return;
+
+      // 如果是第一行,初始化合并组
+      if (rowIndex === 0) {
+        // 日期列的合并组
+        this.dateMergeGroups = [];
+        let dateGroup = {
+          start: 0,
+          count: 1,
+        };
+
+        // 货品相关列的合并组
+        this.mergeGroups = [];
+        let currentGroup = {
+          start: 0,
+          count: 1,
+        };
+
+        // 遍历所有行,分别处理日期和货品的合并
+        for (let i = 1; i < data.length; i++) {
+          const currentRow = data[i - 1];
+          const nextRow = data[i];
+
+          // 日期列的合并逻辑(只看日期是否相同)
+          if (currentRow.installDate === nextRow.installDate) {
+            dateGroup.count++;
+          } else {
+            this.dateMergeGroups.push({ ...dateGroup });
+            dateGroup = {
+              start: i,
+              count: 1,
+            };
+          }
+
+          // 货品相关列的合并逻辑(需要所有相关字段都相同)
+          const isSameProduct =
+            currentRow.goodsCode === nextRow.goodsCode &&
+            currentRow.goodsName === nextRow.goodsName &&
+            currentRow.orderQuantity === nextRow.orderQuantity &&
+            currentRow.unshippedQuantity === nextRow.unshippedQuantity &&
+            currentRow.installDate === nextRow.installDate; // 添加日期条件
+
+          if (isSameProduct) {
+            currentGroup.count++;
+          } else {
+            this.mergeGroups.push({ ...currentGroup });
+            currentGroup = {
+              start: i,
+              count: 1,
+            };
+          }
+        }
+        // 添加最后一组
+        this.dateMergeGroups.push({ ...dateGroup });
+        this.mergeGroups.push({ ...currentGroup });
+      }
+
+      // 日期列的合并规则(第一列)
+      if (columnIndex === 0) {
+        for (const group of this.dateMergeGroups) {
+          if (rowIndex === group.start) {
+            return {
+              rowspan: group.count,
+              colspan: 1,
+            };
+          } else if (
+            rowIndex > group.start &&
+            rowIndex < group.start + group.count
+          ) {
+            return {
+              rowspan: 0,
+              colspan: 0,
+            };
+          }
+        }
+      }
+
+      // 货品相关列的合并规则(第2-5列)
+      if (columnIndex > 0 && columnIndex <= 4) {
+        for (const group of this.mergeGroups) {
+          if (rowIndex === group.start) {
+            return {
+              rowspan: group.count,
+              colspan: 1,
+            };
+          } else if (
+            rowIndex > group.start &&
+            rowIndex < group.start + group.count
+          ) {
+            return {
+              rowspan: 0,
+              colspan: 0,
+            };
+          }
+        }
+      }
+
+      // 其他列不合并
+      return {
+        rowspan: 1,
+        colspan: 1,
+      };
+    },
   },
 };
 </script>

+ 91 - 33
src/views/productManagement/installationOrder/index.vue

@@ -411,7 +411,7 @@
               v-if="
                 checkButtonPermission(
                   'productManagement:installationOrder:edit'
-                )
+                ) && isCurrentUserInCreateId(scope.row.createId)
               "
               >编辑</el-button
             >
@@ -422,7 +422,7 @@
               v-if="
                 checkButtonPermission(
                   'productManagement:installationOrder:dispatch'
-                )
+                ) && isCurrentUserInCreateId(scope.row.createId)
               "
               >派单</el-button
             >
@@ -437,7 +437,7 @@
               v-if="
                 checkButtonPermission(
                   'productManagement:installationOrder:reject'
-                )
+                ) && isCurrentUserInServiceStaff(scope.row.serviceStaffIds)
               "
               >驳回</el-button
             >
@@ -452,7 +452,7 @@
               v-if="
                 checkButtonPermission(
                   'productManagement:installationOrder:accept'
-                )
+                ) && isCurrentUserInServiceStaff(scope.row.serviceStaffIds)
               "
               >接单</el-button
             >
@@ -463,12 +463,28 @@
               v-if="
                 checkButtonPermission(
                   'productManagement:installationOrder:delete'
-                )
+                ) && isCurrentUserInCreateId(scope.row.createId)
               "
               >删除</el-button
             >
           </template>
-          <template v-else-if="scope.row.statusName === '处理中'">
+          <template
+            v-else-if="
+              scope.row.statusName === '处理中' ||
+              scope.row.statusName === '已接单'
+            "
+          >
+            <el-button
+              size="mini"
+              type="success"
+              @click="handleDispatch(scope.row)"
+              v-if="
+                checkButtonPermission(
+                  'productManagement:installationOrder:dispatch'
+                ) && isCurrentUserInCreateId(scope.row.createId)
+              "
+              >派单</el-button
+            >
             <el-button
               size="mini"
               style="
@@ -480,7 +496,7 @@
               v-if="
                 checkButtonPermission(
                   'productManagement:installationOrder:dailyWrite'
-                )
+                ) && isCurrentUserInServiceStaff(scope.row.serviceStaffIds)
               "
               >填写</el-button
             >
@@ -491,7 +507,7 @@
               v-if="
                 checkButtonPermission(
                   'productManagement:installationOrder:complete'
-                )
+                ) && isCurrentUserInServiceStaff(scope.row.serviceStaffIds)
               "
               >完成</el-button
             >
@@ -599,6 +615,7 @@
     <daily-write-dialog
       :visible.sync="dailyWriteDialogVisible"
       :row-data="currentRow"
+      :current-user="currentUser"
       @confirm="handleDailyWriteConfirm"
     />
 
@@ -701,10 +718,13 @@ export default {
         orderNo: "",
         deliveryNo: "",
         contractNo: "",
+        userId: Cookies.get("g_userId") || "",
+        roleId: Cookies.get("g_roleId") || "",
       },
       // 状态选项
       statusOptions: [
         { label: "未接单", value: "未接单" },
+        { label: "已接单", value: "已接单" },
         { label: "处理中", value: "处理中" },
         { label: "已完成未验收", value: "已完成未验收" },
         { label: "已完成", value: "已完成" },
@@ -751,6 +771,7 @@ export default {
       currentUser: {
         id: Cookies.get("g_userId") || "",
         name: Cookies.get("g_empname") || "",
+        roleId: Cookies.get("g_roleId") || "",
       },
       editDialogVisible: false,
       viewDialogVisible: false,
@@ -796,15 +817,26 @@ export default {
       this.loading = true;
 
       // 使用参数处理混入方法
-      const send_data = this.handleParams(
-        this.queryParams,
-        "getInstallationOrderList"
-      );
+      const send_data = [
+        this.handleParams(this.queryParams, "getInstallationOrderList"),
+        {
+          name: "getInstallationOrderToDoNum",
+          offset: 0,
+          pagecount: 0,
+          parammaps: {
+            userId: this.currentUser.id,
+            roleId: this.currentUser.roleId,
+          },
+        },
+      ];
 
-      GetDataByName(send_data)
+      GetDataByNames(send_data)
         .then((response) => {
-          this.tableData = response.data.list || [];
-          this.total = response.data.total || 0;
+          this.tableData = response.data.getInstallationOrderList.list || [];
+          this.total = response.data.getInstallationOrderList.total || 0;
+          // 获取处理工单数量
+          this.pendingCount =
+            response.data.getInstallationOrderToDoNum.list[0].num;
           this.loading = false;
         })
         .catch((error) => {
@@ -831,12 +863,6 @@ export default {
             pid: "79",
           },
         },
-        {
-          name: "getInstallationOrderToDoNum",
-          offset: 0,
-          pagecount: 0,
-          parammaps: {},
-        },
       ];
 
       GetDataByNames(send_select_list)
@@ -853,10 +879,6 @@ export default {
           this.projectOptions = response.data.getDictListSelect.list || [];
 
           console.log("项目下拉框", this.projectOptions);
-
-          // 获取处理工单数量
-          this.pendingCount =
-            response.data.getInstallationOrderToDoNum.list[0].num;
         })
         .catch((error) => {
           console.error("获取下拉框数据失败:", error);
@@ -906,6 +928,7 @@ export default {
     getStatusType(statusName) {
       const statusMap = {
         未接单: "info",
+        已接单: "primary",
         处理中: "primary",
         已完成: "success",
         已完成未验收: "warning",
@@ -1001,6 +1024,7 @@ export default {
               type: "e",
               parammaps: {
                 datas: formData.dates.join(","),
+                userId: this.currentUser.id,
                 orderId: this.currentRow.id,
               },
             },
@@ -1046,6 +1070,13 @@ export default {
               parammaps: {
                 orderId: this.currentRow.id,
               },
+            },
+            {
+              name: "updateDailyDetailYesterdayAndBeforeQuantity",
+              type: "e",
+              parammaps: {
+                orderId: this.currentRow.id,
+              },
             }, // 在 handleDailyWriteConfirm 方法的 params.data 数组中添加:
             {
               name: "insertInstallationOrderProcessLog",
@@ -1261,6 +1292,7 @@ export default {
       // 根据状态返回对应的颜色
       const statusColorMap = {
         未接单: "#909399", // 灰色
+        已接单: "#409eff", // 蓝色
         处理中: "#409eff", // 蓝色
         已完成未验收: "#e6a23c", // 橙色
         已完成: "#67c23a", // 绿色
@@ -1270,6 +1302,9 @@ export default {
       return statusColorMap[status] || "#909399";
     },
     showProgressTooltip(event, percentage) {
+      // 先移除可能存在的旧tooltip
+      this.hideProgressTooltip();
+
       this.progressTooltip = document.createElement("div");
       this.progressTooltip.className = "progress-tooltip";
       this.progressTooltip.textContent = (() => {
@@ -1289,19 +1324,27 @@ export default {
         rect.left + rect.width / 2 - tooltipRect.width / 2 + "px";
       this.progressTooltip.style.top = rect.top - tooltipRect.height - 8 + "px";
 
-      setTimeout(() => {
-        this.progressTooltip.classList.add("show");
-      }, 0);
+      // 使用 requestAnimationFrame 确保 DOM 更新后再添加显示类
+      requestAnimationFrame(() => {
+        if (this.progressTooltip) {
+          this.progressTooltip.classList.add("show");
+        }
+      });
     },
     hideProgressTooltip() {
       if (this.progressTooltip) {
+        // 移除显示类
         this.progressTooltip.classList.remove("show");
-        setTimeout(() => {
-          if (this.progressTooltip && this.progressTooltip.parentNode) {
-            this.progressTooltip.parentNode.removeChild(this.progressTooltip);
+
+        // 确保移除所有可能存在的旧tooltip
+        const oldTooltips = document.querySelectorAll(".progress-tooltip");
+        oldTooltips.forEach((tooltip) => {
+          if (tooltip.parentNode) {
+            tooltip.parentNode.removeChild(tooltip);
           }
-          this.progressTooltip = null;
-        }, 200);
+        });
+
+        this.progressTooltip = null;
       }
     },
     // 处理驳回确认
@@ -1399,6 +1442,21 @@ export default {
     tableRowClassName({ row }) {
       return row.statusName === "已驳回" ? "rejected-row" : "";
     },
+    // 判断当前用户是否在服务人员列表中
+    isCurrentUserInServiceStaff(serviceStaffIds) {
+      if (!serviceStaffIds) return false;
+      const currentUserId = parseInt(this.currentUser.id);
+      const staffIds = serviceStaffIds
+        .split(",")
+        .map((id) => parseInt(id.trim()));
+      return staffIds.includes(currentUserId);
+    },
+    // 判断当前用户是否是创建人
+    isCurrentUserInCreateId(createId) {
+      console.log("createId", createId, this.currentUser.id);
+      if (!createId) return false;
+      return createId === parseInt(this.currentUser.id);
+    },
   },
 };
 </script>

+ 4 - 0
src/views/productManagement/installationOrder/mixins/paramsMixin.js

@@ -35,6 +35,8 @@ export default {
             : "",
           contractNo: restParams.contractNo || "",
           deliveryNo: restParams.deliveryNo || "",
+          userId: restParams.userId || "",
+          roleId: restParams.roleId || "",
         },
       };
     },
@@ -81,6 +83,8 @@ export default {
             : "",
           contractNo: restParams.contractNo || "",
           deliveryNo: restParams.deliveryNo || "",
+          userId: restParams.userId || "",
+          roleId: restParams.roleId || "",
         },
       };
     },

+ 1 - 1
src/views/productManagement/installationSummary/components/InstallationTable.vue

@@ -9,7 +9,7 @@
         "
       >
         <div>
-          <span style="font-size: 13px">安装概况</span>
+          <span style="font-size: 13px">服务概况</span>
           <span style="margin-left: 8px; font-size: 13px; color: #666">{{
             updateTime
           }}</span>

Некоторые файлы не были показаны из-за большого количества измененных файлов