中兴实强
1.新增工艺路线的时候产品可以多选
2.绑定工艺路线时,可以删减工序并且每道工序可以多选报工人
3.报工列表展示报工人,非本订单报工人不可报工
其他
已修改4个文件
1784 ■■■■■ 文件已修改
src/views/basicData/product/ProductSelectDialog.vue 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/processRouteItem/index.vue 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/New.vue 450 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 977 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/ProductSelectDialog.vue
@@ -18,7 +18,7 @@
    <!-- 列表 -->
    <el-table ref="tableRef" v-loading="loading" :data="tableData" height="420" highlight-current-row row-key="id"
      @selection-change="handleSelectionChange" @select="handleSelect">
      <el-table-column type="selection" width="55" />
      <el-table-column type="selection" width="55" :reserve-selection="true" />
      <el-table-column type="index" label="序号" width="60" />
      <el-table-column prop="productName" label="产品大类" min-width="160" />
      <el-table-column prop="model" label="型号名称" min-width="200" />
@@ -54,7 +54,7 @@
const props = defineProps<{
  modelValue: boolean;
  single?: boolean; // 是否只能选择一个,默认false(可选择多个)
  single?: boolean;
}>();
const emit = defineEmits(['update:modelValue', 'confirm']);
@@ -78,46 +78,68 @@
const tableData = ref<ProductRow[]>([]);
const total = ref(0);
const multipleSelection = ref<ProductRow[]>([]);
const selectedMap = ref<Map<number, ProductRow>>(new Map());
const tableRef = ref();
function close() {
  visible.value = false;
}
function syncCurrentPageSelection() {
  nextTick(() => {
    if (!tableRef.value) {
      return;
    }
    tableData.value.forEach((item) => {
      tableRef.value.toggleRowSelection(item, selectedMap.value.has(item.id));
    });
  });
}
const handleSelectionChange = (val: ProductRow[]) => {
  if (props.single && val.length > 1) {
    // 如果限制为单个选择,只保留最后一个选中的
    const lastSelected = val[val.length - 1];
    multipleSelection.value = [lastSelected];
    // 清空表格选中状态,然后重新选中最后一个
    selectedMap.value = new Map(lastSelected ? [[lastSelected.id, lastSelected]] : []);
    nextTick(() => {
      if (tableRef.value) {
        tableRef.value.clearSelection();
        tableRef.value.toggleRowSelection(lastSelected, true);
        if (lastSelected) {
          tableRef.value.toggleRowSelection(lastSelected, true);
        }
      }
    });
  } else {
    multipleSelection.value = val;
    return;
  }
  const currentPageIds = new Set(tableData.value.map((item) => item.id));
  currentPageIds.forEach((id) => {
    selectedMap.value.delete(id);
  });
  val.forEach((item) => {
    selectedMap.value.set(item.id, item);
  });
  multipleSelection.value = Array.from(selectedMap.value.values());
}
// 处理单个选择
const handleSelect = (selection: ProductRow[], row: ProductRow) => {
  if (props.single) {
    // 如果限制为单个,清空其他选择,只保留当前行
    if (selection.includes(row)) {
      // 选中当前行时,清空其他选中
      multipleSelection.value = [row];
      nextTick(() => {
        if (tableRef.value) {
          tableData.value.forEach((item) => {
            if (item.id !== row.id) {
              tableRef.value.toggleRowSelection(item, false);
            }
          });
        }
      });
    }
  if (!props.single) {
    return;
  }
  if (selection.includes(row)) {
    multipleSelection.value = [row];
    selectedMap.value = new Map([[row.id, row]]);
    nextTick(() => {
      if (tableRef.value) {
        tableData.value.forEach((item) => {
          if (item.id !== row.id) {
            tableRef.value.toggleRowSelection(item, false);
          }
        });
      }
    });
  }
}
@@ -139,7 +161,7 @@
function onConfirm() {
  if (multipleSelection.value.length === 0) {
    ElMessage.warning("请选择一条产品");
    ElMessage.warning("请选择产品");
    return;
  }
  if (props.single && multipleSelection.value.length > 1) {
@@ -153,24 +175,26 @@
async function loadData() {
  loading.value = true;
  try {
    multipleSelection.value = []; // 翻页/搜索后清空选择更符合预期
    const res: any = await productModelList({
      productName: query.productName.trim(),
      model: query.model.trim(),
      current: page.pageNum,
      size: page.pageSize,
    });
    tableData.value = res.records;
    total.value = res.total;
    tableData.value = res.records || [];
    total.value = res.total || 0;
    syncCurrentPageSelection();
  } finally {
    loading.value = false;
  }
}
// 监听弹窗打开,重置选择
watch(() => props.modelValue, (visible) => {
  if (visible) {
    multipleSelection.value = [];
    selectedMap.value = new Map();
    page.pageNum = 1;
    loadData();
  }
});
src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -179,18 +179,27 @@
          </el-select>
        </el-form-item>
        <el-form-item label="产品名称" prop="productModelId">
        <el-form-item label="产品名称" prop="selectedProducts">
          <el-button type="primary" @click="showProductSelectDialog = true">
            {{ form.productName && form.model
              ? `${form.productName} - ${form.model}`
              : '选择产品' }}
            {{ form.selectedProducts.length ? '重新选择产品' : '选择产品' }}
          </el-button>
          <div v-if="form.selectedProducts.length" class="selected-product-tags">
            <el-tag
              v-for="product in form.selectedProducts"
              :key="product.id"
              class="selected-product-tag"
              type="info"
              effect="plain"
            >
              {{ product.productName }} - {{ product.model }}
            </el-tag>
          </div>
        </el-form-item>
        <el-form-item label="单位" prop="unit">
          <el-input 
              v-model="form.unit" 
              :placeholder="form.productModelId ? '根据选择的产品自动带出' : '请先选择产品'"
              :placeholder="form.selectedProducts.length === 1 ? '根据选择的产品自动带出' : '多个产品时不展示单个单位'"
              clearable 
              :disabled="true" 
          />
@@ -211,7 +220,6 @@
    <ProductSelectDialog
        v-model="showProductSelectDialog"
        @confirm="handleProductSelect"
        single
    />
  </div>
</template>
@@ -269,6 +277,8 @@
  routeId: routeId.value,
  processId: undefined,
  productModelId: undefined,
  productModelIds: "",
  selectedProducts: [],
  productName: "",
  model: "",
  unit: "",
@@ -277,94 +287,118 @@
const rules = {
  processId: [{ required: true, message: '请选择工序', trigger: 'change' }],
  productModelId: [{ required: true, message: '请选择产品', trigger: 'change' }],
  selectedProducts: [{
    required: true,
    validator: (_, value, callback) => {
      if (Array.isArray(value) && value.length > 0) {
        callback();
        return;
      }
      callback(new Error('请选择产品'));
    },
    trigger: 'change',
  }],
};
// 根据工序ID获取工序名称
const getProcessName = (processId) => {
  if (!processId) return '';
  const process = processOptions.value.find(p => p.id === processId);
  const process = processOptions.value.find((p) => p.id === processId);
  return process ? process.name : '';
};
// 获取列表
const getList = () => {
  tableLoading.value = true;
  const listPromise =
    pageType.value === "order"
    pageType.value === 'order'
      ? findProductProcessRouteItemList({ orderId: orderId.value })
      : findProcessRouteItemList({ routeId: routeId.value });
  listPromise
    .then(res => {
    .then((res) => {
      tableData.value = res.data || [];
      tableLoading.value = false;
      // 列表加载完成后初始化拖拽排序
      nextTick(() => {
        initSortable();
      });
    })
    .catch(err => {
    .catch((err) => {
      tableLoading.value = false;
      console.error("获取列表失败:", err);
      proxy?.$modal?.msgError("获取列表失败");
      console.error('获取列表失败:', err);
      proxy?.$modal?.msgError('获取列表失败');
    });
};
// 获取工序列表
const getProcessList = () => {
  processList({})
    .then(res => {
    .then((res) => {
      processOptions.value = res.data || [];
    })
    .catch(err => {
      console.error("获取工序失败:", err);
    .catch((err) => {
      console.error('获取工序失败:', err);
    });
};
// 获取工艺路线详情(从路由参数获取)
const getRouteInfo = () => {
  routeInfo.value = {
    processRouteCode: route.query.processRouteCode || '',
    productName: route.query.productName || '',
    model: route.query.model || '',
    bomNo: route.query.bomNo || '',
    description: route.query.description || ''
    description: route.query.description || '',
  };
};
// 新增
const getEditSelectedProducts = (row) => {
  const idList = String(row.productModelIds || row.productModelId || '')
    .split(',')
    .map((item) => item.trim())
    .filter(Boolean);
  const nameList = String(row.productName || '')
    .split(',')
    .map((item) => item.trim());
  const modelList = String(row.model || '')
    .split(',')
    .map((item) => item.trim());
  return idList.map((id, index) => ({
    id: Number(id) || id,
    productName: nameList[index] || row.productName || '',
    model: modelList[index] || row.model || '',
    unit: row.unit || '',
  }));
};
const handleAdd = () => {
  operationType.value = 'add';
  resetForm();
  dialogVisible.value = true;
};
// 编辑
const handleEdit = (row) => {
  operationType.value = 'edit';
  const selectedProducts = getEditSelectedProducts(row);
  form.value = {
    id: row.id,
    routeId: routeId.value,
    processId: row.processId,
    productModelId: row.productModelId,
    productName: row.productName || "",
    model: row.model || "",
    unit: row.unit || "",
    productModelIds: row.productModelIds || (row.productModelId ? String(row.productModelId) : ''),
    selectedProducts,
    productName: row.productName || '',
    model: row.model || '',
    unit: row.unit || '',
    isQuality: row.isQuality,
  };
  dialogVisible.value = true;
};
// 删除
const handleDelete = (row) => {
  ElMessageBox.confirm('确认删除该工艺路线项目?', '提示', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning'
    type: 'warning',
  })
    .then(() => {
      // 生产订单下使用 productProcessRoute 的删除接口(路由后拼接 id),其它情况使用工艺路线项目批量删除接口
      const deletePromise =
        pageType.value === 'order'
          ? deleteRouteItem(row.id)
@@ -382,112 +416,115 @@
    .catch(() => {});
};
// 产品选择
const handleProductSelect = (products) => {
  if (products && products.length > 0) {
    const product = products[0];
    form.value.productModelId = product.id;
    form.value.productName = product.productName;
    form.value.model = product.model;
    form.value.unit = product.unit || "";
    const firstProduct = products[0];
    form.value.selectedProducts = products;
    form.value.productModelIds = products.map((item) => item.id).join(',');
    form.value.productModelId = products.length === 1 ? firstProduct.id : undefined;
    form.value.productName = products.length === 1 ? firstProduct.productName : '';
    form.value.model = products.length === 1 ? firstProduct.model : '';
    form.value.unit = products.length === 1 ? (firstProduct.unit || '') : '';
    showProductSelectDialog.value = false;
    // 触发表单验证
    formRef.value?.validateField('productModelId');
    formRef.value?.validateField('selectedProducts');
  }
};
// 提交
const handleSubmit = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      submitLoading.value = true;
      if (operationType.value === 'add') {
        // 新增:传单个对象,包含dragSort字段
        // dragSort = 当前列表长度 + 1,表示新增记录排在最后
        const dragSort = tableData.value.length + 1;
        const isOrderPage = pageType.value === 'order';
        const addPromise = isOrderPage
          ? addRouteItem({
              productOrderId: orderId.value,
              productRouteId: routeId.value,
              processId: form.value.processId,
              productModelId: form.value.productModelId,
              isQuality: form.value.isQuality,
              dragSort,
            })
          : addOrUpdateProcessRouteItem({
              routeId: routeId.value,
              processId: form.value.processId,
              productModelId: form.value.productModelId,
              isQuality: form.value.isQuality,
              dragSort,
            });
        addPromise
          .then(() => {
            proxy?.$modal?.msgSuccess('新增成功');
            closeDialog();
            getList();
          })
          .catch(() => {
            proxy?.$modal?.msgError('新增失败');
          })
          .finally(() => {
            submitLoading.value = false;
          });
      } else {
        // 编辑:生产订单下使用 productProcessRoute/updateRouteItem,其它情况使用工艺路线项目更新接口
        const isOrderPage = pageType.value === 'order';
        const updatePromise = isOrderPage
          ? addOrUpdateProductProcessRouteItem({
              id: form.value.id,
              processId: form.value.processId,
              productModelId: form.value.productModelId,
              isQuality: form.value.isQuality,
            })
          : addOrUpdateProcessRouteItem({
              routeId: routeId.value,
              processId: form.value.processId,
              productModelId: form.value.productModelId,
              id: form.value.id,
              isQuality: form.value.isQuality,
            });
        updatePromise
          .then(() => {
            proxy?.$modal?.msgSuccess('修改成功');
            closeDialog();
            getList();
          })
          .catch(() => {
            proxy?.$modal?.msgError('修改失败');
          })
          .finally(() => {
            submitLoading.value = false;
          });
      }
    if (!valid) {
      return;
    }
    submitLoading.value = true;
    if (operationType.value === 'add') {
      const dragSort = tableData.value.length + 1;
      const isOrderPage = pageType.value === 'order';
      const addPromise = isOrderPage
        ? addRouteItem({
            productOrderId: orderId.value,
            productRouteId: routeId.value,
            processId: form.value.processId,
            productModelId: form.value.productModelId,
            productModelIds: form.value.productModelIds,
            isQuality: form.value.isQuality,
            dragSort,
          })
        : addOrUpdateProcessRouteItem({
            routeId: routeId.value,
            processId: form.value.processId,
            productModelId: form.value.productModelId,
            productModelIds: form.value.productModelIds,
            isQuality: form.value.isQuality,
            dragSort,
          });
      addPromise
        .then(() => {
          proxy?.$modal?.msgSuccess('新增成功');
          closeDialog();
          getList();
        })
        .catch(() => {
          proxy?.$modal?.msgError('新增失败');
        })
        .finally(() => {
          submitLoading.value = false;
        });
      return;
    }
    const isOrderPage = pageType.value === 'order';
    const updatePromise = isOrderPage
      ? addOrUpdateProductProcessRouteItem({
          id: form.value.id,
          processId: form.value.processId,
          productModelId: form.value.productModelId,
          productModelIds: form.value.productModelIds,
          isQuality: form.value.isQuality,
        })
      : addOrUpdateProcessRouteItem({
          routeId: routeId.value,
          processId: form.value.processId,
          productModelId: form.value.productModelId,
          productModelIds: form.value.productModelIds,
          id: form.value.id,
          isQuality: form.value.isQuality,
        });
    updatePromise
      .then(() => {
        proxy?.$modal?.msgSuccess('修改成功');
        closeDialog();
        getList();
      })
      .catch(() => {
        proxy?.$modal?.msgError('修改失败');
      })
      .finally(() => {
        submitLoading.value = false;
      });
  });
};
// 重置表单
const resetForm = () => {
  form.value = {
    id: undefined,
    routeId: routeId.value,
    processId: undefined,
    productModelId: undefined,
    productName: "",
    model: "",
    unit: "",
    productModelIds: '',
    selectedProducts: [],
    productName: '',
    model: '',
    unit: '',
    isQuality: false,
  };
  formRef.value?.resetFields();
};
// 关闭弹窗
const closeDialog = () => {
  dialogVisible.value = false;
  resetForm();
@@ -753,6 +790,18 @@
  margin: 10px 0;
}
.selected-product-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}
.selected-product-tag {
  max-width: 100%;
}
.card-footer {
  display: flex;
  justify-content: space-around;
src/views/productionManagement/productionOrder/New.vue
@@ -1,68 +1,137 @@
<template>
  <div>
    <el-dialog
        v-model="isShow"
        title="新增生产订单"
        width="800"
        @close="closeModal"
      v-model="isShow"
      title="新增生产订单"
      width="800"
      @close="closeModal"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
      <el-form
        ref="formRef"
        :model="formState"
        label-width="140px"
        label-position="top"
      >
        <el-form-item
            label="产品名称"
            prop="productModelId"
            :rules="[
                {
                required: true,
                message: '请选择产品',
                trigger: 'change',
              }
            ]"
          label="产品名称"
          prop="productModelId"
          :rules="[
            {
              required: true,
              message: '请选择产品',
              trigger: 'change',
            },
          ]"
        >
          <el-button type="primary" @click="showProductSelectDialog = true">
            {{ formState.productName ? formState.productName : '选择产品' }}
            {{ formState.productName || "选择产品" }}
          </el-button>
        </el-form-item>
        <el-form-item
            label="规格"
            prop="productModelName"
        >
          <el-input v-model="formState.productModelName"  disabled />
        <el-form-item label="规格" prop="productModelName">
          <el-input v-model="formState.productModelName" disabled />
        </el-form-item>
        <el-form-item label="单位" prop="unit">
          <el-input v-model="formState.unit" disabled />
        </el-form-item>
        <el-form-item
            label="单位"
            prop="unit"
          label="工艺路线"
          prop="routeId"
          :rules="[
            {
              required: true,
              message: '请选择工艺路线',
              trigger: 'change',
            },
          ]"
        >
          <el-input v-model="formState.unit"  disabled />
        </el-form-item>
        <el-form-item label="工艺路线">
          <el-select v-model="formState.routeId"
                     placeholder="请选择工艺路线"
                     style="width: 100%;"
                     :loading="bindRouteLoading">
            <el-option v-for="item in routeOptions"
                       :key="item.id"
                       :label="`${item.processRouteCode || ''}`"
                       :value="item.id" />
          <el-select
            v-model="formState.routeId"
            placeholder="请选择工艺路线"
            style="width: 100%"
            :loading="bindRouteLoading"
            @change="handleRouteChange"
          >
            <el-option
              v-for="item in routeOptions"
              :key="item.id"
              :label="item.processRouteCode || ''"
              :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item
            label="需求数量"
            prop="quantity"
          v-if="processListData.length"
          label="工序报工人"
          prop="processUserList"
          :rules="[
            {
              validator: validateProcessUsers,
              trigger: 'change',
            },
          ]"
        >
          <el-input-number v-model="formState.quantity" :step="1" :min="1" style="width: 100%" />
          <div class="process-user-list">
            <div
              v-for="(item, index) in processListData"
              :key="item.id || `${item.processId}-${index}`"
              class="process-user-item"
            >
              <div class="process-user-header">
                <div class="process-user-name">
                  {{ item.name || item.processName || item.no || `工序${index + 1}` }}
                </div>
                <el-button
                  type="danger"
                  link
                  class="process-user-remove"
                  @click="removeProcessItem(index)"
                >
                  删除
                </el-button>
              </div>
              <el-select
                v-model="formState.processUserList[index].userIds"
                placeholder="请选择报工人"
                class="process-user-select"
                filterable
                clearable
                multiple
                collapse-tags
                collapse-tags-tooltip
                :max-collapse-tags="3"
                @change="handleProcessUserChange(index, $event)"
              >
                <el-option
                  v-for="user in userOptions"
                  :key="user.userId"
                  :label="user.nickName"
                  :value="user.userId"
                />
              </el-select>
            </div>
          </div>
        </el-form-item>
        <el-form-item label="需求数量" prop="quantity">
          <el-input-number
            v-model="formState.quantity"
            :step="1"
            :min="1"
            style="width: 100%"
          />
        </el-form-item>
      </el-form>
      <!-- 产品选择弹窗 -->
      <ProductSelectDialog
          v-model="showProductSelectDialog"
          @confirm="handleProductSelect"
          single
        v-model="showProductSelectDialog"
        single
        @confirm="handleProductSelect"
      />
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
@@ -74,115 +143,234 @@
</template>
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import { computed, getCurrentInstance, nextTick, ref } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {addProductOrder, listProcessRoute} from "@/api/productionManagement/productionOrder.js";
import { addProductOrder, listProcessRoute } from "@/api/productionManagement/productionOrder.js";
import { processList } from "@/api/productionManagement/productionProcess.js";
import { userListNoPageByTenantId } from "@/api/system/user.js";
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
  },
  type: {
    type: String,
    required: true,
    default: 'qualified',
    default: "qualified",
  },
});
const emit = defineEmits(['update:visible', 'completed']);
const emit = defineEmits(["update:visible", "completed"]);
// 响应式数据(替代选项式的 data)
const formState = ref({
const createDefaultFormState = () => ({
  productId: undefined,
  productModelId: undefined,
  routeId: undefined,
  productName: "",
  productModelName: "",
  unit: "",
  quantity: 0,
  quantity: 1,
  processUserList: [],
});
const formState = ref(createDefaultFormState());
const isShow = computed({
  get() {
    return props.visible;
  },
  set(val) {
    emit('update:visible', val);
    emit("update:visible", val);
  },
});
const showProductSelectDialog = ref(false);
const routeOptions = ref([]);
const bindRouteLoading = ref(false);
const processListData = ref([]);
const userOptions = ref([]);
const userLoading = ref(false);
let { proxy } = getCurrentInstance()
const { proxy } = getCurrentInstance();
const formRef = ref();
const validateProcessUserField = async () => {
  await nextTick();
  if (!formRef.value) return;
  if (processListData.value.length) {
    formRef.value.validateField("processUserList");
    return;
  }
  formRef.value.clearValidate("processUserList");
};
const resetProcessUsers = () => {
  processListData.value = [];
  formState.value.processUserList = [];
};
const closeModal = () => {
  // 重置表单数据
  formState.value = {
    productId: undefined,
    productModelId: undefined,
    routeId: undefined,
    productName: "",
    productModelName: "",
    quantity: '',
  };
  formState.value = createDefaultFormState();
  routeOptions.value = [];
  resetProcessUsers();
  isShow.value = false;
};
// 产品选择处理
const handleProductSelect = async (products) => {
  if (products && products.length > 0) {
    const product = products[0];
    formState.value.productId = product.productId;
    formState.value.productName = product.productName;
    formState.value.productModelName = product.model;
    formState.value.productModelId = product.id;
    formState.value.unit = product.unit;
    showProductSelectDialog.value = false;
    fetchRouteOptions( product.id);
    // 触发表单验证更新
    proxy.$refs["formRef"]?.validateField('productModelId');
  }
const ensureUserOptions = () => {
  if (userOptions.value.length || userLoading.value) return;
  userLoading.value = true;
  userListNoPageByTenantId()
    .then(res => {
      userOptions.value = res.data || [];
    })
    .finally(() => {
      userLoading.value = false;
    });
};
const routeOptions = ref([]);
const bindRouteLoading = ref(false);
const fetchRouteOptions = (productModelId) => {
const createProcessUserList = list =>
  list.map(item => ({
    processId: item.id,
    processName: item.name || item.processName || item.no || "",
    userIds: [],
    userNames: "",
  }));
const fetchProcessList = routeId => {
  if (!routeId) {
    resetProcessUsers();
    return;
  }
  processList({ routeId })
    .then(res => {
      processListData.value = res.data || [];
      formState.value.processUserList = createProcessUserList(processListData.value);
      ensureUserOptions();
      validateProcessUserField();
    })
    .catch(() => {
      resetProcessUsers();
    });
};
const handleRouteChange = routeId => {
  fetchProcessList(routeId);
};
const handleProcessUserChange = (index, userIds) => {
  const selectedUsers = userOptions.value.filter(user => Array.isArray(userIds) && userIds.includes(user.userId));
  formState.value.processUserList[index].userNames = selectedUsers.map(user => user.nickName).join(",");
  validateProcessUserField();
};
const removeProcessItem = index => {
  processListData.value.splice(index, 1);
  formState.value.processUserList.splice(index, 1);
  validateProcessUserField();
};
const validateProcessUsers = (_, value, callback) => {
  if (!formState.value.routeId) {
    callback();
    return;
  }
  if (!processListData.value.length) {
    callback(new Error("当前工艺路线下没有工序"));
    return;
  }
  if (!Array.isArray(value) || value.length !== processListData.value.length) {
    callback(new Error("请为每道工序选择报工人"));
    return;
  }
  const hasEmptyUser = value.some(item => !Array.isArray(item.userIds) || item.userIds.length === 0);
  if (hasEmptyUser) {
    callback(new Error("请为每道工序选择报工人"));
    return;
  }
  callback();
};
const handleProductSelect = products => {
  if (!products?.length) return;
  const product = products[0];
  formState.value.productId = product.productId;
  formState.value.productName = product.productName;
  formState.value.productModelName = product.model;
  formState.value.productModelId = product.id;
  formState.value.unit = product.unit;
  formState.value.routeId = undefined;
  routeOptions.value = []
  routeOptions.value = [];
  resetProcessUsers();
  showProductSelectDialog.value = false;
  fetchRouteOptions(product.id);
  formRef.value?.validateField("productModelId");
};
const fetchRouteOptions = productModelId => {
  formState.value.routeId = undefined;
  routeOptions.value = [];
  resetProcessUsers();
  bindRouteLoading.value = true;
  listProcessRoute({ productModelId: productModelId }).then(res => {
    routeOptions.value = res.data || [];
  }).finally(() => {
    bindRouteLoading.value = false;
  })
}
  listProcessRoute({ productModelId })
    .then(res => {
      routeOptions.value = res.data || [];
    })
    .finally(() => {
      bindRouteLoading.value = false;
    });
};
const buildProcessRouteItems = () =>
  processListData.value.map((item, index) => {
    const processUser = formState.value.processUserList[index] || {};
    return {
      productOrderId: undefined,
      productRouteId: formState.value.routeId,
      processId: item.id,
      productModelId: formState.value.productModelId,
      dragSort: item.dragSort ?? index + 1,
      isQuality: item.isQuality ?? false,
      reportUserIds: Array.isArray(processUser.userIds) ? processUser.userIds.join(",") : "",
    };
  });
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      // 验证是否选择了产品和规格
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择产品");
        return;
      }
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择规格");
        return;
      }
  formRef.value.validate(valid => {
    if (!valid) return;
      addProductOrder(formState.value).then(res => {
        // 关闭模态框
        isShow.value = false;
        // 告知父组件已完成
        emit('completed');
        proxy.$modal.msgSuccess("提交成功");
      })
    if (!formState.value.productModelId) {
      proxy.$modal.msgError("请选择产品");
      return;
    }
  })
};
    if (!formState.value.routeId) {
      proxy.$modal.msgError("请选择工艺路线");
      return;
    }
    if (!formState.value.processUserList.length) {
      proxy.$modal.msgError("当前工艺路线下没有工序");
      return;
    }
    if (formState.value.processUserList.some(item => !Array.isArray(item.userIds) || item.userIds.length === 0)) {
      proxy.$modal.msgError("请为每道工序选择报工人");
      return;
    }
    addProductOrder({
      ...formState.value,
      processRouteItems: buildProcessRouteItems(),
    }).then(() => {
      isShow.value = false;
      emit("completed");
      proxy.$modal.msgSuccess("提交成功");
    });
  });
};
defineExpose({
  closeModal,
@@ -190,3 +378,55 @@
  isShow,
});
</script>
<style scoped>
.process-user-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 14px;
  border-radius: 12px;
  background: #f7f9fc;
  border: 1px solid #e8eef5;
}
.process-user-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  padding: 12px 14px;
  border-radius: 10px;
  background: #fff;
  border: 1px solid #edf2f7;
}
.process-user-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.process-user-name {
  color: #1f2d3d;
  font-weight: 500;
  line-height: 1.4;
}
.process-user-remove {
  flex-shrink: 0;
  padding: 0;
}
.process-user-select {
  width: 100%;
}
@media (max-width: 768px) {
  .process-user-item {
    grid-template-columns: 1fr;
    gap: 10px;
  }
}
</style>
src/views/productionManagement/productionOrder/index.vue
@@ -1,43 +1,49 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="客户名称:">
          <el-input v-model="searchForm.customerName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery" />
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="客户名称">
          <el-input
            v-model="searchForm.customerName"
            placeholder="请输入"
            clearable
            :prefix-icon="Search"
            style="width: 160px;"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="合同号:">
          <el-input v-model="searchForm.salesContractNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery" />
        <el-form-item label="销售合同号">
          <el-input
            v-model="searchForm.salesContractNo"
            placeholder="请输入"
            clearable
            :prefix-icon="Search"
            style="width: 160px;"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="产品名称:">
          <el-input v-model="searchForm.productCategory"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery" />
        <el-form-item label="产品名称">
          <el-input
            v-model="searchForm.productCategory"
            placeholder="请输入"
            clearable
            :prefix-icon="Search"
            style="width: 160px;"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="规格:">
          <el-input v-model="searchForm.specificationModel"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery" />
        <el-form-item label="规格型号">
          <el-input
            v-model="searchForm.specificationModel"
            placeholder="请输入"
            clearable
            :prefix-icon="Search"
            style="width: 160px;"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">搜索</el-button>
          <el-button type="primary" @click="handleQuery">查询</el-button>
        </el-form-item>
      </el-form>
      <div>
