gongchunyi
13 小时以前 b4e3bcda9d02f40702758485d894def270201ee6
src/views/salesManagement/salesLedger/components/ProcessFlowConfigSelectDialog.vue
@@ -8,10 +8,14 @@
  >
    <el-row :gutter="20">
      <el-col :span="24">
        <div style="font-weight: 600; margin-bottom: 8px;">配置</div>
        <div style="font-size: 12px; margin-bottom: 8px;">
          <span v-if="boundRouteName" style="color: #67c23a;">已绑定:{{ boundRouteName }}</span>
          <span v-else style="color: #e6a23c;">未绑定</span>
        <div class="dialog-topbar">
          <div>
            <div style="font-weight: 600; margin-bottom: 8px;">配置</div>
            <div style="font-size: 12px; margin-bottom: 8px;">
              <span v-if="boundRouteName" style="color: #67c23a;">已绑定:{{ boundRouteName }}</span>
              <span v-else style="color: #e6a23c;">未绑定</span>
            </div>
          </div>
        </div>
        <el-select
          v-model="selectedRouteId"
@@ -33,7 +37,7 @@
        <div style="font-weight: 600; margin-bottom: 8px;">步骤预览</div>
        <div style="font-size: 12px; color: #909399; margin-bottom: 10px;">
          根据所选配置展示流程图
          根据所选配置展示流程图,勾选表示该工序已完成
        </div>
      </el-col>
@@ -46,8 +50,27 @@
            class="process-diagram-segment"
          >
            <div class="process-diagram-node">
              <el-checkbox
                v-model="step.isCompleted"
                class="process-diagram-checkbox"
                @change="() => handleStepCompletedChange(step)"
              />
              <div class="process-diagram-index">{{ idx + 1 }}</div>
              <div class="process-diagram-name">{{ step.processName }}</div>
              <div class="process-diagram-status" :class="{ 'is-done': Number(step.isCompleted) === 1 }">
                {{ Number(step.isCompleted) === 1 ? "已完成" : "未完成" }}
              </div>
              <div v-if="step.isCompleted" style="margin-top: 8px;">
                <el-date-picker
                  v-model="step.completedTime"
                  type="datetime"
                  placeholder="选择时间"
                  format="YYYY-MM-DD HH:mm:ss"
                  value-format="YYYY-MM-DD HH:mm:ss"
                  size="small"
                  style="width: 100%;"
                />
              </div>
            </div>
            <div v-if="idx < steps.length - 1" class="process-diagram-arrow">→</div>
          </div>
@@ -79,6 +102,8 @@
  visible: { type: Boolean, default: false },
  // 打开弹窗时的回显:若业务已绑定工艺路线则传入该 routeId;否则默认展示列表第一条
  defaultRouteId: { type: [Number, String, null], default: null },
  // 打开弹窗时的工序完成记录回显
  defaultRecordList: { type: Array, default: () => [] },
  // 页面提示:订单已绑定的工艺路线名称
  boundRouteName: { type: String, default: "" },
});
@@ -103,9 +128,11 @@
  if (!Array.isArray(list)) return [];
  return list.map((s, idx) => ({
    stepId: s.stepId ?? s.id ?? null,
    processRouteItemId: s.processRouteItemId ?? s.process_route_item_id ?? s.id ?? null,
    processId: s.processId ?? s.process_id ?? s.id ?? null,
    processName: s.processName ?? s.process_name ?? s.name ?? "",
    sortNo: s.sortNo ?? idx + 1,
    isCompleted: Boolean(Number(s.isCompleted ?? s.completed ?? 0)),
  }));
};
@@ -116,6 +143,27 @@
    processRouteName: r.processRouteName ?? r.routeName ?? r.name ?? "",
    isDefault: Boolean(r.isDefault),
  }));
};
const applyRecordListToSteps = (stepList, recordList) => {
  if (!Array.isArray(stepList) || stepList.length === 0) return stepList;
  if (!Array.isArray(recordList) || recordList.length === 0) return stepList;
  const recordMap = new Map(
    recordList
      .filter((item) => item && item.processRouteItemId !== null && item.processRouteItemId !== undefined)
      .map((item) => [String(item.processRouteItemId), item])
  );
  return stepList.map((step) => {
    const matched = recordMap.get(String(step.processRouteItemId));
    if (!matched) return step;
    return {
      ...step,
      isCompleted: Boolean(Number(matched.isCompleted ?? 0)),
      completedTime: matched.completedTime ?? matched.completed_time ?? null,
    };
  });
};
const fetchRouteList = async () => {
@@ -132,7 +180,12 @@
  }
  const res = await salesProcessFlowConfigItemList(routeId);
  const raw = res?.data ?? res ?? [];
  steps.value = normalizeStepsFromApi(raw);
  const normalizedSteps = normalizeStepsFromApi(raw);
  if (String(routeId) === String(props.defaultRouteId)) {
    steps.value = applyRecordListToSteps(normalizedSteps, props.defaultRecordList);
    return;
  }
  steps.value = normalizedSteps;
};
watch(
@@ -163,6 +216,18 @@
  await fetchRouteSteps(selectedRouteId.value);
};
const formatDateTime = (date) => {
  const pad = (n) => (n < 10 ? '0' + n : n);
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
};
const handleStepCompletedChange = (step) => {
  step.isCompleted = Boolean(step.isCompleted);
  if (step.isCompleted && !step.completedTime) {
    step.completedTime = proxy?.parseTime ? proxy.parseTime(new Date()) : formatDateTime(new Date());
  }
};
const handleClose = () => {
  emit("update:visible", false);
  saving.value = false;
@@ -176,7 +241,14 @@
  }
  saving.value = true;
  try {
    emit("confirm", selectedRouteId.value);
    emit("confirm", {
      routeId: selectedRouteId.value,
      recordList: steps.value.map((step) => ({
        processRouteItemId: step.processRouteItemId,
        isCompleted: Number(step.isCompleted ?? 0),
        completedTime: step.completedTime || (step.isCompleted ? (proxy?.parseTime ? proxy.parseTime(new Date()) : formatDateTime(new Date())) : null)
      })),
    });
  } catch (e) {
    proxy?.$modal?.msgError?.("确认失败,请稍后重试");
  } finally {
@@ -201,9 +273,9 @@
}
.process-diagram-node {
  width: 160px;
  min-width: 160px;
  height: 78px;
  width: 180px;
  min-width: 180px;
  min-height: 78px;
  border: 1px solid #ebeef5;
  border-radius: 10px;
  background: #fff;
@@ -213,6 +285,13 @@
  padding: 10px 12px;
  margin-right: 10px;
  box-sizing: border-box;
  position: relative;
}
.process-diagram-checkbox {
  position: absolute;
  top: 8px;
  right: 8px;
}
.process-diagram-index {
@@ -228,6 +307,17 @@
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.process-diagram-status {
  margin-top: 4px;
  font-size: 12px;
  color: #909399;
}
.process-diagram-status.is-done {
  color: #67c23a;
  font-weight: 600;
}
.process-diagram-arrow {
@@ -251,5 +341,11 @@
  justify-content: flex-end;
  gap: 10px;
}
</style>
.dialog-topbar {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
}
</style>