yyb
5 天以前 da004bc93d20874eee8ccd33fd3b246674a4531f
工艺路线绑定
已修改3个文件
210 ■■■■■ 文件已修改
src/api/salesManagement/salesLedger.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/components/ProcessFlowConfigSelectDialog.vue 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/salesManagement/salesLedger.js
@@ -151,4 +151,21 @@
    url: `/salesLedgerProductProcess/delete/${id}`,
    method: "delete",
  });
}
// 销售台账-绑定工艺路线
export function saleProcessBind(data) {
  return request({
    url: "/sales/ledger/saleProcessBind",
    method: "post",
    data,
  });
}
// 销售台账-查询订单已绑定工艺路线
export function getSaleProcessBindInfo(salesLedgerId) {
  return request({
    url: `/sales/ledger/salesProcess/${salesLedgerId}`,
    method: "get",
  });
}
src/views/salesManagement/salesLedger/components/ProcessFlowConfigSelectDialog.vue
@@ -7,33 +7,37 @@
    @close="handleClose"
  >
    <el-row :gutter="20">
      <el-col :span="10">
      <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>
        <el-select
          v-model="selectedConfigId"
          v-model="selectedRouteId"
          filterable
          clearable
          placeholder="请选择工艺路线配置"
          placeholder="请选择工艺路线"
          style="width: 100%;"
          @change="handleConfigChange"
          @change="handleRouteChange"
        >
          <el-option
            v-for="cfg in configList"
            :key="cfg.configId"
            :label="cfg.configName"
            :value="cfg.configId"
            v-for="cfg in routeList"
            :key="cfg.routeId"
            :label="cfg.processRouteName"
            :value="cfg.routeId"
          />
        </el-select>
        <el-divider style="margin: 16px 0;" />
        <div style="font-weight: 600; margin-bottom: 8px;">步骤预览</div>
        <div style="font-size: 12px; color: #909399; margin-bottom: 6px;">
        <div style="font-size: 12px; color: #909399; margin-bottom: 10px;">
          根据所选配置展示流程图
        </div>
      </el-col>
      <el-col :span="14">
      <el-col :span="24">
        <div class="process-diagram">
          <div v-if="steps.length === 0" class="process-diagram-empty">暂无步骤</div>
          <div
@@ -48,7 +52,7 @@
            <div v-if="idx < steps.length - 1" class="process-diagram-arrow">→</div>
          </div>
        </div>
        <div v-if="selectedConfigId === null" style="margin-top: 10px; font-size: 12px; color: #909399;">
        <div v-if="selectedRouteId === null" style="margin-top: 10px; font-size: 12px; color: #909399;">
          请先选择一条已维护好的工艺路线
        </div>
      </el-col>