@@ -46,16 +52,19 @@
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :tableLoading="tableLoading"
                :row-class-name="tableRowClassName"
                :isSelection="true"
                @selection-change="handleSelectionChange"
                @pagination="pagination">
      <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :tableLoading="tableLoading"
        :row-class-name="tableRowClassName"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        @pagination="pagination"
      >
        <template #completionStatus="{ row }">
          <el-progress
            :percentage="toProgressPercentage(row?.completionStatus)"
@@ -65,409 +74,591 @@
        </template>
      </PIMTable>
    </div>
    <el-dialog v-model="bindRouteDialogVisible"
               title="绑定工艺路线"
               width="500px">
    <el-dialog
      v-model="bindRouteDialogVisible"
      title="绑定工艺路线"
      width="700px"
    >
      <el-form label-width="90px">
        <el-form-item label="工艺路线">
          <el-select v-model="bindForm.routeId"
                     placeholder="请选择工艺路线"
                     style="width: 100%;"
                     :loading="bindRouteLoading">
            <el-option v-for="item in routeOptions"
                       :key="item.id"
                       :label="`${item.processRouteCode || ''}`"
                       :value="item.id" />
          <el-select
            v-model="bindForm.routeId"
            placeholder="请选择工艺路线"
            style="width: 100%;"
            :loading="bindRouteLoading"
            @change="handleBindRouteChange"
          >
            <el-option
              v-for="item in routeOptions"
              :key="item.id"
              :label="item.processRouteCode || ''"
              :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item v-if="bindProcessList.length" label="报工人员">
          <div class="process-user-list">
            <div
              v-for="(item, index) in bindProcessList"
              :key="item.id || `${item.processId}-${index}`"
              class="process-user-item"
            >
              <div class="process-user-header">
                <div class="process-user-name">
                  {{ item.name || item.processName || item.no || `工序${index + 1}` }}
                </div>
                <el-button
                  type="danger"
                  link
                  class="process-user-remove"
                  @click="removeBindProcessItem(index)"
                >
                  删除
                </el-button>
              </div>
              <el-select
                v-model="bindForm.processUserList[index].userIds"
                class="process-user-select"
                placeholder="请选择报工人员"
                filterable
                clearable
                multiple
                collapse-tags
                collapse-tags-tooltip
                :max-collapse-tags="3"
                @change="handleBindProcessUserChange(index, $event)"
              >
                <el-option
                  v-for="user in userOptions"
                  :key="user.userId"
                  :label="user.nickName"
                  :value="user.userId"
                />
              </el-select>
            </div>
          </div>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     :loading="bindRouteSaving"
                     @click="handleBindRouteConfirm">确 认</el-button>
          <el-button @click="bindRouteDialogVisible = false">取 消</el-button>
          <el-button
            type="primary"
            :loading="bindRouteSaving"
            @click="handleBindRouteConfirm"
          >
            确认
          </el-button>
          <el-button @click="bindRouteDialogVisible = false">取消</el-button>
        </span>
      </template>
    </el-dialog>
    <new-product-order v-if="isShowNewModal"
                         v-model:visible="isShowNewModal"
                         @completed="handleQuery" />
    <new-product-order
      v-if="isShowNewModal"
      v-model:visible="isShowNewModal"
      @completed="handleQuery"
    />
  </div>
