已修改19个文件
1350 ■■■■ 文件已修改
src/api/productionManagement/productStructure.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionOrder.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/ProductSelectDialog.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/index.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/transportTaskManagement/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/inventoryManagement/vehicleFuelManagement/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personnelManagement/attendanceCheckin/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/paymentEntry/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/processRouteItem/index.vue 100 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productStructure/Detail/index.vue 307 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productStructure/index.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/New.vue 715 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 111 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionProcess/Edit.vue 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index.vue 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrder/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/invoiceRegistration/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/receiptPayment/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productStructure.js
@@ -9,6 +9,14 @@
  });
}
// 根据BOM ID查询父项产品
export function listByBomIdIsParent(bomId) {
  return request({
    url: "/productStructure/listBybomIdIsParent/" + bomId,
    method: "get",
  });
}
export function add(data) {
  return request({
    url: "/productStructure",
src/api/productionManagement/productionOrder.js
@@ -36,6 +36,14 @@
  });
}
export function startOrPause(data) {
  return request({
    url: "/productOrder/startOrPause",
    method: "post",
    data,
  });
}
// 生产订单-新增
export function addProductOrder(data) {
  return request({
src/views/basicData/product/ProductSelectDialog.vue
@@ -22,7 +22,6 @@
      <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" />
      <el-table-column prop="drawingNumber" label="规格型号" min-width="160" />
      <el-table-column prop="unit" label="单位" min-width="160" />
    </el-table>
src/views/basicData/product/index.vue
@@ -254,11 +254,6 @@
    minWidth: 150,
  },
  {
    label: "工艺路线",
    prop: "routeName",
    minWidth: 150,
  },
  {
    label: "单位",
    prop: "unit",
    minWidth: 100,
@@ -390,12 +385,7 @@
    modelForm.value = { ...data };
    modelForm.value.tempFileIds = data.tempFileIds || [];
    modelForm.value.salesLedgerFiles = data.salesLedgerFiles || [];
    if (data.salesLedgerFiles && data.salesLedgerFiles.length > 0) {
      drawingFileList.value = data.salesLedgerFiles.map(file => ({
        name: file.name,
        url: file.url
      }));
    } else if (data.drawingFile) {
    if (data.drawingFile) {
      drawingFileList.value = [{
        name: data.drawingFile.split('/').pop(),
        url: data.drawingFile
src/views/inventoryManagement/transportTaskManagement/index.vue
@@ -681,11 +681,11 @@
  text-align: right;
}
::v-deep(.row-finished) {
:deep(.row-finished) {
  background-color: #f6ffed;
}
::v-deep(.row-running) {
:deep(.row-running) {
  background-color: #fffbe6;
}
</style>
src/views/inventoryManagement/vehicleFuelManagement/index.vue
@@ -549,7 +549,7 @@
  text-align: right;
}
::v-deep(.row-abnormal) {
:deep(.row-abnormal) {
  background-color: #fff5f5;
}
</style>
src/views/personnelManagement/attendanceCheckin/index.vue
@@ -462,7 +462,7 @@
  color: #333;
}
::v-deep(.row-abnormal) {
:deep(.row-abnormal) {
  background-color: #fff5f5;
}
</style>
src/views/procurementManagement/paymentEntry/index.vue
@@ -572,7 +572,7 @@
.table_list {
  margin-top: unset;
}
::v-deep(.el-checkbox__label) {
:deep(.el-checkbox__label) {
  font-weight: bold;
}
.empty-tip {
src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -63,11 +63,9 @@
        <el-button 
            icon="Grid" 
            @click="toggleView"
            style="margin-right: 10px;"
        >
          卡片视图
        </el-button>
        <el-button type="primary" @click="handleAdd">新增</el-button>
      </div>
    </div>
    <el-table
@@ -87,10 +85,6 @@
          {{ getProcessName(scope.row.processId) || '-' }}
        </template>
      </el-table-column>
      <el-table-column label="产品名称" prop="productName" min-width="160" />
      <el-table-column label="图纸编号" prop="model" min-width="140" />
      <el-table-column label="规格型号" prop="drawingNumber" min-width="160" />
      <el-table-column label="单位" prop="unit" width="100" />
      <el-table-column label="是否质检" prop="isQuality" width="100">
        <template #default="scope">
          {{scope.row.isQuality ? "是" : "否"}}
@@ -117,11 +111,9 @@
          <el-button 
              icon="Menu" 
              @click="toggleView"
              style="margin-right: 10px;"
          >
            表格视图
          </el-button>
          <el-button type="primary" @click="handleAdd">新增</el-button>
        </div>
      </div>
      <div v-loading="tableLoading" class="card-container">
@@ -141,18 +133,8 @@
            <div class="card-process-name">{{ getProcessName(item.processId) || '-' }}</div>
          </div>
          
          <!-- 产品信息 -->
          <div class="card-content">
            <div v-if="item.productName" class="product-info">
              <div class="product-name">{{ item.productName }}</div>
              <div class="product-model">{{ item.drawingNumber || '-' }}</div>
              <div v-if="item.model" class="product-model">
                {{ item.model }}
                <!-- <span v-if="item.unit" class="product-unit">{{ item.unit }}</span> -->
              </div>
              <el-tag type="primary" class="product-tag" v-if="item.isQuality">质检</el-tag>
            </div>
            <div v-else class="product-info empty">暂无产品信息</div>
            <el-tag type="primary" class="product-tag" v-if="item.isQuality">质检</el-tag>
          </div>
          
          <!-- 操作按钮 -->
@@ -194,23 +176,6 @@
          </el-select>
        </el-form-item>
        <el-form-item label="产品名称" prop="productModelId">
          <el-button type="primary" @click="showProductSelectDialog = true">
            {{ form.productName && form.model
              ? `${form.productName} - ${form.model}`
              : '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item label="单位" prop="unit">
          <el-input
              v-model="form.unit"
              :placeholder="form.productModelId ? '根据选择的产品自动带出' : '请先选择产品'"
              clearable
              :disabled="true"
          />
        </el-form-item>
        <el-form-item label="是否质检" prop="isQuality">
          <el-switch v-model="form.isQuality" :active-value="true" :inactive-value="false"/>
        </el-form-item>
@@ -237,18 +202,11 @@
      </template>
    </el-dialog>
    <!-- 产品选择对话框 -->
    <ProductSelectDialog
        v-model="showProductSelectDialog"
        @confirm="handleProductSelect"
        single
    />
  </div>
</template>
<script setup>
import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import { findProcessRouteItemList, addOrUpdateProcessRouteItem, sortProcessRouteItem, batchDeleteProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
import { findProductProcessRouteItemList, deleteRouteItem, addRouteItem, addOrUpdateProductProcessRouteItem, sortRouteItem } from "@/api/productionManagement/productProcessRoute.js";
import { processList } from "@/api/productionManagement/productionProcess.js";
@@ -283,7 +241,6 @@
});
const processOptions = ref([]);
const showProductSelectDialog = ref(false);
const staffList = ref([]);
const treeProps = {
@@ -307,17 +264,12 @@
  id: undefined,
  routeId: routeId.value,
  processId: undefined,
  productModelId: undefined,
  productName: "",
  model: "",
  unit: "",
  isQuality: false,
  userPower: [],
});
const rules = {
  processId: [{ required: true, message: '请选择工序', trigger: 'change' }],
  productModelId: [{ required: true, message: '请选择产品', trigger: 'change' }],
};
// 根据工序ID获取工序名称
@@ -395,10 +347,6 @@
    id: row.id,
    routeId: routeId.value,
    processId: row.processId,
    productModelId: row.productModelId,
    productName: row.productName || "",
    model: row.model || "",
    unit: row.unit || "",
    isQuality: row.isQuality,
    userPower: userPowerIds,
  };
@@ -447,20 +395,6 @@
    .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 || "";
    showProductSelectDialog.value = false;
    // 触发表单验证
    formRef.value?.validateField('productModelId');
  }
};
// 提交
const handleSubmit = () => {
  formRef.value.validate((valid) => {
@@ -480,7 +414,6 @@
              productOrderId: orderId.value,
              productRouteId: routeId.value,
              processId: form.value.processId,
              productModelId: form.value.productModelId,
              isQuality: form.value.isQuality,
              userPower: userPowerNames.join(','),
              dragSort,
@@ -488,7 +421,6 @@
          : addOrUpdateProcessRouteItem({
              routeId: routeId.value,
              processId: form.value.processId,
              productModelId: form.value.productModelId,
              isQuality: form.value.isQuality,
              userPower: userPowerNames.join(','),
              dragSort,
@@ -514,14 +446,12 @@
          ? addOrUpdateProductProcessRouteItem({
              id: form.value.id,
              processId: form.value.processId,
              productModelId: form.value.productModelId,
              isQuality: form.value.isQuality,
              userPower: userPowerNames.join(','),
            })
          : addOrUpdateProcessRouteItem({
              routeId: routeId.value,
              processId: form.value.processId,
              productModelId: form.value.productModelId,
              id: form.value.id,
              isQuality: form.value.isQuality,
              userPower: userPowerNames.join(','),
@@ -550,10 +480,8 @@
    id: undefined,
    routeId: routeId.value,
    processId: undefined,
    productModelId: undefined,
    productName: "",
    model: "",
    unit: "",
    isQuality: false,
    userPower: [],
  };
  formRef.value?.resetFields();
};
@@ -800,20 +728,20 @@
.process-card {
  flex-shrink: 0;
  width: 220px;
  width: 160px;
  background: #fff;
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
  padding: 16px;
  display: flex;
  flex-direction: column;
  cursor: move;
  transition: all 0.3s;
  transition: all 0.2s;
}
.process-card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  transform: translateY(-2px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
.card-header {
@@ -822,20 +750,20 @@
}
.card-number {
  width: 36px;
  height: 36px;
  line-height: 36px;
  width: 28px;
  height: 28px;
  line-height: 28px;
  border-radius: 50%;
  background: #409eff;
  color: #fff;
  font-weight: bold;
  font-size: 16px;
  font-size: 13px;
  margin: 0 auto 8px;
}
.card-process-name {
  font-size: 14px;
  color: #333;
  color: #303133;
  font-weight: 500;
  word-break: break-all;
}
@@ -843,7 +771,7 @@
.card-content {
  flex: 1;
  margin-bottom: 12px;
  min-height: 60px;
  min-height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
src/views/productionManagement/productStructure/Detail/index.vue
@@ -2,16 +2,21 @@
  <div class="app-container">
    <PageHeader content="产品结构详情">
      <template #right-button>
        <el-button v-if="dataValue.isEdit && !isOrderPage"
                   @click="cancelEdit">取消
        </el-button>
        <el-button v-if="!dataValue.isEdit && !isOrderPage"
                   type="primary"
                   @click="dataValue.isEdit = true">编辑
        </el-button>
        <el-button v-if="dataValue.isEdit && !isOrderPage"
                   type="primary"
                   @click="cancelEdit">取消
                   @click="dataValue.isEdit = true">点击进行修改
        </el-button>
        <el-button v-if="!isOrderPage"
                   type="primary"
                   @click="openBomAddDialog"
                   :disabled="!dataValue.isEdit">
          <el-icon><Document /></el-icon> 按BOM添加
        </el-button>
        <el-button v-if="!isOrderPage"
                   type="success"
                   :loading="dataValue.loading"
                   @click="submit"
                   :disabled="!dataValue.isEdit">确认
@@ -38,8 +43,8 @@
                                                             label="图纸编号">
                                <template #default="{ row, $index }">
                                    <el-form-item v-if="dataValue.isEdit"
                                                                :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]"
                                                                style="margin: 0">
                                                            :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]"
                                                            style="margin: 0">
                                        <el-select v-model="row.model"
                                                             placeholder="请选择规格"
                                                             clearable
@@ -53,13 +58,11 @@
                                    </el-form-item>
                                </template>
                            </el-table-column>
              <el-table-column prop="drawingNumber"
                               label="规格型号" />
              <el-table-column prop="processName"
                               label="消耗工序">
                               label="工序">
                <template #default="{ row, $index }">
                  <el-form-item v-if="dataValue.isEdit"
                                :rules="dataValue.dataList.some(item => (item as any).tempId === row.tempId) ? [] : [{ required: true, message: '请选择消耗工序', trigger: 'change' }]"
                                :rules="dataValue.dataList.some(item => (item as any).tempId === row.tempId) ? [] : [{ required: true, message: '请选工序', trigger: 'change' }]"
                                style="margin: 0">
                    <el-select v-model="row.processId"
                               placeholder="请选择"
@@ -76,10 +79,10 @@
                </template>
              </el-table-column>
              <el-table-column prop="unitQuantity"
                               label="单位产出所需数量">
                               label="单位用量">
                <template #default="{ row, $index }">
                  <el-form-item v-if="dataValue.isEdit"
                                :rules="[{ required: true, message: '请输入单位产出所需数量', trigger: ['blur','change'] }]"
                                :rules="[{ required: true, message: '请输入单位用量', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-input-number v-model="row.unitQuantity"
                                     :min="0"
@@ -141,18 +144,62 @@
          </el-form>
        </template>
      </el-table-column>
      <el-table-column label="BOM编号"
      <el-table-column v-if="!isOrderPage"
                       label="BOM编号"
                       prop="bomNo" />
      <el-table-column label="产品名称"
                       prop="productName" />
            <el-table-column label="图纸编号"
                                             prop="model" />
      <el-table-column label="规格型号"
                       prop="drawingNumber" />
                                                         prop="model" />
    </el-table>
    <product-select-dialog v-if="dataValue.showProductDialog"
                           v-model:model-value="dataValue.showProductDialog"
                           @confirm="handleProduct" />
    <!-- 按BOM添加弹窗 -->
    <el-dialog v-model="bomAddDialogVisible" title="按BOM添加" width="500px" @close="closeBomAddDialog">
      <el-form ref="bomAddFormRef" :model="bomAddForm" :rules="bomAddRules" label-width="100px">
        <el-form-item label="父项产品" prop="parentProductId">
          <el-select v-model="bomAddForm.parentProductId" placeholder="请选择" clearable filterable
            style="width: 100%" @change="handleBomParentProductChange">
            <el-option v-for="item in parentProductOptions" :key="item.id"
              :label="`${item.model || item.productCode || item.id} | ${item.productName}`"
              :value="item.id" />
          </el-select>
        </el-form-item>
        <!-- 选中产品后展示产品信息 -->
        <div v-if="selectedBomProduct" class="selected-product-info">
          <div class="product-info-row">
            <span class="info-label">产品编号</span>
            <span class="info-value">{{ selectedBomProduct.model || selectedBomProduct.productCode || '-' }}</span>
          </div>
          <div class="product-info-row">
            <span class="info-label">产品名称</span>
            <span class="info-value">{{ selectedBomProduct.productName || '-' }}</span>
          </div>
          <div class="product-info-row">
            <span class="info-label">产品规格</span>
            <span class="info-value">{{ selectedBomProduct.spec || selectedBomProduct.drawingNumber || '-' }}</span>
          </div>
          <div class="stock-info-box">
            <div class="stock-number">{{ selectedBomProduct.stockQuantity || 0 }}</div>
            <div class="stock-label">库存数量(台)</div>
          </div>
        </div>
        <el-form-item label="用量系数" prop="coefficient" style="margin-top: 20px;">
          <el-input-number v-model="bomAddForm.coefficient" :min="0.01" :precision="2" :step="1"
            controls-position="right" style="width: 100%" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="submitBomAdd">确定</el-button>
        <el-button @click="closeBomAddDialog">取消</el-button>
      </template>
    </el-dialog>
    <!-- 高级选择产品弹窗 -->
    <product-select-dialog v-if="showAdvancedProductDialog" v-model:model-value="showAdvancedProductDialog"
      @confirm="handleAdvancedProductSelect" />
  </div>
</template>
@@ -165,11 +212,14 @@
    reactive,
    ref,
  } from "vue";
  import { queryList, add } from "@/api/productionManagement/productStructure.js";
  import { queryList, add, listByBomIdIsParent } from "@/api/productionManagement/productStructure.js";
  import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
  import { list } from "@/api/productionManagement/productionProcess";
  import { productListPage } from "@/api/basicData/product.js";
  import { listPage as listBomPage } from "@/api/productionManagement/productBom.js";
  import { ElMessage } from "element-plus";
  import { useRoute, useRouter } from "vue-router";
  import { Search, Document } from '@element-plus/icons-vue'
  defineComponent({
    name: "StructureEdit",
@@ -216,6 +266,21 @@
    loading: false,
    isEdit: false,
  });
  // 按BOM添加相关
  const bomAddDialogVisible = ref(false);
  const bomAddFormRef = ref();
  const parentProductOptions = ref([]);
  const selectedBomProduct = ref(null);
  const selectedBomTreeData = ref([]); // 保存选中产品时获取的BOM树数据
  const bomAddForm = reactive({
    parentProductId: undefined,
    coefficient:1
  });
  const bomAddRules = {
    parentProductId: [{ required: true, message: "请选择父项产品", trigger: "change" }],
    coefficient: [{ required: true, message: "请输入用量系数", trigger: "blur" }]
  };
  const tableData = reactive([
    {
@@ -340,13 +405,8 @@
        isValid = false;
        return;
      }
      if (!isTopLevel && !item.processId) {
        ElMessage.error("请选择消耗工序");
        isValid = false;
        return;
      }
      if (!item.unitQuantity) {
        ElMessage.error("请输入单位产出所需数量");
        ElMessage.error("请输入单位用量");
        isValid = false;
        return;
      }
@@ -511,6 +571,151 @@
    fetchData();
  };
  // 获取父项产品列表
  const getParentProductList = async () => {
    try {
      const { data } = await listBomPage({ current: 1, size: 1000 });
      parentProductOptions.value = data?.records || [];
    } catch (error) {
      console.error("获取父项产品列表失败:", error);
      parentProductOptions.value = [];
    }
  };
  // 打开按BOM添加弹窗
  const openBomAddDialog = () => {
    bomAddDialogVisible.value = true;
    bomAddForm.parentProductId = undefined;
    bomAddForm.coefficient = 1;
    selectedBomProduct.value = null;
    getParentProductList();
  };
  // 关闭按BOM添加弹窗
  const closeBomAddDialog = () => {
    bomAddDialogVisible.value = false;
    bomAddFormRef.value?.resetFields();
    selectedBomProduct.value = null;
  };
  // 打开高级选择
  const openAdvancedSelect = () => {
    showAdvancedProductDialog.value = true;
  };
  // 父项产品变更
  const handleBomParentProductChange = async (val) => {
    if (val) {
      const product = parentProductOptions.value.find(item => item.id === val);
      selectedBomProduct.value = product || null;
      // 直接用选中的 BOM ID 调用 listByBomIdIsParent
      const { data: treeData } = await listByBomIdIsParent(val);
      selectedBomTreeData.value = treeData || [];
      // 将二级树中的产品添加到父项产品选项(从第二级开始)
      const addTreeToOptions = (items: any[]) => {
        items.forEach((item: any) => {
          // 跳过第一级,只添加第二级及以下的产品
          if (item.children && item.children.length > 0) {
            item.children.forEach((child: any) => {
              const exists = parentProductOptions.value.some(opt => opt.id === child.id);
              if (!exists) {
                parentProductOptions.value.push({
                  id: child.id,
                  productName: child.productName,
                  model: child.model,
                  productCode: child.model,
                  spec: child.drawingNumber,
                  drawingNumber: child.drawingNumber,
                  stockQuantity: child.stockQuantity || 0
                });
              }
              // 递归添加子项
              if (child.children && child.children.length > 0) {
                addTreeToOptions([child]);
              }
            });
          }
        });
      };
      if (selectedBomTreeData.value.length > 0) {
        addTreeToOptions(selectedBomTreeData.value);
      }
    } else {
      selectedBomProduct.value = null;
      selectedBomTreeData.value = [];
    }
  };
  // 提交按BOM添加
  const submitBomAdd = () => {
    bomAddFormRef.value.validate(async (valid) => {
      if (!valid) return;
      const product = parentProductOptions.value.find(item => item.id === bomAddForm.parentProductId);
      if (!product) {
        ElMessage.error("未找到选中的产品");
        return;
      }
      try {
        // 使用选择产品时保存的BOM树数据
        const bomItems = selectedBomTreeData.value || [];
        if (bomItems.length === 0) {
          ElMessage.warning("该产品没有BOM信息");
          return;
        }
        // 列表的第一级已经存在,把BOM数据作为第一级的子项(第二级)添加
        if (dataValue.dataList.length > 0) {
          const firstLevelItem = dataValue.dataList[0];
          // 把BOM数据添加到第一级的children中
          const addBomItemsRecursively = (items: any[], parentItem: any) => {
            items.forEach((item: any) => {
              const newItem: any = {
                parentId: item.parentId || "",
                parentTempId: parentItem.tempId || "",
                productName: item.productName || "",
                productId: item.productId || item.productModelId || "",
                model: item.model || "",
                productModelId: item.productModelId || "",
                drawingNumber: item.drawingNumber || "",
                processId: item.processId || "",
                processName: item.processName || "",
                unitQuantity: (item.unitQuantity || 0) * bomAddForm.coefficient,
                demandedQuantity: (item.demandedQuantity || 0) * bomAddForm.coefficient,
                unit: item.unit || "",
                children: [],
                tempId: new Date().getTime() + Math.random(),
              };
              // 添加到父项的children
              if (!parentItem.children) {
                parentItem.children = [];
              }
              parentItem.children.push(newItem);
              // 递归处理子项
              if (item.children && item.children.length > 0) {
                addBomItemsRecursively(item.children, newItem);
              }
            });
          };
          addBomItemsRecursively(bomItems, firstLevelItem);
        }
        ElMessage.success("添加成功");
        closeBomAddDialog();
      } catch (error) {
        console.error("按BOM添加失败:", error);
        ElMessage.error("添加失败");
      }
    });
  };
  onMounted(async () => {
    // 从路由参数回显数据
    tableData[0].productName = routeProductName.value as string;
@@ -527,4 +732,56 @@
    await fetchProcessOptions();
    await fetchData();
  });
</script>
</script>
<style scoped>
.selected-product-info {
  background-color: #f5f7fa;
  border-radius: 4px;
  padding: 16px;
  margin: 10px 0;
  position: relative;
}
.product-info-row {
  display: flex;
  margin-bottom: 8px;
  font-size: 14px;
}
.product-info-row:last-child {
  margin-bottom: 0;
}
.info-label {
  color: #909399;
  width: 70px;
  flex-shrink: 0;
}
.info-value {
  color: #303133;
  flex: 1;
}
.stock-info-box {
  position: absolute;
  right: 16px;
  top: 50%;
  transform: translateY(-50%);
  text-align: center;
}
.stock-number {
  font-size: 28px;
  font-weight: bold;
  color: #303133;
  line-height: 1;
  margin-bottom: 4px;
}
.stock-label {
  font-size: 12px;
  color: #909399;
}
</style>
src/views/productionManagement/productStructure/index.vue
@@ -1,11 +1,11 @@
<template>
  <div class="app-container">
    <div style="text-align: right; margin-bottom: 10px;">
      <el-button type="primary" @click="handleAdd">新增</el-button>
      <el-button type="info" plain icon="Upload" @click="handleImport"
        v-hasPermi="['product:bom:import']">导入</el-button>
      <el-button type="warning" plain icon="Download" @click="handleExport" :disabled="selectedRows.length !== 1"
        v-hasPermi="['product:bom:export']">导出</el-button>
      <el-button type="primary" @click="handleAdd">新增</el-button>
      <el-button type="danger" plain @click="handleBatchDelete" :disabled="selectedRows.length === 0">删除</el-button>
    </div>
    <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true"
@@ -82,11 +82,6 @@
        prop: "productModelName",
        minWidth: 140
    },
  {
    label: "规格型号",
    prop: "drawingNumber",
    minWidth: 160
  },
  {
    label: "版本号",
    prop: "version",
src/views/productionManagement/productionOrder/New.vue
@@ -3,63 +3,87 @@
    <el-dialog
        v-model="isShow"
        title="新增生产订单"
        width="800"
        width="1500"
        :close-on-click-modal="false"
        @close="closeModal"
        class="production-order-dialog"
    >
      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
        <el-form-item
            label="产品名称"
            prop="productModelId"
            :rules="[
                {
                required: true,
                message: '请选择产品',
                trigger: 'change',
              }
            ]"
        >
          <el-button type="primary" @click="showProductSelectDialog = true">
            {{ 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>
        <el-form-item
            label="规格型号"
            prop="drawingNumber"
        >
          <el-input v-model="formState.drawingNumber"  disabled />
        </el-form-item>
        <el-form-item
            label="单位"
            prop="unit"
        >
          <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>
        </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>
      <!-- 基本信息 -->
      <div class="section-card">
        <div class="section-header">
          <span class="section-icon">📋</span>
          <span class="section-title-text">基本信息</span>
        </div>
        <el-form label-width="100px" :model="formState" label-position="right" ref="formRef" class="compact-form">
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item
                  label="产品名称"
                  prop="productModelId"
                  :rules="[{ required: true, message: '请选择产品', trigger: 'change' }]"
              >
                <el-button type="primary" @click="showProductSelectDialog = true" class="select-btn">
                  {{ formState.productName ? formState.productName : '选择产品' }}
                </el-button>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="图纸编号" prop="model">
                <el-input v-model="formState.model" disabled />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="单位" prop="unit">
                <el-input v-model="formState.unit" disabled />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="需求数量" prop="quantity">
                <el-input-number v-model="formState.quantity" :step="1" :min="1" style="width: 100%" />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="交付日期" prop="deliveryDate">
                <el-date-picker
                    v-model="formState.deliveryDate"
                    type="date"
                    placeholder="选择交付日期"
                    style="width: 100%"
                    format="YYYY-MM-DD"
                    value-format="YYYY-MM-DD"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="图纸上传">
                <el-upload
                    v-model:file-list="fileList"
                    :action="upload.url"
                    :headers="upload.headers"
                    :data="upload.data"
                    :on-success="handleDrawingUploadSuccess"
                    :on-remove="handleDrawingRemove"
                    :before-upload="handleDrawingBeforeUpload"
                    :limit="5"
                    accept=".pdf,.jpg,.jpeg,.png,.dwg"
                    list-type="picture-card"
                >
                  <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
                  <template #tip>
                    <div class="el-upload__tip">
                      支持 pdf、jpg、jpeg、png、dwg 格式,大小不超过 10MB
                    </div>
                  </template>
                </el-upload>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
      <!-- 产品选择弹窗 -->
      <ProductSelectDialog
@@ -67,6 +91,167 @@
          @confirm="handleProductSelect"
          single
      />
      <!-- 用料产品选择弹窗 -->
      <ProductSelectDialog
          v-model="showMaterialProductDialog"
          @confirm="handleMaterialProductSelect"
      />
      <!-- BOM选择弹窗 -->
      <el-dialog
          v-model="showBomDialog"
          title="选择BOM"
          width="500"
          append-to-body
      >
        <el-table :data="bomList" border size="small" @row-click="handleBomSelect" highlight-current-row style="margin-bottom: 10px;">
          <el-table-column type="index" label="序号" width="60" />
          <el-table-column label="BOM编号" prop="bomNo" min-width="120" />
          <el-table-column label="版本" prop="version" min-width="80" />
          <el-table-column label="产品名称" prop="productName" min-width="100" />
        </el-table>
      </el-dialog>
      <!-- 工序 -->
      <div class="section-card">
        <div class="section-header">
          <span class="section-icon">🔧</span>
          <span class="section-title-text">工序</span>
          <el-button type="primary" size="small" @click="addProductionTask" class="add-btn">
            <el-icon><Plus /></el-icon> 添加任务
          </el-button>
        </div>
        <div class="table-container">
          <el-table :data="processRouteItems" border size="small" class="compact-table">
        <el-table-column type="index" label="序号" width="60" />
        <el-table-column label="工序名称" min-width="150">
          <template #default="{ row }">
            <el-select
              v-model="row.processId"
              placeholder="请选择工序"
              style="width: 100%"
              @change="(val) => handleProcessChange(val, row)"
            >
              <el-option
                v-for="item in processRouteItemsOptions"
                :key="item.id"
                :label="item.processName"
                :value="item.processId"
              />
            </el-select>
          </template>
        </el-table-column>
        <el-table-column label="报工权限" min-width="180">
          <template #default="{ row }">
            <el-select
              v-model="row.userPower"
              multiple
              collapse-tags
              collapse-tags-tooltip
              placeholder="请选择报工权限"
              style="width: 100%"
            >
              <el-option
                v-for="item in userOptions"
                :key="item.userId"
                :label="item.nickName"
                :value="item.nickName"
              />
            </el-select>
          </template>
        </el-table-column>
        <el-table-column label="计划数" min-width="100">
          <template #default="{ row }">
            <el-input-number v-model="row.planNum" :min="1" size="small" style="width: 100%" />
          </template>
        </el-table-column>
        <el-table-column label="是否质检" min-width="100">
          <template #default="{ row }">
            <el-switch v-model="row.isQuality" :active-value="true" :inactive-value="false" size="small" />
          </template>
        </el-table-column>
        <el-table-column label="计划开始时间" min-width="180">
          <template #default="{ row }">
            <el-date-picker
                v-model="row.planStartTime"
                type="date"
                placeholder="选择开始时间"
                style="width: 100%"
                format="YYYY-MM-DD"
                value-format="YYYY-MM-DD"
            />
          </template>
        </el-table-column>
        <el-table-column label="计划结束时间" min-width="180">
          <template #default="{ row }">
            <el-date-picker
                v-model="row.planEndTime"
                type="date"
                placeholder="选择结束时间"
                style="width: 100%"
                format="YYYY-MM-DD"
                value-format="YYYY-MM-DD"
            />
          </template>
        </el-table-column>
        <el-table-column label="操作" width="80" fixed="right" align="center">
          <template #default="{ $index }">
            <el-button type="danger" link size="small" @click="removeProcessRouteItem($index)">
              <el-icon><Delete /></el-icon>
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <div v-if="processRouteItems.length === 0" class="empty-tip">
        <el-empty description="暂无工序,点击上方按钮添加" :image-size="60" />
      </div>
        </div>
      </div>
      <!-- 用料清单 -->
      <div class="section-card">
        <div class="section-header">
          <span class="section-icon">📦</span>
          <span class="section-title-text">用料清单</span>
          <el-button type="primary" size="small" @click="addMaterialItem" class="add-btn">
            <el-icon><Plus /></el-icon> 添加用料
          </el-button>
        </div>
        <div class="table-container">
          <el-table :data="productStructureRecords" border size="small" class="compact-table">
            <el-table-column type="index" label="序号" width="50" align="center" />
            <el-table-column label="图纸编号" prop="model" min-width="120" />
            <el-table-column label="产品名称" prop="productName" min-width="120" />
            <el-table-column label="单位产出需要数量" min-width="140">
              <template #default="{ row }">
                <el-input-number v-model="row.unitQuantity" :min="0" :precision="2" size="small" style="width: 100%" />
              </template>
            </el-table-column>
            <el-table-column label="需求数量" min-width="120">
              <template #default="{ row }">
                <el-input-number v-model="row.demandedQuantity" :min="0" :precision="2" size="small" style="width: 100%" />
              </template>
            </el-table-column>
            <el-table-column label="单位" min-width="80">
              <template #default="{ row }">
                <el-input v-model="row.unit" placeholder="请输入" size="small" />
              </template>
            </el-table-column>
            <el-table-column label="操作" width="80" fixed="right" align="center">
              <template #default="{ $index }">
                <el-button type="danger" link size="small" @click="removeProductStructureRecord($index)">
                  <el-icon><Delete /></el-icon>
                </el-button>
              </template>
            </el-table-column>
          </el-table>
          <div v-if="productStructureRecords.length === 0" class="empty-tip">
            <el-empty description="暂无用料清单,点击上方按钮添加" :image-size="60" />
          </div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="handleSubmit">确认</el-button>
@@ -78,9 +263,15 @@
</template>
<script setup>
import {ref, computed, getCurrentInstance} from "vue";
import {ref, computed, getCurrentInstance, watch} from "vue";
import { Plus, Delete, Upload } from '@element-plus/icons-vue';
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
import {addProductOrder, listProcessRoute} from "@/api/productionManagement/productionOrder.js";
import {addProductOrder} from "@/api/productionManagement/productionOrder.js";
import {listDeptUserTree} from "@/api/basicData/productProcess.js";
import {findProcessRouteItemList} from "@/api/productionManagement/processRouteItem.js";
import {listPage as listProductBom} from "@/api/productionManagement/productBom.js";
import {listByBomIdIsParent} from "@/api/productionManagement/productStructure.js";
import { getToken } from "@/utils/auth.js";
const props = defineProps({
  visible: {
@@ -103,10 +294,31 @@
  productModelId: undefined,
  routeId: undefined,
  productName: "",
  productModelName: "",
  model: "",
  unit: "",
  drawingNumber: "",
  quantity: 0,
  deliveryDate: "",
  tempFileIds: [],
  salesLedgerFiles: [],
});
// 工序路线明细列表
 const processRouteItems = ref([]);
 // 物料清单列表
 const productStructureRecords = ref([]);
// 用户列表
const userOptions = ref([]);
// 文件列表
const fileList = ref([]);
const upload = reactive({
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  headers: { Authorization: "Bearer " + getToken() },
  data: { type: 14 },
});
const isShow = computed({
@@ -119,6 +331,34 @@
});
const showProductSelectDialog = ref(false);
const showMaterialProductDialog = ref(false);
// 获取用户列表
const fetchUserOptions = () => {
  listDeptUserTree().then(res => {
    const users = [];
    const extractUsers = (nodes) => {
      nodes.forEach(node => {
        if (node.userList && node.userList.length > 0) {
          node.userList.forEach(user => {
            users.push({
              userId: user.userId,
              nickName: user.nickName || user.userName
            });
          });
        }
        if (node.childrenList && node.childrenList.length > 0) {
          extractUsers(node.childrenList);
        }
      });
    };
    extractUsers(res.data || []);
    userOptions.value = users;
  });
};
// 组件挂载时获取数据
fetchUserOptions();
let { proxy } = getCurrentInstance()
@@ -130,9 +370,17 @@
    routeId: undefined,
    productName: "",
    drawingNumber: "",
    productModelName: "",
    quantity: '',
    model: "",
    unit: "",
    quantity: 0,
    deliveryDate: "",
    tempFileIds: [],
    salesLedgerFiles: [],
  };
  // 重置工序路线明细和物料清单
  processRouteItems.value = [];
  productStructureRecords.value = [];
  fileList.value = [];
  isShow.value = false;
};
@@ -143,28 +391,219 @@
    formState.value.productId = product.productId;
    formState.value.productName = product.productName;
    formState.value.drawingNumber = product.drawingNumber;
    formState.value.productModelName = product.model;
    formState.value.model = product.model;
    formState.value.productModelId = product.id;
    formState.value.unit = product.unit;
    formState.value.routeId = product.routeId;
    showProductSelectDialog.value = false;
    fetchRouteOptions( product.id);
    // 1. 通过产品自带的routeId获取工序列表
    if (product.routeId) {
      fetchProcessRouteItems(product.routeId);
    }
    // 2. 通过产品id查询BOM列表
    fetchBomList(product.id);
    // 触发表单验证更新
    proxy.$refs["formRef"]?.validateField('productModelId');
  }
};
const routeOptions = ref([]);
const bindRouteLoading = ref(false);
const fetchRouteOptions = (productModelId) => {
  formState.value.routeId = undefined;
  routeOptions.value = []
  bindRouteLoading.value = true;
  listProcessRoute({ productModelId: productModelId }).then(res => {
    routeOptions.value = res.data || [];
  }).finally(() => {
    bindRouteLoading.value = false;
  })
}
// 工艺路线工序列表
const processRouteItemsOptions = ref([]);
const fetchProcessRouteItems = (routeId) => {
  processRouteItemsOptions.value = [];
  findProcessRouteItemList({ routeId: routeId }).then(res => {
    const items = res.data || [];
    processRouteItemsOptions.value = items;
    // 自动添加工序
    processRouteItems.value = items.map(item => ({
      processId: item.processId,
      processName: item.processName,
      productModelId: item.productModelId,
      userPower: item.userPower ? item.userPower.split(',') : [],
      planStartTime: "",
      planEndTime: "",
      planNum: 1,
      isQuality: item.isQuality || false,
    }));
  });
};
// BOM列表弹窗相关
const showBomDialog = ref(false);
const bomList = ref([]);
const currentProductId = ref(null);
const fetchBomList = (productModelId) => {
  currentProductId.value = productModelId;
  listProductBom({ productModelId: productModelId }).then(res => {
    const bomData = res.data?.records || [];
    if (bomData.length === 1) {
      // 只有一个BOM,直接查询物料清单
      fetchMaterialList(bomData[0].id);
    } else if (bomData.length > 1) {
      // 多个BOM,弹出选择框
      bomList.value = bomData;
      showBomDialog.value = true;
    }
  });
};
const handleBomSelect = (bom) => {
  showBomDialog.value = false;
  fetchMaterialList(bom.id);
};
const fetchMaterialList = (bomId) => {
  listByBomIdIsParent(bomId).then(res => {
    const materials = res.data || [];
    const demandQty = formState.value.quantity || 1;
    productStructureRecords.value = materials.map(item => ({
      parentId: item.parentId,
      productModelId: item.productModelId,
      model: item.model || '',
      productName: item.productName || '',
      productOrderId: undefined,
      processId: undefined,
      unitQuantity: item.unitQuantity || 1,
      demandedQuantity: (item.unitQuantity || 1) * demandQty,
      unit: item.unit || '',
      bomId: item.bomId,
    }));
  });
};
// 监听需求数量变化,重新计算物料需求数量
watch(() => formState.value.quantity, (newQty) => {
  if (productStructureRecords.value.length > 0 && newQty) {
    productStructureRecords.value = productStructureRecords.value.map(item => ({
      ...item,
      demandedQuantity: (item.unitQuantity || 1) * newQty
    }));
  }
});
// 工序选择变化处理
const handleProcessChange = (processId, row) => {
  const selectedProcess = processRouteItemsOptions.value.find(item => item.processId === processId);
  if (selectedProcess) {
    row.processName = selectedProcess.processName;
    row.productModelId = selectedProcess.productModelId;
    row.isQuality = selectedProcess.isQuality || false;
    // userPower是逗号分隔的用户名,转换为数组
    if (selectedProcess.userPower) {
      row.userPower = selectedProcess.userPower.split(',');
    } else {
      row.userPower = [];
    }
  }
};
// 添加工序
const addProductionTask = () => {
  if (!formState.value.productModelId) {
    proxy.$modal.msgWarning("请先选择产品");
    return;
  }
  if (processRouteItemsOptions.value.length === 0) {
    proxy.$modal.msgWarning("请先选择产品以获取工序列表");
    return;
  }
  processRouteItems.value.push({
    processId: undefined,
    processName: "",
    processNo: "",
    productModelId: undefined,
    userPower: [],
    planStartTime: "",
    planEndTime: "",
    planNum: 1,
    isQuality: false,
  });
};
// 删除工序路线明细
const removeProcessRouteItem = (index) => {
  processRouteItems.value.splice(index, 1);
};
// 添加用料 - 弹出产品选择框
const addMaterialItem = () => {
  showMaterialProductDialog.value = true;
};
// 处理用料产品选择
const handleMaterialProductSelect = (products) => {
  if (products && products.length > 0) {
    products.forEach(product => {
      productStructureRecords.value.push({
        productModelId: product.id,
        parentId: undefined,
        productOrderId: undefined,
        processId: undefined,
        model: product.model || '',
        productName: product.productName || '',
        unitQuantity: 1,
        demandedQuantity: 1,
        unit: product.unit,
        bomId: undefined,
      });
    });
  }
  showMaterialProductDialog.value = false;
};
// 删除物料清单
const removeProductStructureRecord = (index) => {
  productStructureRecords.value.splice(index, 1);
};
const handleDrawingBeforeUpload = (file) => {
  const isAllowed = [
    'application/pdf',
    'image/jpeg',
    'image/jpg',
    'image/png',
    'application/dwg'
  ].includes(file.type) || file.name.endsWith('.dwg');
  const isLt10M = file.size / 1024 / 1024 < 10;
  if (!isAllowed) {
    proxy.$modal.msgError("只能上传 pdf、jpg、jpeg、png、dwg 格式的文件!");
    return false;
  }
  if (!isLt10M) {
    proxy.$modal.msgError("上传文件大小不能超过 10MB!");
    return false;
  }
  return true;
};
const handleDrawingUploadSuccess = (response, file, fileList) => {
  console.log('上传成功响应', response);
  console.log('response.data', response.data);
  if (response.code === 200) {
    formState.value.tempFileIds = [response.data?.tempId];
    formState.value.salesLedgerFiles = [{
      tempId: response.data?.tempId,
      originalName: response.data?.originalName || file.name,
      tempPath: response.data?.tempPath,
      type: response.data?.type || 14
    }];
    proxy.$modal.msgSuccess("上传成功");
  } else {
    proxy.$modal.msgError(response.msg || "上传失败");
  }
};
const handleDrawingRemove = (file) => {
  formState.value.tempFileIds = [];
  formState.value.salesLedgerFiles = [];
};
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
@@ -179,7 +618,21 @@
        return;
      }
      addProductOrder(formState.value).then(res => {
      // 处理提交数据 - 将userPower数组转换为逗号分隔的字符串
      const processedProcessRouteItems = processRouteItems.value.map(item => ({
        ...item,
        userPower: Array.isArray(item.userPower) ? item.userPower.join(',') : item.userPower
      }));
      // 组装提交数据
      const submitData = {
        ...formState.value,
        processRouteItems: processedProcessRouteItems,
        productStructureRecords: productStructureRecords.value,
        files: formState.value.salesLedgerFiles,
      };
      addProductOrder(submitData).then(res => {
        // 关闭模态框
        isShow.value = false;
        // 告知父组件已完成
@@ -197,3 +650,115 @@
  isShow,
});
</script>
<style scoped>
.production-order-dialog :deep(.el-dialog__body) {
  padding: 15px 20px;
  max-height: 70vh;
  overflow-y: auto;
}
.section-card {
  background: #f8f9fa;
  border-radius: 8px;
  padding: 15px;
  margin-bottom: 15px;
}
.section-header {
  display: flex;
  align-items: center;
  margin-bottom: 15px;
  padding-bottom: 10px;
  border-bottom: 1px solid #e4e7ed;
}
.section-icon {
  font-size: 18px;
  margin-right: 8px;
}
.section-title-text {
  font-size: 15px;
  font-weight: 600;
  color: #303133;
  flex: 1;
}
.add-btn {
  margin-left: auto;
}
.compact-form :deep(.el-form-item) {
  margin-bottom: 12px;
}
.compact-form :deep(.el-form-item__label) {
  font-size: 13px;
  color: #606266;
}
.select-btn {
  width: 100%;
  justify-content: flex-start;
}
.table-container {
  background: #fff;
  border-radius: 4px;
  padding: 10px;
}
.compact-table :deep(.el-table__cell) {
  padding: 4px 0;
}
.compact-table :deep(.el-input__wrapper),
.compact-table :deep(.el-input-number) {
  width: 100%;
}
.empty-tip {
  padding: 20px 0;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 148px;
  height: 148px;
  text-align: center;
  line-height: 148px;
}
:deep(.el-upload--picture-card) {
  width: 148px;
  height: 148px;
}
:deep(.el-upload-list__item) {
  width: 148px;
  height: 148px;
}
:deep(.el-upload__tip) {
  font-size: 12px;
  color: #909399;
  margin-top: 8px;
}
.upload-inline :deep(.el-upload) {
  display: inline-flex;
}
.upload-inline :deep(.el-upload__tip) {
  margin-top: 4px;
  font-size: 12px;
}
.dialog-footer {
  display: flex;
  justify-content: center;
  gap: 10px;
}
</style>
src/views/productionManagement/productionOrder/index.vue
@@ -3,32 +3,8 @@
    <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-item>
        <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>
        <el-form-item label="规格:">
          <el-input v-model="searchForm.specificationModel"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
@@ -93,6 +69,7 @@
    <new-product-order v-if="isShowNewModal"
                         v-model:visible="isShowNewModal"
                         type="qualified"
                         @completed="handleQuery" />
  </div>
</template>
@@ -106,7 +83,7 @@
    productOrderListPage,
    listProcessRoute,
    bindingRoute,
    listProcessBom, delProductOrder,
    listProcessBom, delProductOrder, startOrPause,
  } from "@/api/productionManagement/productionOrder.js";
  import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
  import {fileDel} from "@/api/financialManagement/revenueManagement.js";
@@ -125,16 +102,6 @@
      width: '120px',
    },
    {
      label: "销售合同号",
      prop: "salesContractNo",
      width: '150px',
    },
    {
      label: "客户名称",
      prop: "customerName",
      width: '200px',
    },
    {
      label: "产品名称",
      prop: "productCategory",
      width: '120px',
@@ -143,11 +110,6 @@
      label: "图纸编号",
      prop: "specificationModel",
      width: '160px',
    },
    {
      label: "规格型号",
      prop: "drawingNumber",
      width: '120px',
    },
    {
      label: "工艺路线编号",
@@ -196,8 +158,24 @@
      label: "操作",
      align: "center",
      fixed: "right",
      width: 200,
      width: 240,
      operation: [
        {
          name: "开始",
          type: "text",
          showHide: row => row.status === '待生产',
          clickFun: row => {
            handleStartOrPause(row);
          },
        },
        {
          name: "暂停",
          type: "text",
          showHide: row => row.status === '生产中',
          clickFun: row => {
            handleStartOrPause(row);
          },
        },
        {
          name: "工艺路线",
          type: "text",
@@ -214,7 +192,7 @@
          },
        },
        {
          name: "产品结构",
          name: "物料清单",
          type: "text",
          clickFun: row => {
            showProductStructure(row);
@@ -234,11 +212,8 @@
  const data = reactive({
    searchForm: {
      customerName: "",
      salesContractNo: "",
      projectName: "",
      productCategory: "",
      specificationModel: "",
    },
  });
  const { searchForm } = toRefs(data);
@@ -266,6 +241,8 @@
    if (row.isFh) return '';
    const diff = row.deliveryDaysDiff;
    if (diff === undefined || diff === null || diff === '' || diff < 0) return '';
    if (diff === 15) {
      return 'yellow';
    } else if (diff === 10) {
@@ -372,11 +349,12 @@
    const orderId = row.id;
    try {
      const res = await getOrderProcessRouteMain(orderId);
      const data = res.data || {};
      if (!data || !data.id) {
      const dataList = res.data || [];
      if (!dataList || dataList.length === 0 || !dataList[0].id) {
        proxy.$modal.msgWarning("未找到关联的工艺路线");
        return;
      }
      const data = dataList[0];
      router.push({
        path: "/productionManagement/processRouteItem",
        query: {
@@ -397,12 +375,37 @@
    }
  };
  const showProductStructure = row => {
  const handleStartOrPause = async (row) => {
    const operation = row.status === '待生产' ? 1 : 2;
    const operationText = operation === 1 ? "开始" : "暂停";
    try {
      await startOrPause({ id: row.id, operation });
      proxy.$modal.msgSuccess(`${operationText}成功`);
      getList();
    } catch (e) {
      console.error(`${operationText}失败:`, e);
      proxy.$modal.msgError(`${operationText}失败`);
    }
  };
  const showProductStructure = async row => {
    let bomNo = row.bomNo || "";
    if (!bomNo && row.id) {
      try {
        const res = await getOrderProcessRouteMain(row.id);
        const dataList = res.data || [];
        if (dataList && dataList.length > 0 && dataList[0].bomNo) {
          bomNo = dataList[0].bomNo;
        }
      } catch (e) {
        console.error("获取BOM编号失败:", e);
      }
    }
    router.push({
      path: "/productionManagement/productStructureDetail",
      query: {
        id: row.id,
        bomNo: row.bomNo || "",
        bomNo: bomNo,
        drawingNumber: row.drawingNumber || "",
        productName: row.productCategory || "",
        productModelName: row.specificationModel || "",
@@ -466,19 +469,19 @@
  align-items: start;
}
::v-deep .yellow {
:deep(.yellow) {
  background-color: #FAF0DE;
}
::v-deep .pink {
:deep(.pink) {
  background-color: #FAE1DE;
}
::v-deep .red {
  background-color: #f80202;
:deep(.red) {
  background-color: #FFCCCC;
}
::v-deep .purple{
:deep(.purple){
  background-color: #F4DEFA;
}
</style>
src/views/productionManagement/productionProcess/Edit.vue
@@ -109,12 +109,6 @@
// 监听 record 变化,更新表单数据
watch(() => props.record, (newRecord) => {
  if (newRecord && isShow.value) {
    const userPowerNames = newRecord.userPower ? newRecord.userPower.split(',') : [];
    const userPowerIds = userPowerNames.map(name => {
      const user = findUserByName(name);
      return user ? user.id : null;
    }).filter(id => id !== null);
    formState.value = {
      id: newRecord.id,
      name: newRecord.name || '',
@@ -122,11 +116,46 @@
      remark: newRecord.remark || '',
      salaryQuota: newRecord.salaryQuota || '',
      isQuality: newRecord.isQuality,
      userPower: userPowerIds,
      userPower: [],
    };
    // 等待 staffList 加载完成后,再查找用户ID
    if (staffList.value.length > 0) {
      setUserPowerIds(newRecord.userPower);
    } else {
      // staffList 还没加载,等待一下再查找
      const checkStaffList = () => {
        if (staffList.value.length > 0) {
          setUserPowerIds(newRecord.userPower);
        } else {
          setTimeout(checkStaffList, 100);
        }
      };
      checkStaffList();
    }
  }
}, { immediate: true, deep: true });
// 根据用户名查找用户ID
const setUserPowerIds = (userPowerStr) => {
  const userPowerNames = userPowerStr ? userPowerStr.split(',') : [];
  const userPowerIds = [];
  const findUserIds = (nodes, targetName) => {
    nodes.forEach(node => {
      if (node.isUser && node.label === targetName) {
        userPowerIds.push(node.id);
      }
      if (node.children && node.children.length > 0) {
        findUserIds(node.children, targetName);
      }
    });
  };
  userPowerNames.forEach(name => {
    findUserIds(staffList.value, name);
  });
  formState.value.userPower = userPowerIds;
};
let { proxy } = getCurrentInstance()
const closeModal = () => {
src/views/productionManagement/productionReporting/index.vue
@@ -168,11 +168,6 @@
      width: 120,
    },
    {
      label: "销售合同号",
      prop: "salesContractNo",
      width: 120,
    },
    {
      label: "产品名称",
      prop: "productName",
      width: 120,
@@ -181,11 +176,6 @@
      label: "产品图纸编号",
      prop: "productModelName",
      width: 160,
    },
    {
      label: "产品规格型号",
      prop: "drawingNumber",
      width: 120,
    },
    {
      label: "产出数量",
src/views/productionManagement/workOrder/index.vue
@@ -255,10 +255,6 @@
      prop: "model"
    },
    {
      label: "规格型号",
      prop: "drawingNumber",
    },
    {
      label: "单位",
      prop: "unit",
    },
@@ -333,7 +329,7 @@
          clickFun: row => {
            showReportDialog(row);
          },
          disabled: row => row.planQuantity <= 0,
          disabled: row => row.status === 1 || row.status === 3 || row.planQuantity <= 0,
        },
      ],
    },
src/views/salesManagement/invoiceRegistration/index.vue
@@ -761,7 +761,7 @@
.justify-between {
    justify-content: space-between;
}
::v-deep(.el-checkbox__label) {
:deep(.el-checkbox__label) {
    font-weight: bold;
}
</style>
src/views/salesManagement/receiptPayment/index.vue
@@ -589,7 +589,7 @@
.table_list {
  margin-top: unset;
}
::v-deep(.el-checkbox__label) {
:deep(.el-checkbox__label) {
  font-weight: bold;
}
.actions {
src/views/salesManagement/salesLedger/index.vue
@@ -2194,19 +2194,19 @@
    margin-left: 10px;
}
::v-deep .yellow {
:deep(.yellow) {
  background-color: #FAF0DE;
}
::v-deep .pink {
:deep(.pink) {
  background-color: #FAE1DE;
}
::v-deep .red {
:deep(.red) {
  background-color: #FAE1DE;
}
::v-deep .purple{
:deep(.purple){
  background-color: #F4DEFA;
}