@@ -67,13 +71,16 @@
<script setup>
import { computed, getCurrentInstance, ref, watch } from "vue";
import { salesProcessFlowConfigList, salesProcessFlowConfigGetById } from "@/api/salesManagement/salesProcessFlowConfig.js";
import { salesProcessFlowConfigList, salesProcessFlowConfigItemList } from "@/api/salesManagement/salesProcessFlowConfig.js";
const emit = defineEmits(["update:visible", "confirm"]);
const props = defineProps({
  visible: { type: Boolean, default: false },
  defaultConfigId: { type: [Number, String, null], default: null },
  // 打开弹窗时的回显:若业务已绑定工艺路线则传入该 routeId;否则默认展示列表第一条
  defaultRouteId: { type: [Number, String, null], default: null },
  // 页面提示:订单已绑定的工艺路线名称
  boundRouteName: { type: String, default: "" },
});
const { proxy } = getCurrentInstance();
@@ -87,8 +94,8 @@
  },
});
const configList = ref([]);
const selectedConfigId = ref(null);
const routeList = ref([]);
const selectedRouteId = ref(null);
const steps = ref([]);
const saving = ref(false);
@@ -102,26 +109,30 @@
  }));
};
const fetchConfigList = async () => {
  const res = await salesProcessFlowConfigList();
  const list = res?.data ?? res?.records ?? res ?? [];
  configList.value = Array.isArray(list) ? list : [];
const normalizeRouteList = (list) => {
  if (!Array.isArray(list)) return [];
  return list.map((r) => ({
    routeId: r.routeId ?? r.id ?? null,
    processRouteName: r.processRouteName ?? r.routeName ?? r.name ?? "",
    isDefault: Boolean(r.isDefault),
  }));
};
const fetchConfigDetail = async (id) => {
  if (!id) {
const fetchRouteList = async () => {
  // 选择弹窗:尽量一次性拉全,避免分页影响选择体验
  const res = await salesProcessFlowConfigList({ current: 1, size: 1000 });
  const records = res?.records ?? res?.data?.records ?? res?.data ?? res ?? [];
  routeList.value = normalizeRouteList(records).filter((r) => r.routeId !== null && r.routeId !== undefined && r.routeId !== "");
};
const fetchRouteSteps = async (routeId) => {
  if (!routeId) {
    steps.value = [];
    return;
  }
  const res = await salesProcessFlowConfigGetById(id);
  const detail = res?.data ?? res ?? {};
  steps.value = normalizeStepsFromApi(detail?.steps ?? []);
};
const initDefault = async () => {
  await fetchConfigList();
  selectedConfigId.value = props.defaultConfigId ?? null;
  await fetchConfigDetail(selectedConfigId.value);
  const res = await salesProcessFlowConfigItemList(routeId);
  const raw = res?.data ?? res ?? [];
  steps.value = normalizeStepsFromApi(raw);
};
watch(
@@ -129,7 +140,18 @@
  async (v) => {
    if (v) {
      try {
        await initDefault();
        await fetchRouteList();
        // 回显绑定:
        // 1. 若传入 defaultRouteId,则优先使用它
        // 2. 否则优先选中标记为默认(isDefault=true)的工艺路线
        // 3. 若都没有,则回退为第一条
        const first = routeList.value?.[0] ?? null;
        const defaultRoute =
          routeList.value.find((r) => r.isDefault) ?? first;
        const desired = props.defaultRouteId ?? (defaultRoute ? defaultRoute.routeId : null);
        selectedRouteId.value = desired ?? null;
        await fetchRouteSteps(selectedRouteId.value);
      } catch {
        proxy?.$modal?.msgError?.("获取工艺路线配置失败");
      }
@@ -137,8 +159,8 @@
  }
);
const handleConfigChange = async () => {
  await fetchConfigDetail(selectedConfigId.value);
const handleRouteChange = async () => {
  await fetchRouteSteps(selectedRouteId.value);
};
const handleClose = () => {
@@ -148,14 +170,13 @@
const confirmSelect = async () => {
  if (saving.value) return;
  if (selectedConfigId.value === null || selectedConfigId.value === undefined || selectedConfigId.value === "") {
    proxy?.$modal?.msgWarning?.("请选择工艺路线配置");
  if (selectedRouteId.value === null || selectedRouteId.value === undefined || selectedRouteId.value === "") {
    proxy?.$modal?.msgWarning?.("请选择工艺路线");
    return;
  }
  saving.value = true;
  try {
    emit("confirm", selectedConfigId.value);
    handleClose();
    emit("confirm", selectedRouteId.value);
  } catch (e) {
    proxy?.$modal?.msgError?.("确认失败,请稍后重试");
  } finally {
src/views/salesManagement/salesLedger/index.vue
@@ -31,6 +31,8 @@
        </div>
        <ProcessFlowConfigSelectDialog
          v-model:visible="processFlowSelectDialogVisible"
          :default-route-id="processFlowSelectDefaultRouteId"
          :bound-route-name="processFlowSelectBoundRouteName"
          @confirm="handleProcessFlowSelectConfirm"
        />
        <div>
@@ -992,12 +994,14 @@
    delLedgerFile,
    getProductInventory,
    salesLedgerProductProcessList,
    saleProcessBind,
    getSaleProcessBindInfo,
} from "@/api/salesManagement/salesLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
import { salesLedgerProductSetProcessFlowConfig } from "@/api/salesManagement/salesProcessFlowConfig.js";
// import { salesLedgerProductSetProcessFlowConfig } from "@/api/salesManagement/salesProcessFlowConfig.js";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
@@ -1020,6 +1024,9 @@
// 工艺路线配置选择弹窗(绑定到台账产品)
const processFlowSelectDialogVisible = ref(false);
const processFlowSelectLedgerRow = ref(null);
const processFlowSelectDefaultRouteId = ref(null);
const processFlowSelectBoundRouteId = ref(null);
const processFlowSelectBoundRouteName = ref("");
// 用户信息表单弹框数据
const operationType = ref("");
@@ -1420,52 +1427,79 @@
};
// 打开“工艺路线配置”选择弹窗(必须显式选择)
const openProcessFlowSelect = (ledgerRow) => {
const openProcessFlowSelect = async (ledgerRow) => {
    if (!ledgerRow) return;
    if (!ledgerRow.isEdit) return;
    processFlowSelectLedgerRow.value = ledgerRow;
    processFlowSelectDefaultRouteId.value = null;
    processFlowSelectBoundRouteId.value = null;
    processFlowSelectBoundRouteName.value = "";
    try {
        const res = await getSaleProcessBindInfo(ledgerRow.id);
        const info = res?.data ?? res ?? {};
        const boundId =
            info?.processRouteId ??
            info?.routeId ??
            info?.id ??
            null;
        const boundName =
            info?.processRouteName ??
            info?.routeName ??
            info?.name ??
            "";
        processFlowSelectBoundRouteId.value = boundId;
        processFlowSelectBoundRouteName.value = boundName;
        processFlowSelectDefaultRouteId.value = boundId;
    } catch (e) {
        // 查询失败时按未绑定处理,不阻塞弹窗
        processFlowSelectBoundRouteId.value = null;
        processFlowSelectBoundRouteName.value = "";
        processFlowSelectDefaultRouteId.value = null;
    }
    processFlowSelectDialogVisible.value = true;
};
// 将配置应用到台账下的所有未发货产品
const handleProcessFlowSelectConfirm = async (configId) => {
// 绑定工艺路线到当前台账数据
const handleProcessFlowSelectConfirm = async (routeId) => {
    const ledgerRow = processFlowSelectLedgerRow.value;
    if (!ledgerRow?.id) return;
    const finalConfigId = configId ?? null;
    if (!finalConfigId) return;
    const finalRouteId = routeId ?? null;
    if (!finalRouteId) return;
    proxy?.$modal?.loading?.("正在应用工艺路线配置,请稍候...");
    try {
        const res = await productList({ salesLedgerId: ledgerRow.id, type: 1 });
        const products = res?.data ?? res?.records ?? res ?? [];
        if (!Array.isArray(products) || products.length === 0) {
            proxy?.$modal?.msgWarning?.("该台账下没有产品数据");
    const oldRouteId = processFlowSelectBoundRouteId.value;
    if (oldRouteId !== null && oldRouteId !== undefined && oldRouteId !== "" && String(oldRouteId) !== String(finalRouteId)) {
        try {
            await ElMessageBox.confirm(
                "该订单已绑定工艺路线,是否确定更换?",
                "提示",
                {
                    confirmButtonText: "确定",
                    cancelButtonText: "取消",
                    type: "warning",
                }
            );
        } catch {
            return;
        }
    }
        for (const product of products) {
            // 已发货/审核通过不可编辑,跳过
            if (isProductShipped(product)) continue;
            await salesLedgerProductSetProcessFlowConfig({
                salesLedgerProductId: product.id,
                processFlowConfigId: finalConfigId,
            });
        }
    proxy?.$modal?.loading?.("正在绑定工艺路线,请稍候...");
    try {
        await saleProcessBind({
            salesLedgerId: ledgerRow.id,
            processRouteId: finalRouteId,
        });
        proxy?.$modal?.msgSuccess?.("工艺路线配置应用成功");
        // 若当前行已展开,刷新其子产品列表
        if (expandedRowKeys.value.includes(ledgerRow.id)) {
            const childRes = await productList({ salesLedgerId: ledgerRow.id, type: 1 });
            const children = childRes?.data ?? childRes?.records ?? childRes ?? [];
            const index = tableData.value.findIndex((item) => item.id === ledgerRow.id);
            if (index > -1) {
                tableData.value[index].children = children;
            }
        }
        proxy?.$modal?.msgSuccess?.("工艺路线绑定成功");
        processFlowSelectDialogVisible.value = false;
        // 绑定后刷新列表,确保操作列再次点击能回显绑定
        await getList();
    } catch (e) {
        proxy?.$modal?.msgError?.("应用失败,请稍后重试");
        proxy?.$modal?.msgError?.("绑定失败,请稍后重试");
    } finally {
        proxy?.$modal?.closeLoading?.();
    }