</template>
<script setup>
  import { onMounted, ref } from "vue";
  import { ElMessageBox } from "element-plus";
  import dayjs from "dayjs";
  import { useRouter } from "vue-router";
  import {
    productOrderListPage,
    listProcessRoute,
    bindingRoute,
    listProcessBom, delProductOrder,
  } from "@/api/productionManagement/productionOrder.js";
  import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
  import {fileDel} from "@/api/financialManagement/revenueManagement.js";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
import { defineAsyncComponent, getCurrentInstance, onMounted, reactive, ref, toRefs } from "vue";
import { ElMessageBox } from "element-plus";
import { Search } from "@element-plus/icons-vue";
import dayjs from "dayjs";
import { useRouter } from "vue-router";
import {
  bindingRoute,
  delProductOrder,
  listProcessRoute,
  productOrderListPage,
} from "@/api/productionManagement/productionOrder.js";
import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
import { processList } from "@/api/productionManagement/productionProcess.js";
import { userListNoPageByTenantId } from "@/api/system/user.js";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
  const { proxy } = getCurrentInstance();
const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
  const router = useRouter();
  const isShowNewModal = ref(false);
const { proxy } = getCurrentInstance();
const router = useRouter();
const isShowNewModal = ref(false);
  const tableColumn = ref([
    {
      label: "生产订单号",
      prop: "npsNo",
      width: '120px',
    },
    {
      label: "销售合同号",
      prop: "salesContractNo",
      width: '150px',
    },
    {
      label: "客户名称",
      prop: "customerName",
      width: '200px',
    },
    {
      label: "产品名称",
      prop: "productCategory",
      width: '120px',
    },
    {
      label: "规格",
      prop: "specificationModel",
      width: '120px',
    },
    {
      label: "工艺路线编号",
      prop: "processRouteCode",
      width: '200px',
    },
    {
      label: "需求数量",
      prop: "quantity",
    },
    {
      label: "完成数量",
      prop: "completeQuantity",
    },
    {
      dataType: "slot",
      label: "完成进度",
      prop: "completionStatus",
      slot: "completionStatus",
      width: 180,
    },
    {
      label: "开始日期",
      prop: "startTime",
      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
      width: 120,
    },
    {
      label: "结束日期",
      prop: "endTime",
      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
      width: 120,
    },
    {
      label: "交付日期",
      prop: "deliveryDate",
      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
      width: 120,
    },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: 200,
      operation: [
        {
          name: "工艺路线",
          type: "text",
          clickFun: row => {
            showRouteItemModal(row);
          },
const tableColumn = ref([
  {
    label: "生产订单号",
    prop: "npsNo",
    width: "120px",
  },
  {
    label: "销售合同号",
    prop: "salesContractNo",
    width: "150px",
  },
  {
    label: "客户名称",
    prop: "customerName",
    width: "200px",
  },
  {
    label: "产品名称",
    prop: "productCategory",
    width: "120px",
  },
  {
    label: "规格型号",
    prop: "specificationModel",
    width: "120px",
  },
  {
    label: "工艺路线编号",
    prop: "processRouteCode",
    width: "200px",
  },
  {
    label: "需求数量",
    prop: "quantity",
  },
  {
    label: "完成数量",
    prop: "completeQuantity",
  },
  {
    dataType: "slot",
    label: "完成进度",
    prop: "completionStatus",
    slot: "completionStatus",
    width: 180,
  },
  {
    label: "开始日期",
    prop: "startTime",
    formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    width: 120,
  },
  {
    label: "结束日期",
    prop: "endTime",
    formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    width: 120,
  },
  {
    label: "交付日期",
    prop: "deliveryDate",
    formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    width: 120,
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 200,
    operation: [
      {
        name: "工艺路线",
        type: "text",
        clickFun: row => {
          showRouteItemModal(row);
        },
        {
          name: "绑定工艺路线",
          type: "text",
          showHide: row => !row.processRouteCode,
          clickFun: row => {
            openBindRouteDialog(row);
          },
      },
      {
        name: "绑定工艺路线",
        type: "text",
        showHide: row => !row.processRouteCode,
        clickFun: row => {
          openBindRouteDialog(row);
        },
        {
          name: "产品结构",
          type: "text",
          clickFun: row => {
            showProductStructure(row);
          },
      },
      {
        name: "产品结构",
        type: "text",
        clickFun: row => {
          showProductStructure(row);
        },
      ],
    },
  ]);
  const tableData = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  const selectedRows = ref([]);
      },
    ],
  },
]);
  const data = reactive({
    searchForm: {
      customerName: "",
      salesContractNo: "",
      projectName: "",
      productCategory: "",
      specificationModel: "",
    },
  });
  const { searchForm } = toRefs(data);
const tableData = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
  total: 0,
});
const selectedRows = ref([]);
  const toProgressPercentage = val => {
    const n = Number(val);
    if (!Number.isFinite(n)) return 0;
    if (n <= 0) return 0;
    if (n >= 100) return 100;
    return Math.round(n);
  };
const data = reactive({
  searchForm: {
    customerName: "",
    salesContractNo: "",
    projectName: "",
    productCategory: "",
    specificationModel: "",
  },
});
const { searchForm } = toRefs(data);
  // 30/50/80/100 分段颜色:红/橙/蓝/绿
  const progressColor = percentage => {
    const p = toProgressPercentage(percentage);
    if (p < 30) return "#f56c6c";
    if (p < 50) return "#e6a23c";
    if (p < 80) return "#409eff";
    return "#67c23a";
  };
const toProgressPercentage = val => {
  const n = Number(val);
  if (!Number.isFinite(n) || n <= 0) return 0;
  if (n >= 100) return 100;
  return Math.round(n);
};
  // 添加表行类名方法
  const tableRowClassName = ({ row }) => {
    if (!row.deliveryDate) return '';
    if (row.isFh) return '';
const progressColor = percentage => {
  const p = toProgressPercentage(percentage);
  if (p < 30) return "#f56c6c";
  if (p < 50) return "#e6a23c";
  if (p < 80) return "#409eff";
  return "#67c23a";
};
    const diff = row.deliveryDaysDiff;
    if (diff === 15) {
      return 'yellow';
    } else if (diff === 10) {
      return 'pink';
    } else if (diff === 2) {
      return 'purple';
    } else if (diff < 2) {
      return 'red';
    }
  };
const tableRowClassName = ({ row }) => {
  if (!row.deliveryDate || row.isFh) return "";
  // 绑定工艺路线弹框
  const bindRouteDialogVisible = ref(false);
  const bindRouteLoading = ref(false);
  const bindRouteSaving = ref(false);
  const routeOptions = ref([]);
  const bindForm = reactive({
    orderId: null,
    routeId: null,
  const diff = row.deliveryDaysDiff;
  if (diff === 15) return "yellow";
  if (diff === 10) return "pink";
  if (diff === 2) return "purple";
  if (diff < 2) return "red";
  return "";
};
const bindRouteDialogVisible = ref(false);
const bindRouteLoading = ref(false);
const bindRouteSaving = ref(false);
const routeOptions = ref([]);
const bindProcessList = ref([]);
const userOptions = ref([]);
const userLoading = ref(false);
const bindForm = reactive({
  orderId: null,
  productModelId: null,
  routeId: null,
  processUserList: [],
});
const resetBindProcessUsers = () => {
  bindProcessList.value = [];
  bindForm.processUserList = [];
};
const ensureUserOptions = () => {
  if (userOptions.value.length || userLoading.value) return;
  userLoading.value = true;
  userListNoPageByTenantId()
    .then(res => {
      userOptions.value = res.data || [];
    })
    .finally(() => {
      userLoading.value = false;
    });
};
const createBindProcessUserList = list =>
  list.map(item => ({
    processId: item.id,
    processName: item.name || item.processName || item.no || "",
    userIds: [],
    userNames: "",
  }));
const buildBindProcessRouteItems = () =>
  bindProcessList.value.map((item, index) => {
    const processUser = bindForm.processUserList[index] || {};
    return {
      productOrderId: bindForm.orderId,
      productRouteId: bindForm.routeId,
      processId: item.id,
      productModelId: bindForm.productModelId,
      dragSort: item.dragSort ?? index + 1,
      isQuality: item.isQuality ?? false,
      reportUserIds: Array.isArray(processUser.userIds) ? processUser.userIds.join(",") : "",
    };
  });
  const openBindRouteDialog = async row => {
    bindForm.orderId = row.id;
    bindForm.routeId = null;
    bindRouteDialogVisible.value = true;
    routeOptions.value = [];
    if (!row.productModelId) {
      proxy.$modal.msgWarning("当前订单缺少产品型号,无法查询工艺路线");
      bindRouteDialogVisible.value = false;
const fetchBindProcessList = async routeId => {
  if (!routeId) {
    resetBindProcessUsers();
    return;
  }
  try {
    const res = await processList({ routeId });
    bindProcessList.value = res.data || [];
    bindForm.processUserList = createBindProcessUserList(bindProcessList.value);
    ensureUserOptions();
  } catch (error) {
    console.error("获取工序列表失败", error);
    proxy.$modal.msgError("获取工序列表失败");
    resetBindProcessUsers();
  }
};
const handleBindRouteChange = routeId => {
  fetchBindProcessList(routeId);
};
const handleBindProcessUserChange = (index, userIds) => {
  const selectedUsers = userOptions.value.filter(user => Array.isArray(userIds) && userIds.includes(user.userId));
  bindForm.processUserList[index].userNames = selectedUsers.map(user => user.nickName).join(",");
};
const removeBindProcessItem = index => {
  bindProcessList.value.splice(index, 1);
  bindForm.processUserList.splice(index, 1);
};
const openBindRouteDialog = async row => {
  bindForm.orderId = row.id;
  bindForm.productModelId = row.productModelId ?? null;
  bindForm.routeId = null;
  bindForm.processUserList = [];
  bindRouteDialogVisible.value = true;
  routeOptions.value = [];
  resetBindProcessUsers();
  if (!row.productModelId) {
    proxy.$modal.msgWarning("当前订单缺少产品型号,无法查询工艺路线");
    bindRouteDialogVisible.value = false;
    return;
  }
  bindRouteLoading.value = true;
  try {
    const res = await listProcessRoute({ productModelId: row.productModelId });
    routeOptions.value = res.data || [];
  } catch (error) {
    console.error("获取工艺路线列表失败", error);
    proxy.$modal.msgError("获取工艺路线列表失败");
  } finally {
    bindRouteLoading.value = false;
  }
};
const handleBindRouteConfirm = async () => {
  if (!bindForm.routeId) {
    proxy.$modal.msgWarning("请选择工艺路线");
    return;
  }
  if (!bindForm.processUserList.length) {
    proxy.$modal.msgWarning("当前工艺路线下没有工序");
    return;
  }
  if (bindForm.processUserList.some(item => !Array.isArray(item.userIds) || item.userIds.length === 0)) {
    proxy.$modal.msgWarning("请为每道工序选择报工人员");
    return;
  }
  bindRouteSaving.value = true;
  try {
    await bindingRoute({
      id: bindForm.orderId,
      routeId: bindForm.routeId,
      processRouteItems: buildBindProcessRouteItems(),
      processUserList: bindForm.processUserList.map(item => ({
        ...item,
        userIds: item.userIds.join(","),
      })),
    });
    proxy.$modal.msgSuccess("绑定成功");
    bindRouteDialogVisible.value = false;
    getList();
  } catch (error) {
    console.error("绑定工艺路线失败", error);
    proxy.$modal.msgError("绑定工艺路线失败");
  } finally {
    bindRouteSaving.value = false;
  }
};
const handleQuery = () => {
  page.current = 1;
  getList();
};
const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  const params = { ...searchForm.value, ...page };
  params.entryDate = undefined;
  productOrderListPage(params)
    .then(res => {
      tableData.value = res.data.records;
      page.total = res.data.total;
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
const showRouteItemModal = async row => {
  const orderId = row.id;
  try {
    const res = await getOrderProcessRouteMain(orderId);
    const detail = res.data || {};
    if (!detail.id) {
      proxy.$modal.msgWarning("未找到关联的工艺路线");
      return;
    }
    bindRouteLoading.value = true;
    try {
      const res = await listProcessRoute({ productModelId: row.productModelId });
      routeOptions.value = res.data || [];
    } catch (e) {
      console.error("获取工艺路线列表失败:", e);
      proxy.$modal.msgError("获取工艺路线列表失败");
    } finally {
      bindRouteLoading.value = false;
    }
  };
  const handleBindRouteConfirm = async () => {
    if (!bindForm.routeId) {
      proxy.$modal.msgWarning("请选择工艺路线");
      return;
    }
    bindRouteSaving.value = true;
    try {
      await bindingRoute({
        id: bindForm.orderId,
        routeId: bindForm.routeId,
      });
      proxy.$modal.msgSuccess("绑定成功");
      bindRouteDialogVisible.value = false;
      getList();
    } catch (e) {
      console.error("绑定工艺路线失败:", e);
      proxy.$modal.msgError("绑定工艺路线失败");
    } finally {
      bindRouteSaving.value = false;
    }
  };
  // 查询列表
  /** 搜索按钮操作 */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const pagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const changeDaterange = value => {
    if (value) {
      searchForm.value.entryDateStart = value[0];
      searchForm.value.entryDateEnd = value[1];
    } else {
      searchForm.value.entryDateStart = undefined;
      searchForm.value.entryDateEnd = undefined;
    }
    handleQuery();
  };
  const getList = () => {
    tableLoading.value = true;
    // 构造一个新的对象,不包含entryDate字段
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined;
    productOrderListPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  const showRouteItemModal = async row => {
    const orderId = row.id;
    try {
      const res = await getOrderProcessRouteMain(orderId);
      const data = res.data || {};
      if (!data || !data.id) {
        proxy.$modal.msgWarning("未找到关联的工艺路线");
        return;
      }
      router.push({
        path: "/productionManagement/processRouteItem",
        query: {
          id: data.id,
          processRouteCode: data.processRouteCode || "",
          productName: data.productName || "",
          model: data.model || "",
          bomNo: data.bomNo || "",
          description: data.description || "",
          orderId,
          type: "order",
        },
      });
    } catch (e) {
      console.error("获取工艺路线主信息失败:", e);
      proxy.$modal.msgError("获取工艺路线信息失败");
    }
  };
  const showProductStructure = row => {
    router.push({
      path: "/productionManagement/productStructureDetail",
      path: "/productionManagement/processRouteItem",
      query: {
        id: row.id,
        bomNo: row.bomNo || "",
        productName: row.productCategory || "",
        productModelName: row.specificationModel || "",
        orderId: row.id,
        id: detail.id,
        processRouteCode: detail.processRouteCode || "",
        productName: detail.productName || "",
        model: detail.model || "",
        bomNo: detail.bomNo || "",
        description: detail.description || "",
        orderId,
        type: "order",
      },
    });
  };
  } catch (error) {
    console.error("获取工艺路线信息失败", error);
    proxy.$modal.msgError("获取工艺路线信息失败");
  }
};
  // 表格选择数据
  const handleSelectionChange = (selection) => {
    selectedRows.value = selection;
  };
  const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map((item) => item.id);
    } else {
      proxy.$modal.msgWarning("请选择数据");
      return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      delProductOrder(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    }).catch(() => {
      proxy.$modal.msg("已取消");
    });
  };
  // 导出
  const handleOut = () => {
    ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        proxy.download("/productOrder/export", {...searchForm.value}, "生产订单.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  const handleConfirmRoute = () => {};
  onMounted(() => {
    getList();
const showProductStructure = row => {
  router.push({
    path: "/productionManagement/productStructureDetail",
    query: {
      id: row.id,
      bomNo: row.bomNo || "",
      productName: row.productCategory || "",
      productModelName: row.specificationModel || "",
      orderId: row.id,
      type: "order",
    },
  });
};
const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
const handleDelete = () => {
  if (!selectedRows.value.length) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  const ids = selectedRows.value.map(item => item.id);
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => delProductOrder(ids))
    .then(() => {
      proxy.$modal.msgSuccess("删除成功");
      getList();
    })
    .catch(() => {
      proxy.$modal.msg("已取消删除");
    });
};
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      proxy.download("/productOrder/export", { ...searchForm.value }, "生产订单.xlsx");
    })
    .catch(() => {
      proxy.$modal.msg("已取消导出");
    });
};
onMounted(() => {
  getList();
});
</script>
<style scoped lang="scss">
.search_form{
.search_form {
  align-items: start;
}
::v-deep .yellow {
  background-color: #FAF0DE;
:deep(.yellow) {
  background-color: #faf0de;
}
::v-deep .pink {
  background-color: #FAE1DE;
:deep(.pink) {
  background-color: #fae1de;
}
::v-deep .red {
:deep(.red) {
  background-color: #f80202;
}
::v-deep .purple{
  background-color: #F4DEFA;
:deep(.purple) {
  background-color: #f4defa;
}
.process-user-list {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 14px;
  border-radius: 12px;
  background: #f7f9fc;
  border: 1px solid #e8eef5;
}
.process-user-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  padding: 12px 14px;
  border-radius: 10px;
  background: #fff;
  border: 1px solid #edf2f7;
}
.process-user-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.process-user-name {
  color: #1f2d3d;
  font-weight: 500;
  line-height: 1.4;
}
.process-user-remove {
  flex-shrink: 0;
  padding: 0;
}
.process-user-select {
  width: 100%;
}
@media (max-width: 768px) {
  .process-user-item {
    grid-template-columns: 1fr;
    gap: 10px;
  }
}
</style>