<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="生产令号" prop="productionOrderNo">
        <el-input v-model="queryParams.productionOrderNo" placeholder="请输入生产令号" clearable
          @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="项目名称" prop="productionName">
        <el-input v-model="queryParams.productionName" placeholder="请输入项目名称" clearable
          @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="图号" prop="drawingNo">
        <el-input v-model="queryParams.drawingNo" placeholder="请输入图号" clearable @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="名称" prop="drawingName">
        <el-input v-model="queryParams.drawingName" placeholder="请输入名称" clearable @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="是否延期" prop="whetherDelay">
        <el-select v-model="queryParams.whetherDelay" placeholder="请选择" clearable>
          <el-option label="是" :value="1" />
          <el-option label="否" :value="0" />
        </el-select>
      </el-form-item>
      <el-form-item label="项目类型" prop="isEnterpriseStandardList">
        <el-select v-model="queryParams.isEnterpriseStandardList" placeholder="请选择" clearable multiple collapse-tags>
          <el-option label="产品 (CP)" value="产品" />
          <el-option label="非标 (FB)" value="非标" />
          <el-option label="变更 (BG)" value="变更" />
          <el-option label="研发 (EY)" value="研发" />
          <el-option label="企标 (QB)" value="企标" />
          <el-option label="库存 (KC)" value="库存" />
          <el-option label="固资 (GZ)" value="固资" />
          <el-option label="展品 (ZP)" value="展品" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
          v-hasPermi="['system:orderPro:add']">新增
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate"
          v-hasPermi="['system:orderPro:edit']">修改
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete"
          v-hasPermi="['system:orderPro:remove']">删除
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
          v-hasPermi="['system:orderPro:export']">导出
        </el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table v-loading="loading" :data="orderProList" ref="orderTable" row-key="id"
      @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
      <!-- <el-table-column label="序号" align="center" prop="id" v-if="true"/> -->
      <el-table-column label="生产令号" align="center" prop="productionOrderNo">
        <template slot-scope="scope">
          <el-link @click="productionCodeHandle(scope.row)" type="primary">
            {{ scope.row.productionOrderNo }}
          </el-link>
        </template>
      </el-table-column>

      <el-table-column label="执行单图" align="center" min-width="100">
        <template slot-scope="scope">
          <div v-if="getImageList(scope.row).length" style="display:flex; flex-wrap:wrap; gap:6px; justify-content:center">
            <el-image
              v-for="(img,idx) in getImageList(scope.row)"
              :key="idx"
              :src="img"
              :preview-src-list="getImageList(scope.row)"
              fit="cover"
              style="width: 80px; height: 80px; object-fit: cover; border-radius: 4px"
            />
          </div>
          <span v-else style="color: #909399; font-size: 12px">暂无执行单图</span>
        </template>
      </el-table-column>

      <el-table-column label="项目状态" align="center" prop="imCategory">
        <template slot-scope="scope">
          <dict-tag :options="dict.type.process_status" :value="scope.row.bomStatus" />
        </template>
      </el-table-column>
      <el-table-column label="项目名称" align="center" prop="productionName" />
      <el-table-column label="是否延期" align="center" prop="whetherDelay">
        <template slot-scope="scope">
          <el-tag size="mini" :type="Number(scope.row.whetherDelay) === 1 ? 'danger' : 'success'">
            {{ Number(scope.row.whetherDelay) === 1 ? '是' : '否' }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column label="延期天数" align="center" prop="delayDay">
        <template slot-scope="scope">
          <span style="color:#ff4757; font-weight:600;">
            {{ scope.row.delayDay != null ? scope.row.delayDay : '-' }}
          </span>
        </template>
      </el-table-column>
      <el-table-column label="计量单位" align="center" prop="unit" />
      <el-table-column label="DWG" align="center" prop="quantity">
        <template slot-scope="scope">
          <el-tag size="mini" :type="Number(scope.row.quantity) === 1 ? 'success' : 'info'">
            {{ Number(scope.row.quantity) === 1 ? '允许' : '不允许' }}
          </el-tag>
        </template>
      </el-table-column>
      <!-- <el-table-column label="产品数量" align="center" prop="quantity"/>-->
      <el-table-column label="项目类型" align="center" prop="isEnterpriseStandard">
        <template slot-scope="scope">
          <span>{{ getProjectTypeLabel(scope.row.isEnterpriseStandard) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="创建人" align="center" prop="createBy"/>
      <el-table-column label="出图时间" align="center" prop="drawingTime" width="180">
        <template slot-scope="scope">
          <span>{{ drawingTime(scope.row, 'drawingTime', '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="项目完成时间" align="center" prop="projectEndTime" width="180">
        <template slot-scope="scope">
          <span>{{ formatStepTime(scope.row, 'projectEndTime', '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>

      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
        <template slot-scope="scope">
          <span>{{ formatStepTime(scope.row, 'createTime', '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
        </template>
      </el-table-column>

<!--      <el-table-column label="工艺上传更新时间" align="center" prop="routeUptime" width="180">
        <template slot-scope="scope">
          <span>{{ formatStepTime(scope.row, 'routeUptime', '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="排产计划更新时间" align="center" prop="planUptime" width="180">
        <template slot-scope="scope">
          <span>{{ formatStepTime(scope.row, 'planUptime', '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="BOM/物料更新时间" align="center" prop="bomUptime" width="180">
        <template slot-scope="scope">
          <span>{{ formatStepTime(scope.row, 'bomUptime', '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
        </template>
      </el-table-column>-->

      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="180">
        <template slot-scope="scope">
          <el-dropdown :split-button="true" size="small" type="primary" @click="handleDetail(scope.row.productionOrderNo)"
            v-hasPermi="['system:orderPro:remove']"
            @command="(command) => handleCommand(command, scope.row)">
            查看工艺
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item command="生产计划">生产计划</el-dropdown-item>
              <el-dropdown-item command="BOM" v-hasPermi="['system:orderPro:remove']">查看BOM</el-dropdown-item>
              <el-dropdown-item :divided="true" command="修改" v-hasPermi="['system:orderPro:edit']">修改
              </el-dropdown-item>
              <el-dropdown-item :divided="true" command="删除" v-hasPermi="['system:orderPro:remove']">删除
              </el-dropdown-item>
              <el-dropdown-item :divided="true" command="清空项目" v-hasPermi="['system:orderPro:clear']">清空项目
              </el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
        </template>
      </el-table-column>
    </el-table>

    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
      @pagination="getList" />
    <el-drawer :title="getDrawerTitle()" :visible.sync="drawer" :direction="direction" :before-close="handleClose"
      :size="1500">
      <el-divider content-position="center">
        <span style="color: #67c23a; font-weight: 600; font-size: 14px">
          <i class="el-icon-picture" style="margin-right: 6px"></i>
          选择产品
        </span>
      </el-divider>
      <div style="padding: 20px">
        <!-- 第一部分：销齿链型号表格管理 -->
        <div style="
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 10px;
          ">
          <el-button type="primary" @click="openAddChainDialog" size="mini"
            v-hasPermi="['system:save:add']">新增</el-button>
          <el-upload :action="dwgUploadUrl" :headers="uploadHeaders" :show-file-list="false" accept=".dwg"
            :http-request="batchUploadDwg" :before-upload="beforeBatchDwgUpload">
            <el-button type="success" size="mini" :disabled="!selectedChainIds.length"
              v-hasPermi="['system:orderPro:uploadDwg']">
              <i class="el-icon-upload2"></i> 上传图纸
            </el-button>
          </el-upload>
          <el-button
            type="primary"
            size="mini"
            :disabled="!selectedChainIds.length"
            @click="downloadSelectedDwgZip"
            v-hasPermi="['system:orderPro:downloadZip']"
          >
            <i class="el-icon-download"></i> 下载图纸ZIP
          </el-button>
          <el-button
            type="warning"
            size="mini"
            @click="pushDingTalkCard"
            v-hasPermi="['system:orderPro:pushDingTalkCard']"
          >
            <i class="el-icon-message"></i> 推送钉钉
          </el-button>
          <el-upload
            :show-file-list="false"
            :http-request="importProducts"
            :before-upload="beforeElectricalBomUpload"
            accept=".xls,.xlsx"
          >
            <el-button
              type="info"
              size="mini"
              v-hasPermi="['system:save:import']"
            >
              <i class="el-icon-upload"></i> 导入产品
            </el-button>
          </el-upload>
        </div>
        <el-table :data="chainTable" border stripe size="small" style="width: 100%; margin-bottom: 10px"
          highlight-current-row :header-cell-style="{ fontWeight: 'bold', background: '#f5f7fa' }"
          @selection-change="handleChainSelectionChange">
          <el-table-column type="selection" width="55" />
          <el-table-column prop="figureNumber" label="产品型号" min-width="100" />
          <el-table-column prop="figureName" label="产品名称" min-width="140" />
          <el-table-column prop="jdInventory" label="行程" min-width="80" />
          <el-table-column prop="figureNum" label="数量" min-width="60" />
          <el-table-column prop="drawPath" label="文件路径" min-width="120" :show-overflow-tooltip="true" />
          <el-table-column prop="productType" label="产品类型" min-width="100" />
          <el-table-column prop="fileType" label="文件类型" min-width="100" />
          <el-table-column label="出图人" prop="drawBy" min-width="100">
            <template slot-scope="scope">
              <dict-tag :options="dict.type.plan_proer" :value="scope.row.drawBy" />
            </template>
          </el-table-column>
          <el-table-column label="是否延期" prop="whetherDelay" min-width="90" align="center">
            <template slot-scope="scope">
              <el-tag size="mini" :type="Number(scope.row.whetherDelay) === 1 ? 'danger' : 'success'">
                {{ Number(scope.row.whetherDelay) === 1 ? '是' : '否' }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="delayDay" label="延期天数" min-width="90" align="right" />
          <el-table-column label="出图时间" prop="drawTime" min-width="160">
            <template slot-scope="scope">
              <span>{{ formatStepTime(scope.row, 'drawTime', '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
            </template>
          </el-table-column>
          <el-table-column label="上传图纸时间" prop="uploadTime" min-width="160">
            <template slot-scope="scope">
              <span>{{ formatStepTime(scope.row, 'uploadTime', '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="200">
            <template slot-scope="scope">
              <div style="display: flex; align-items: center; gap: 8px">
                <el-button type="text" size="mini" @click="editChain(scope.$index, scope.row)"
                  v-hasPermi="['system:save:edit']">
                  <i class="el-icon-edit"></i> 修改
                </el-button>

                <el-button type="text" size="mini" @click="removeChain(scope.$index)"
                  v-hasPermi="['system:save:remove']">
                  <i class="el-icon-delete"></i> 删除
                </el-button>
              </div>
            </template>
          </el-table-column>
        </el-table>
        <el-divider content-position="center">
          <span style="color: #67c23a; font-weight: 600; font-size: 14px">
            <i class="el-icon-picture" style="margin-right: 6px"></i>
            执行出图
          </span>
        </el-divider>
        <!-- 第二部分：执行出图命令及日志展示 -->
        <div style="margin-bottom: 20px">
          <div style="
              display: flex;
              align-items: center;
              gap: 10px;
              margin-bottom: 10px;
            ">
            <el-button type="success" :loading="drawLoading" @click="executeDrawCommand"
              v-hasPermi="['system:orderPro:executDrawing']">
              执行出图
            </el-button>
            <el-button type="primary" @click="downloadPDF" v-hasPermi="['system:orderPro:uploadPDF']">
              下载PDF
            </el-button>
            <el-button type="warning" @click="downloadRoute" v-hasPermi="['system:orderPro:exportRoute2']">
              下载金蝶工艺计划表
            </el-button>
            <el-button type="primary" @click="downloadRoute3" v-hasPermi="['system:orderPro:exportRoute3']">
              导出工艺计划表
            </el-button>
            <el-button type="danger" @click="pushProcessInbound" v-hasPermi="['system:route:updateProductionOrders']">
              推送工序计划单入库
            </el-button>
            <el-button type="success" @click="pushPurchaseInbound" v-hasPermi="['system:route:updateProductionOrders']">
              推送入库采购申请
            </el-button>
            <el-upload :show-file-list="false" :http-request="uploadElectricalBom"
              :before-upload="beforeElectricalBomUpload" accept=".xls,.xlsx" style="display: inline-block">
              <el-button type="info" v-hasPermi="['system:details:importElectricalBom']">
                导入电气BOM
              </el-button>
            </el-upload>
          </div>
          <el-progress :text-inside="true" :stroke-width="26" :percentage="percentage"></el-progress>
          <el-card shadow="never" style="
              background: #f9f9f9;
              min-height: 120px;
              max-height: 220px;
              overflow-y: auto;
            ">
            <div style="font-size: 13px; color: #333; white-space: pre-line">
              <template v-if="drawLog">{{ drawLog }}</template>
              <template v-else>暂无日志</template>
            </div>
          </el-card>
        </div>
        <el-divider content-position="center">
          <span style="color: #409eff; font-weight: 600; font-size: 14px">
            <i class="el-icon-more" style="margin-right: 6px"></i>
            MRP运算结果复查
          </span>
        </el-divider>
        <!-- 第三部分：预留 -->
        <div style="color: #aaa; font-size: 13px"></div>
        <!-- 第三部分：MRP运算结果复查表格 -->
        <el-card class="box-card mrp-result-card" style="margin-bottom: 20px">
          <div slot="header" class="clearfix">
            <span style="font-weight: 600; color: #303133">
              <i class="el-icon-data-analysis" style="margin-right: 8px; color: #409eff"></i>
              MRP运算结果复查
            </span>
            <div style="
                float: right;
                display: flex;
                align-items: center;
                gap: 12px;
              ">
              <!-- 运算状态显示 -->
              <div v-if="loadingMRP" class="mrp-status-indicator">
                <i class="el-icon-loading" style="margin-right: 6px"></i>
                <span>{{ mrpOperationStatus || "MRP运算进行中..." }}</span>
              </div>

              <!-- 运算结果按钮 -->
              <el-button type="primary" size="medium" :loading="loadingMRP" :disabled="loadingMRP"
                @click="getMRPResults()" class="mrp-calculate-btn">
                {{ loadingMRP ? "核算中..." : "开始核算" }}
              </el-button>

              <!-- 搜索输入框 -->
              <el-input v-model="mrpSearchKeyword" placeholder="搜索物料名称或编码" style="width: 220px" size="small" clearable
                @input="handleMrpSearch" @clear="clearMrpSearch" class="mrp-search-input">
                <i slot="prefix" class="el-input__icon el-icon-search"></i>
              </el-input>

              <!-- 导出按钮 -->
              <el-button type="success" size="small" icon="el-icon-download" @click="handleExportMrp"
                :loading="exportLoading" :disabled="!productionObj || !productionObj.id" class="mrp-export-btn" :title="productionObj && productionObj.id
                    ? `导出${productionObj.productionOrderNo}的MRP数据`
                    : '请先选择生产令号'
                  ">
                导出Excel
              </el-button>

              <el-button type="text" @click="handleRefreshMrpData">
                <i class="el-icon-refresh"></i> 刷新
              </el-button>
            </div>
          </div>
          <!-- 统计信息 -->
          <div style="
              margin-bottom: 15px;
              padding: 15px;
              background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
              border-radius: 8px;
              border-left: 4px solid #409eff;
            ">
            <el-row :gutter="20">
              <el-col :span="6">
                <div style="text-align: center">
                  <div style="font-size: 24px; font-weight: bold; color: #409eff">
                    {{ mrpStats.totalMaterials }}
                  </div>
                  <div style="font-size: 12px; color: #606266">总物料数</div>
                </div>
              </el-col>
              <el-col :span="6">
                <div style="text-align: center">
                  <div style="font-size: 24px; font-weight: bold; color: #67c23a">
                    {{ mrpStats.positiveStock }}
                  </div>
                  <div style="font-size: 12px; color: #606266">正库存</div>
                </div>
              </el-col>
              <el-col :span="6">
                <div style="text-align: center">
                  <div style="font-size: 24px; font-weight: bold; color: #f56c6c">
                    {{ mrpStats.negativeStock }}
                  </div>
                  <div style="font-size: 12px; color: #606266">负库存</div>
                </div>
              </el-col>
              <el-col :span="6">
                <div style="text-align: center">
                  <div style="font-size: 24px; font-weight: bold; color: #e6a23c">
                    {{ mrpStats.zeroStock }}
                  </div>
                  <div style="font-size: 12px; color: #606266">零库存</div>
                </div>
              </el-col>
            </el-row>
          </div>
          <el-table :data="mrpResultData" style="width: 100%"
            :header-cell-style="{ background: '#f5f7fa', color: '#606266' }" :row-class-name="getMrpRowClass" border
            stripe size="small" v-loading="mrpLoading">
            <el-table-column prop="materialCode" label="物料编码" min-width="120" show-overflow-tooltip />
            <el-table-column prop="materialName" label="物料名称" min-width="100" show-overflow-tooltip />
            <el-table-column label="项目本批数" width="130" align="right">
              <template slot-scope="scope">
                <span style="color: #e6a23c">
                  {{ formatFraction(scope.row.purchaseNotInStock) }}
                </span>
              </template>
            </el-table-column>

            <el-table-column prop="availableStock" label="本项目可用库存" width="120" align="right">
              <template slot-scope="scope">
                <span :style="{
                  color: scope.row.availableStock < 0 ? '#f56c6c' : '#67c23a',
                }">
                  {{ scope.row.availableStock.toFixed(3) }}
                </span>
              </template>
            </el-table-column>
            <el-table-column prop="realTimeStock" label="当前可用库存" width="100" align="right">
              <template slot-scope="scope">
                <span :style="{
                  color: scope.row.realTimeStock < 0 ? '#f56c6c' : '#67c23a',
                }">
                  {{ scope.row.realTimeStock.toFixed(3) }}
                </span>
              </template>
            </el-table-column>
            <el-table-column prop="prodNotInStock" label="本次生产未入库" width="130" align="right">
              <template slot-scope="scope">
                <span style="color: #e6a23c">{{
                  scope.row.prodNotInStock.toFixed(3)
                }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="purchaseRequestQty" label="本项目采购申请" width="130" align="right">
              <template slot-scope="scope">
                <span style="color: #409eff">{{
                  scope.row.purchaseRequestQty.toFixed(3)
                }}</span>
              </template>
            </el-table-column>

            <el-table-column prop="childNotPickedQty" label="子项未领料数量" width="130" align="right">
              <template slot-scope="scope">
                <span style="color: #f56c6c">{{
                  scope.row.childNotPickedQty.toFixed(3)
                }}</span>
              </template>
            </el-table-column>
            <el-table-column prop="createTime" label="创建时间" width="160" align="center">
              <template slot-scope="scope">
                {{ formatDateTime(scope.row.createTime) }}
              </template>
            </el-table-column>
            <el-table-column prop="updateTime" label="更新时间" width="160" align="center">
              <template slot-scope="scope">
                {{ formatDateTime(scope.row.updateTime) }}
              </template>
            </el-table-column>
          </el-table>

          <!-- 分页 -->
          <el-pagination @size-change="handleMrpSizeChange" @current-change="handleMrpCurrentChange"
            :current-page="mrpQueryParams.pageNum" :page-sizes="[10, 20, 50, 100]" :page-size="mrpQueryParams.pageSize"
            layout="total, sizes, prev, pager, next, jumper" :total="mrpTotal"
            style="margin-top: 20px; text-align: right" />
        </el-card>
        <!-- 第四部分 工艺上传日志 -->
        <el-card shadow="never" style="margin-top: 20px">
          <div slot="header" class="clearfix">
            <span style="font-weight: 600; font-size: 16px">工艺上传日志</span>
          </div>

          <!-- 概览统计 -->
          <div style="
              margin-bottom: 16px;
              padding: 12px;
              background: #f5f7fa;
              border-radius: 4px;
              display: flex;
              gap: 24px;
              align-items: center;
              flex-wrap: wrap;
            ">
            <div style="display: flex; align-items: center; gap: 8px">
              <el-tag type="success" size="small">成功</el-tag>
              <span style="font-weight: 600; color: #67c23a">{{
                processLogCounts.success
              }}</span>
            </div>
            <div style="display: flex; align-items: center; gap: 8px">
              <el-tag type="warning" size="small">重复</el-tag>
              <span style="font-weight: 600; color: #e6a23c">{{
                processLogCounts.duplicate
              }}</span>
            </div>
            <div style="display: flex; align-items: center; gap: 8px">
              <el-tag type="danger" size="small">失败</el-tag>
              <span style="font-weight: 600; color: #f56c6c">{{
                processLogCounts.failed
              }}</span>
            </div>
            <div style="margin-left: auto; color: #606266; font-size: 13px">
              <i class="el-icon-time" style="margin-right: 4px"></i>
              <span v-if="processUploadLog.time">{{
                processUploadLog.time
              }}</span>
              <span v-else style="color: #909399">暂无时间信息</span>
            </div>
          </div>

          <!-- 拉取按钮 -->
          <div style="margin-bottom: 16px">
            <el-button type="primary" size="small" @click="fetchProcessLog" :loading="processLogLoading"
              :disabled="!productionObj || !productionObj.id" icon="el-icon-refresh">
              拉取最新日志
            </el-button>
            <el-button size="small" @click="clearProcessLog" icon="el-icon-delete"
              style="margin-left: 8px">清空</el-button>
          </div>

          <!-- 成功上传（结构化显示） -->
          <div v-if="
            processUploadLog.successfulRoutes &&
            processUploadLog.successfulRoutes.length
          " style="margin-bottom: 20px">
            <div style="
                margin-bottom: 12px;
                padding: 8px 12px;
                background: #f0f9ff;
                border-left: 4px solid #67c23a;
                border-radius: 4px;
              ">
              <span style="font-weight: 600; color: #67c23a; font-size: 14px">
                <i class="el-icon-success" style="margin-right: 6px"></i>
                成功上传（{{ processUploadLog.successfulRoutes.length }}）
              </span>
              <span style="margin-left: 12px; color: #909399; font-size: 12px">工序行数：{{ successFlatRows.length }}</span>
            </div>
            <el-table :data="successFlatPagedRows" size="small" border stripe style="width: 100%">
              <el-table-column prop="materialCode" label="物料编码" width="160" show-overflow-tooltip />
              <el-table-column prop="materialName" label="物料名称" width="180" show-overflow-tooltip />
              <el-table-column prop="processNo" label="工序号" width="90" align="center" />
              <el-table-column prop="workCenter" label="工作中心" width="120" show-overflow-tooltip />
              <el-table-column prop="processName" label="工序名称" width="120" show-overflow-tooltip />
              <el-table-column prop="activityUnit" label="工序单位" width="80" align="center" />
              <el-table-column prop="processControl" label="工序控制" width="120" show-overflow-tooltip />
              <el-table-column prop="activityDuration" label="工序时长" width="100" align="center" />
              <el-table-column prop="processDescription" label="工序描述" min-width="150" show-overflow-tooltip />
              <el-table-column prop="processNameDescription" label="材质/备注" width="140" show-overflow-tooltip />
            </el-table>
            <pagination v-show="successFlatRows.length > 0" :total="successFlatRows.length"
              :page.sync="processSuccessPage.pageNum" :limit.sync="processSuccessPage.pageSize" @pagination="() => { }"
              style="margin-top: 12px" />
          </div>

          <!-- 重复 -->
          <div v-if="
            processUploadLog.duplicateRoutes &&
            processUploadLog.duplicateRoutes.length
          " style="margin-bottom: 20px">
            <div style="
                margin-bottom: 12px;
                padding: 8px 12px;
                background: #fdf6ec;
                border-left: 4px solid #e6a23c;
                border-radius: 4px;
              ">
              <span style="font-weight: 600; color: #e6a23c; font-size: 14px">
                <i class="el-icon-warning" style="margin-right: 6px"></i>
                重复（{{ processUploadLog.duplicateRoutes.length }}）
              </span>
            </div>
            <div style="padding: 12px; background: #fafafa; border-radius: 4px">
              <el-tag v-for="(dup, idx) in processUploadLog.duplicateRoutes" :key="idx" type="warning" size="small"
                style="margin-right: 8px; margin-bottom: 8px; padding: 4px 12px">
                {{ dup }}
              </el-tag>
            </div>
          </div>

          <!-- 失败 -->
          <div v-if="
            processUploadLog.failedRoutes &&
            processUploadLog.failedRoutes.length
          ">
            <div style="
                margin-bottom: 12px;
                padding: 8px 12px;
                background: #fef0f0;
                border-left: 4px solid #f56c6c;
                border-radius: 4px;
              ">
              <span style="font-weight: 600; color: #f56c6c; font-size: 14px">
                <i class="el-icon-error" style="margin-right: 6px"></i>
                失败（{{ processUploadLog.failedRoutes.length }}）
              </span>
            </div>
            <el-table :data="processFailedPagedItems" size="small" border stripe style="width: 100%">
              <el-table-column prop="materialCode" label="物料编码" width="160" show-overflow-tooltip />
              <el-table-column prop="errorMessage" label="错误信息" min-width="200" show-overflow-tooltip>
                <template slot-scope="scope">
                  <span style="color: #f56c6c">{{
                    scope.row.errorMessage
                  }}</span>
                </template>
              </el-table-column>
            </el-table>
            <pagination v-show="processUploadLog.failedRoutes &&
              processUploadLog.failedRoutes.length > 0
              " :total="processUploadLog.failedRoutes.length" :page.sync="processFailedPage.pageNum"
              :limit.sync="processFailedPage.pageSize" @pagination="() => { }" style="margin-top: 12px" />
          </div>

          <!-- 空状态 -->
          <el-empty v-if="
            (!processUploadLog.successfulRoutes ||
              processUploadLog.successfulRoutes.length === 0) &&
            (!processUploadLog.duplicateRoutes ||
              processUploadLog.duplicateRoutes.length === 0) &&
            (!processUploadLog.failedRoutes ||
              processUploadLog.failedRoutes.length === 0)
          " description="暂无工艺上传日志数据" :image-size="100" style="padding: 40px 0">
          </el-empty>
        </el-card>

        <!-- 第五部分 BOM上传日志 -->
        <el-card shadow="never" style="margin-top: 20px">
          <div slot="header" class="clearfix">
            <span style="font-weight: 600; font-size: 16px">BOM上传日志</span>
          </div>

          <!-- 概览统计 -->
          <div style="
              margin-bottom: 16px;
              padding: 12px;
              background: #f5f7fa;
              border-radius: 4px;
              display: flex;
              gap: 24px;
              align-items: center;
              flex-wrap: wrap;
            ">
            <div style="display: flex; align-items: center; gap: 8px">
              <el-tag type="success" size="small">成功</el-tag>
              <span style="font-weight: 600; color: #67c23a">{{
                bomLogCounts.success
              }}</span>
            </div>
            <div style="display: flex; align-items: center; gap: 8px">
              <el-tag type="info" size="small">已存在</el-tag>
              <span style="font-weight: 600; color: #909399">{{
                bomLogCounts.exists
              }}</span>
            </div>
            <div style="display: flex; align-items: center; gap: 8px">
              <el-tag type="danger" size="small">失败</el-tag>
              <span style="font-weight: 600; color: #f56c6c">{{
                bomLogCounts.failed
              }}</span>
            </div>
            <div style="margin-left: auto; color: #606266; font-size: 13px">
              <span>总计：{{ bomLogCounts.total }}</span>
            </div>
          </div>

          <!-- 拉取按钮 -->
          <div style="margin-bottom: 16px">
            <el-button type="primary" size="small" @click="fetchBomLog" :loading="bomLogLoading"
              :disabled="!productionObj || !productionObj.productionOrderNo" icon="el-icon-refresh">
              拉取最新日志
            </el-button>
            <el-button size="small" @click="clearBomLog" icon="el-icon-delete" style="margin-left: 8px">清空</el-button>
          </div>

          <!-- 成功上传 -->
          <div v-if="bomLogCounts.success > 0" style="margin-bottom: 20px">
            <div style="
                margin-bottom: 12px;
                padding: 8px 12px;
                background: #f0f9ff;
                border-left: 4px solid #67c23a;
                border-radius: 4px;
              ">
              <span style="font-weight: 600; color: #67c23a; font-size: 14px">
                <i class="el-icon-success" style="margin-right: 6px"></i>
                成功上传（{{ bomLogCounts.success }}）
              </span>
            </div>
            <el-table :data="bomSuccessPagedItems" size="small" border stripe style="width: 100%">
              <el-table-column prop="materialCode" label="物料编码" width="160" show-overflow-tooltip />
              <el-table-column prop="reason" label="说明" min-width="200" show-overflow-tooltip>
                <template slot-scope="scope">
                  <span style="color: #67c23a">{{ scope.row.reason }}</span>
                </template>
              </el-table-column>
            </el-table>
            <pagination v-show="bomLogCounts.success > 0" :total="bomLogCounts.success"
              :page.sync="bomSuccessPage.pageNum" :limit.sync="bomSuccessPage.pageSize" @pagination="() => { }"
              style="margin-top: 12px" />
          </div>

          <!-- 已存在 -->
          <div v-if="bomLogCounts.exists > 0" style="margin-bottom: 20px">
            <div style="
                margin-bottom: 12px;
                padding: 8px 12px;
                background: #f4f4f5;
                border-left: 4px solid #909399;
                border-radius: 4px;
              ">
              <span style="font-weight: 600; color: #909399; font-size: 14px">
                <i class="el-icon-info" style="margin-right: 6px"></i>
                已存在且一致（{{ bomLogCounts.exists }}）
              </span>
            </div>
            <el-table :data="bomExistsPagedItems" size="small" border stripe style="width: 100%">
              <el-table-column prop="materialCode" label="物料编码" width="160" show-overflow-tooltip />
              <el-table-column prop="reason" label="说明" min-width="200" show-overflow-tooltip>
                <template slot-scope="scope">
                  <span style="color: #909399">{{ scope.row.reason }}</span>
                </template>
              </el-table-column>
            </el-table>
            <pagination v-show="bomLogCounts.exists > 0" :total="bomLogCounts.exists" :page.sync="bomExistsPage.pageNum"
              :limit.sync="bomExistsPage.pageSize" @pagination="() => { }" style="margin-top: 12px" />
          </div>

          <!-- 失败 -->
          <div v-if="bomLogCounts.failed > 0" style="margin-bottom: 20px">
            <div style="
                margin-bottom: 12px;
                padding: 8px 12px;
                background: #fef0f0;
                border-left: 4px solid #f56c6c;
                border-radius: 4px;
              ">
              <span style="font-weight: 600; color: #f56c6c; font-size: 14px">
                <i class="el-icon-error" style="margin-right: 6px"></i>
                失败（{{ bomLogCounts.failed }}）
              </span>
            </div>
            <el-table :data="bomFailedPagedItems" size="small" border stripe style="width: 100%">
              <el-table-column prop="materialCode" label="物料编码" width="160" show-overflow-tooltip />
              <el-table-column prop="reason" label="错误信息" min-width="200" show-overflow-tooltip>
                <template slot-scope="scope">
                  <span style="color: #f56c6c">{{
                    formatBomErrorReason(scope.row.reason)
                  }}</span>
                </template>
              </el-table-column>
            </el-table>
            <pagination v-show="bomLogCounts.failed > 0" :total="bomLogCounts.failed" :page.sync="bomFailedPage.pageNum"
              :limit.sync="bomFailedPage.pageSize" @pagination="() => { }" style="margin-top: 12px" />
          </div>

          <!-- 空状态 -->
          <el-empty v-if="bomLogCounts.total === 0" description="暂无BOM上传日志数据" :image-size="100" style="padding: 40px 0">
          </el-empty>
        </el-card>

        <!-- 新增/编辑弹窗 -->
        <el-dialog :title="isEditChain ? '编辑销齿链型号' : '新增销齿链型号'" :visible.sync="chainDialogVisible" width="420px"
          append-to-body>
          <el-form :model="chainForm" label-width="100px">
            <div style="
                margin-bottom: 16px;
                padding: 12px;
                background-color: #f0f9ff;
                border: 1px solid #b3d8ff;
                border-radius: 4px;
                color: #409eff;
                font-size: 13px;
              ">
              <i class="el-icon-info" style="margin-right: 6px"></i>
              <strong>操作说明：</strong>可以先搜索选择已有型号自动填充，或直接手动填写所有必填字段（带*号）
            </div>
            <el-form-item label="销齿链型号">
              <div style="margin-bottom: 8px; color: #909399; font-size: 12px">
                可选择已有型号或手动填写所有信息
              </div>
              <el-select v-model="chainForm.id" filterable remote reserve-keyword :placeholder="isEditChain
                  ? '编辑模式不使用此项'
                  : '请输入行程/类型/名称/产品名（可选）'
                " :remote-method="searchRigidChain" :loading="chainLoading" @change="onRigidChainSelect"
                popper-class="chain-autocomplete-dropdown" :default-first-option="false" :disabled="isEditChain"
                clearable>
                <el-option v-for="item in rigidChainOptions" :key="item.id" :label="item.productName" :value="item.id">
                  <div class="chain-suggestion-item">
                    <div class="chain-name">{{ item.figureNumber }}</div>
                    <div class="chain-info">
                      <span>编码: {{ item.typeName }}</span>
                      <span>产品名: {{ item.productName }}</span>
                      <span>类型: {{ item.type }}</span>
                      <span>行程: {{ item.journey }}</span>
                    </div>
                  </div>
                </el-option>
              </el-select>
            </el-form-item>
            <el-form-item label="生产令号">
              <el-input v-model="chainForm.productionCode" placeholder="请输入生产令号" disabled />
            </el-form-item>
            <el-form-item label="物料类型">
              <el-select v-model="chainForm.productType" placeholder="请选择类型" clearable>
                <el-option label="30S" value="30S" />
                <el-option label="30D" value="30D" />
                <el-option label="35R" value="35R" />
                <el-option label="40S" value="40S" />
                <el-option label="40R" value="40R" />
                <el-option label="60R" value="60R" />
                <el-option label="80R" value="80R" />
                <el-option label="100R" value="100R" />
                <el-option label="125R" value="125R" />
                <el-option label="150R" value="150R" />
              </el-select>
            </el-form-item>
            <el-form-item label="物料编码" required>
              <el-input v-model="chainForm.figureNumber" placeholder="请输入图号" />
            </el-form-item>
            <el-form-item label="物料名称" required>
              <el-input v-model="chainForm.figureName" placeholder="请输入名称" />
            </el-form-item>
            <el-form-item label="物料数量" required>
              <el-input v-model="chainForm.figureNum" placeholder="请输入数量" type="number" min="1" />
            </el-form-item>
            <!-- 文件路径和DWG上传，企标可编辑，非企标只读+可上传 -->
            <el-form-item label="文件路径">
              <div>
                <el-select v-model="chainForm.selectedFileType" placeholder="请选择文件类型" @change="onConfigIniSelect"
                  filterable clearable>
                  <el-option v-for="item in configIniOptions" :key="item.fileType" :label="item.fileType"
                    :value="item.fileType" />
                </el-select>
              </div>
              <div>
                <el-input v-model="chainForm.drawPath" placeholder="请输入文件路径" />
              </div>
            </el-form-item>
            <el-form-item label="文件类型">
              <el-select v-model="chainForm.fileType" placeholder="请选择文件类型" clearable>
                <el-option label="二维" value="二维" />
                <el-option label="三维" value="三维" />
              </el-select>
            </el-form-item>
            <el-form-item label="出图人">
              <el-select v-model="chainForm.drawBy" placeholder="请选择出图人" clearable>
                <el-option
                  v-for="d in dict.type.plan_proer || []"
                  :key="d.value"
                  :label="d.label"
                  :value="d.value"
                />
              </el-select>
            </el-form-item>
            <el-form-item label="出图时间">
              <el-date-picker
                clearable
                v-model="chainForm.drawTime"
                type="datetime"
                value-format="yyyy-MM-dd HH:mm:ss"
                :default-time="'09:00:00'"
                placeholder="请选择出图时间"
              />
            </el-form-item>
          </el-form>
          <div slot="footer" class="dialog-footer" style="text-align: right">
            <el-button @click="chainDialogVisible = false">取 消</el-button>
            <el-button type="primary" @click="saveChainForm">确 定</el-button>
          </div>
        </el-dialog>
      </div>
    </el-drawer>

    <el-drawer title="BOM明细 " :visible.sync="bomDrawer" direction="rtl" :size="1500">
      <div style="padding: 20px">
        <bomInfo v-if="bomDrawer" :params="bomParams" />
      </div>
    </el-drawer>

    <el-drawer title="工艺路线 " :visible.sync="processDrawer" direction="rtl" :size="1500">
      <div style="padding: 20px">
        <processInfo v-if="processDrawer" :params="processParams" />
      </div>
    </el-drawer>

    <!-- 添加或修改项目令号对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="生产令号" prop="productionOrderNo">
          <el-input v-model="form.productionOrderNo" placeholder="请输入生产令号" />
        </el-form-item>
        <el-form-item label="项目名称" prop="productionName">
          <el-input v-model="form.productionName" placeholder="请输入项目名称" />
        </el-form-item>
        <!-- 新增：产品图片上传功能 -->
        <el-form-item label="执行单图">
          <image-upload v-model="form.drawingPath" :limit="9" :file-size="5" />
        </el-form-item>
        <el-form-item label="DWG" prop="quantity">
          <el-switch
            v-model="form.quantity"
            :active-value="1"
            :inactive-value="0"
            active-text="允许下载"
            inactive-text="不允许下载"
          />
        </el-form-item>

        <el-form-item label="完成时间" prop="projectEndTime">
          <el-date-picker clearable v-model="form.projectEndTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="请选择项目完成时间">
          </el-date-picker>
        </el-form-item>
        <el-form-item label="计量单位" prop="unit">
          <el-input v-model="form.unit" placeholder="请输入计量单位" />
        </el-form-item>

        <el-form-item label="工艺上传更新时间" prop="routeUptime" label-width="140px">
          <el-date-picker clearable v-model="form.routeUptime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="请选择时间" />
        </el-form-item>
        <el-form-item label="排产计划更新时间" prop="planUptime" label-width="140px">
          <el-date-picker clearable v-model="form.planUptime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="请选择时间" />
        </el-form-item>
        <el-form-item label="BOM/物料更新时间" prop="bomUptime" label-width="140px">
          <el-date-picker clearable v-model="form.bomUptime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="请选择时间" />
        </el-form-item>
        <!--        &lt;!&ndash; 文件路径输入框，非企标时只读 &ndash;&gt;
                <el-form-item label="文件路径" prop="drawingPath">
                  <el-input v-model="form.drawingPath" :readonly="form.isEnterpriseStandard !== 1"
                            placeholder="企标可填写，非企标请在子表单上传DWG文件"/>
                </el-form-item>-->
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<style>
.el-dropdown {
  vertical-align: top;
}

.el-dropdown+.el-dropdown {
  margin-left: 15px;
}

.el-icon-arrow-down {
  font-size: 12px;
}
</style>

<script>
import {
  listOrderPro,
  getOrderPro,
  delOrderPro,
  addOrderPro,
  updateOrderPro,
  getDrawingTypeDict,
  listSave,
  executDrawing,
  delSave,
  uploadPDF,
  exportRoute,
  exportRoute3,
  getMRPResults,
  getRouteLog,
  getBomUploadStatus,
} from "@/api/system/orderPro";
import { processList } from "../../../api/system/orderPro";
import { listRigidChain } from "@/api/system/pcRigidChain";
import { addProduct, updateProduct } from "@/api/system/orderPro";
import { getstate } from "@/utils/external-api";
import { listConfigIni } from "@/api/system/configIni";
import ImageUpload from "@/components/ImageUpload";
import { getToken } from "@/utils/auth";
import axios from "axios";
import bomInfo from "@/views/system/details/index.vue"; //bom明细
import processInfo from "@/views/system/route/index.vue"; //工艺信息
import { listMrp, exportMrp } from "@/api/system/mrp";
import { importElectricalBom } from "@/api/system/details";
import { updateProductionOrders, updateCgOrders } from "@/api/system/route";
import dayjs from "dayjs";

export default {
  name: "OrderPro",
  dicts: ["process_status", "plan_proer"],
  components: {
    ImageUpload,
    bomInfo,
    processInfo,
  },
  data() {
    return {
      // 定时器
      time: null,
      // 进度
      percentage: 0,
      drawer: false,
      direction: "rtl",
      // 按钮loading
      buttonLoading: false,
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 项目令号表格数据
      orderProList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        productionOrderNo: undefined,
        productionName: undefined,
        drawingNo: undefined,
        drawingName: undefined,
        bomStatus: undefined,
        routeStatus: undefined,
        planEndTime: undefined,
        planStartTime: undefined,
        drawingType: undefined,
        projectEndTime: undefined,
        createBy: undefined,
        unit: undefined,
        quantity: undefined,
        isEnterpriseStandard: undefined,
        isEnterpriseStandardList: undefined,
        whetherDelay: undefined,
        drawingPath: undefined,
        routeUptime: undefined,
        planUptime: undefined,
        bomUptime: undefined,
        drawingTime: undefined,
        bomCount: undefined,
        materialCount: undefined,
        routeCount: undefined,
        drawingBy: undefined,
      },
      // 表单参数
      form: {},
      //bom明细
      bomDrawer: false,
      bomParams: {},
      //工艺信息
      processDrawer: false,
      processParams: {},

      // 图纸类型选项
      drawingTypeOptions: [],
      // MRP运算结果复查相关数据
      mrpResultData: [],
      mrpLoading: false,
      mrpTotal: 0,
      mrpSearchKeyword: "",
      mrpQueryParams: {
        pageNum: 1,
        pageSize: 10,
        materialName: "",
        materialCode: "",
        orderProId: null,
      },
      loadingMRP: false,
      mrpOperationStatus: "", // MRP运算状态
      exportLoading: false, // 导出加载状态
      mrpStats: {
        totalMaterials: 0,
        positiveStock: 0,
        negativeStock: 0,
        zeroStock: 0,
      },
      // 表单校验
      rules: {
        productionOrderNo: [
          { required: true, message: "生产令号不能为空", trigger: "blur" },
        ],
        productionName: [
          { required: true, message: "项目名称不能为空", trigger: "blur" },
        ],
        drawingNo: [
          { required: true, message: "图号不能为空", trigger: "blur" },
        ],
        drawingName: [
          { required: true, message: "名称不能为空", trigger: "blur" },
        ],

        drawingType: [
          {
            validator: (rule, value, callback) => {
              if (this.form.isEnterpriseStandard === 1 && !value) {
                callback(new Error("图纸类型不能为空"));
              } else {
                callback();
              }
            },
            trigger: "change",
          },
        ],
        projectEndTime: [
          { required: true, message: "项目完成时间不能为空", trigger: "blur" },
        ],
        unit: [
          { required: true, message: "计量单位不能为空", trigger: "blur" },
        ],
        quantity: [
          { required: true, message: "数量不能为空", trigger: "blur" },
        ],
        isEnterpriseStandard: [
          {
            required: true,
            message: "是否企标 (0-否, 1-是)不能为空",
            trigger: "blur",
          },
        ],
        /*  drawingPath: [
            {
              validator: (rule, value, callback) => {
                if (!value) {
                  callback(new Error('图纸路径不能为空'));
                }
                // 你可以加更多条件
                callback();
              },
              trigger: "blur"
            }
          ]*/
      },
      productionObj: null,
      id: "",
      // 销齿链型号表单
      chainTable: [], // 销齿链型号表格数据
      chainForm: {
        pid: "",
        productionCode: "",
        productType: "",
        figureNumber: "",
        figureName: "",
        figureNum: "",
        jdInventory: "",
        axialType: "",
        boxType: "",
        selectedFileType: "",
        drawPath: "",
        fileType: "",
        drawBy: "",
        drawTime: "",
        id: undefined,
      },
      chainDialogVisible: false, // 控制新增/编辑弹窗
      isEditChain: false, // 区分新增/编辑
      editChainIndex: null, // 编辑时的索引
      rigidChainOptions: [],
      typeOptions: [],
      journeyOptions: [],
      chainLoading: false,
      selectedChainIds: [],
      drawLog: "", // 出图日志内容
      drawLoading: false, // 出图按钮 loading
      currentPid: null, // 当前项目id
      configIniOptions: [], // 文件类型下拉选项
      dwgUploadUrl: process.env.VUE_APP_BASE_API + "/system/orderPro/uploadDwg",
      uploadHeaders: {
        Authorization: "Bearer " + getToken(),
      },
      // DWG 上传大小阈值（MB），用于前端预校验与提示
      dwgMaxSizeMB: 100,
      // 工艺上传日志
      processUploadLog: {
        time: null,
        successfulRoutes: [],
        duplicateRoutes: [],
        failedRoutes: [],
      },
      processLogLoading: false,
      // 工艺日志分页
      processSuccessPage: { pageNum: 1, pageSize: 10 },
      processFailedPage: { pageNum: 1, pageSize: 10 },
      // BOM上传日志
      bomUploadLog: {
        items: [],
      },
      bomLogLoading: false,
      // BOM日志分页
      bomSuccessPage: { pageNum: 1, pageSize: 10 },
      bomExistsPage: { pageNum: 1, pageSize: 10 },
      bomFailedPage: { pageNum: 1, pageSize: 10 },
    };
  },
  created() {
    this.getList();
    this.getDrawingTypeOptions();
    this.getConfigIniOptions(); // 新增

    this.$api = { addProduct, updateProduct };
  },
  watch: {
  },
  computed: {
    processLogCounts() {
      const success = Array.isArray(this.processUploadLog?.successfulRoutes)
        ? this.processUploadLog.successfulRoutes.length
        : 0;
      const duplicate = Array.isArray(this.processUploadLog?.duplicateRoutes)
        ? this.processUploadLog.duplicateRoutes.length
        : 0;
      const failed = Array.isArray(this.processUploadLog?.failedRoutes)
        ? this.processUploadLog.failedRoutes.length
        : 0;
      return { success, duplicate, failed };
    },
    successFlatRows() {
      const rows = [];
      const list = this.processUploadLog?.successfulRoutes || [];
      list.forEach((item) => {
        const base = {
          materialCode: item.materialCode || "",
          materialName: item.materialName || "",
        };
        const details = Array.isArray(item.processRouteDT)
          ? item.processRouteDT
          : [];
        details.forEach((dt) => {
          // 兼容两种字段命名：
          // 1) 我们的前端命名：processNo / workCenter / processName / activityUnit / processControl / activityDuration / processDescription / processNameDescription
          // 2) 后端K3字段命名：FOperNumber / FWorkCenterId.FName / FProcessId.FName / FActivity1UnitID.FName / FOptCtrlCodeId.FName / FActivity1Qty / FOperDescription / FNumber
          const processNo = dt.processNo ?? dt.FOperNumber ?? dt["FOperNumber"];
          const workCenter =
            dt.workCenter ??
            (dt.FWorkCenterId && dt.FWorkCenterId.FName) ??
            dt["FWorkCenterId.FName"];
          const processName =
            dt.processName ??
            (dt.FProcessId && dt.FProcessId.FName) ??
            dt["FProcessId.FName"];
          const activityUnit =
            dt.activityUnit ??
            (dt.FActivity1UnitID && dt.FActivity1UnitID.FName) ??
            dt["FActivity1UnitID.FName"];
          const processControl =
            dt.processControl ??
            (dt.FOptCtrlCodeId && dt.FOptCtrlCodeId.FName) ??
            dt["FOptCtrlCodeId.FName"];
          const activityDuration =
            dt.activityDuration ?? dt.FActivity1Qty ?? dt["FActivity1Qty"];
          const processDescription =
            dt.processDescription ??
            dt.FOperDescription ??
            dt["FOperDescription"];
          const processNameDescription =
            dt.processNameDescription ?? dt.FNumber ?? dt["FNumber"];
          rows.push({
            ...base,
            processNo,
            workCenter,
            processName,
            activityUnit,
            processControl,
            activityDuration,
            processDescription,
            processNameDescription,
          });
        });
      });
      return rows;
    },
    // 工艺成功项（分页）
    successFlatPagedRows() {
      const rows = this.successFlatRows;
      const start =
        (this.processSuccessPage.pageNum - 1) *
        this.processSuccessPage.pageSize;
      const end = start + this.processSuccessPage.pageSize;
      return rows.slice(start, end);
    },
    // 工艺失败项（分页）
    processFailedPagedItems() {
      const items = Array.isArray(this.processUploadLog?.failedRoutes)
        ? this.processUploadLog.failedRoutes
        : [];
      const start =
        (this.processFailedPage.pageNum - 1) * this.processFailedPage.pageSize;
      const end = start + this.processFailedPage.pageSize;
      return items.slice(start, end);
    },
    // BOM上传日志统计
    bomLogCounts() {
      const items = Array.isArray(this.bomUploadLog?.items)
        ? this.bomUploadLog.items
        : [];
      const success = items.filter((item) => item.code === "200").length;
      const exists = items.filter((item) => item.code === "100").length;
      const failed = items.filter(
        (item) =>
          item.code === "300" || (item.code !== "200" && item.code !== "100")
      ).length;
      return {
        success,
        exists,
        failed,
        total: items.length,
      };
    },
    // BOM成功项
    bomSuccessItems() {
      const items = Array.isArray(this.bomUploadLog?.items)
        ? this.bomUploadLog.items
        : [];
      return items.filter((item) => item.code === "200");
    },
    // BOM已存在项
    bomExistsItems() {
      const items = Array.isArray(this.bomUploadLog?.items)
        ? this.bomUploadLog.items
        : [];
      return items.filter((item) => item.code === "100");
    },
    // BOM失败项
    bomFailedItems() {
      const items = Array.isArray(this.bomUploadLog?.items)
        ? this.bomUploadLog.items
        : [];
      return items.filter(
        (item) =>
          item.code === "300" || (item.code !== "200" && item.code !== "100")
      );
    },
    // BOM成功项（分页）
    bomSuccessPagedItems() {
      const items = this.bomSuccessItems;
      const start =
        (this.bomSuccessPage.pageNum - 1) * this.bomSuccessPage.pageSize;
      const end = start + this.bomSuccessPage.pageSize;
      return items.slice(start, end);
    },
    // BOM已存在项（分页）
    bomExistsPagedItems() {
      const items = this.bomExistsItems;
      const start =
        (this.bomExistsPage.pageNum - 1) * this.bomExistsPage.pageSize;
      const end = start + this.bomExistsPage.pageSize;
      return items.slice(start, end);
    },
    // BOM失败项（分页）
    bomFailedPagedItems() {
      const items = this.bomFailedItems;
      const start =
        (this.bomFailedPage.pageNum - 1) * this.bomFailedPage.pageSize;
      const end = start + this.bomFailedPage.pageSize;
      return items.slice(start, end);
    },
  },
  methods: {
    async fetchProcessLog() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.error("请先选择生产令号");
        return;
      }

      this.processLogLoading = true;
      try {
        const res = await getRouteLog(this.productionObj.id);
        if (res && (res.code === 200 || res.success)) {
          // 后端返回的数据在 msg 字段中（JSON 字符串），如果 msg 为空则尝试 data
          const jsonStr = res.msg || res.data || "";

          // 如果返回的字符串为空，提示无数据
          if (!jsonStr || !jsonStr.trim()) {
            this.$message.info("暂无工艺上传日志");
            this.clearProcessLog();
            return;
          }

          // 解析 JSON 字符串
          let parsed = null;
          try {
            parsed = JSON.parse(jsonStr);
          } catch (parseError) {
            console.error("JSON 解析失败", parseError);
            this.$message.error("日志数据格式错误，无法解析");
            this.clearProcessLog();
            return;
          }

          // 处理不同的数据格式
          let latest = null;

          // 如果是数组，选择时间最新的一条记录
          if (Array.isArray(parsed)) {
            if (parsed.length === 0) {
              this.$message.info("暂无工艺上传日志");
              this.clearProcessLog();
              return;
            }
            latest = parsed.reduce((acc, cur) => {
              const accTime =
                acc && acc.time ? new Date(acc.time).getTime() : 0;
              const curTime =
                cur && cur.time ? new Date(cur.time).getTime() : 0;
              return curTime >= accTime ? cur : acc;
            });
          }
          // 如果是单个对象，直接使用
          else if (parsed && typeof parsed === "object") {
            latest = parsed;
          } else {
            this.$message.error("日志数据格式不正确");
            this.clearProcessLog();
            return;
          }
          if (!latest) {
            this.$message.info("暂无工艺上传日志");
            this.clearProcessLog();
            return;
          }
          // 直接使用解析后的数据结构
          // 处理时间：dayjs 支持时间戳（毫秒）和日期字符串
          this.processUploadLog = {
            time: latest.time
              ? dayjs(latest.time).format("YYYY-MM-DD HH:mm:ss")
              : null,
            successfulRoutes: latest.successfulRoutes || [],
            duplicateRoutes: latest.duplicateRoutes || [],
            failedRoutes: latest.failedRoutes || [],
          };
        } else {
          this.$message.error(
            res && res.msg ? res.msg : "获取工艺上传日志失败"
          );
        }
      } catch (e) {
        this.$message.error("获取工艺上传日志失败");
      } finally {
        this.processLogLoading = false;
      }
    },
    clearProcessLog() {
      this.processUploadLog = {
        time: null,
        successfulRoutes: [],
        duplicateRoutes: [],
        failedRoutes: [],
      };
      // 重置分页
      this.processSuccessPage = { pageNum: 1, pageSize: 10 };
      this.processFailedPage = { pageNum: 1, pageSize: 10 };
    },
    // 获取BOM上传日志
    async fetchBomLog() {
      if (!this.productionObj || !this.productionObj.productionOrderNo) {
        this.$message.error("请先选择生产令号");
        return;
      }

      this.bomLogLoading = true;
      try {
        const res = await getBomUploadStatus(
          this.productionObj.productionOrderNo
        );
        if (res && (res.code === 200 || res.success)) {
          // 后端返回的数据在 data 字段中（JSON 字符串），如果 data 为空则尝试 msg
          const jsonStr = res.data || res.msg || "";

          // 如果返回的字符串为空，提示无数据
          if (!jsonStr || !jsonStr.trim()) {
            this.$message.info("暂无BOM上传日志");
            this.clearBomLog();
            return;
          }

          // 解析 JSON 字符串
          let parsed = null;
          try {
            parsed = JSON.parse(jsonStr);
          } catch (parseError) {
            console.error("JSON 解析失败", parseError);

            this.clearBomLog();
            return;
          }

          // 处理数据：如果是数组，直接使用；如果是对象且有 items 属性，使用 items
          let items = [];
          if (Array.isArray(parsed)) {
            items = parsed;
          } else if (
            parsed &&
            typeof parsed === "object" &&
            Array.isArray(parsed.items)
          ) {
            items = parsed.items;
          }

          this.bomUploadLog = {
            items: items,
          };
        } else {
          this.$message.error(res && res.msg ? res.msg : "获取BOM上传日志失败");
        }
      } catch (e) {
        this.$message.error("获取BOM上传日志失败");
      } finally {
        this.bomLogLoading = false;
      }
    },
    // 清空BOM日志
    clearBomLog() {
      this.bomUploadLog = {
        items: [],
      };
      // 重置分页
      this.bomSuccessPage = { pageNum: 1, pageSize: 10 };
      this.bomExistsPage = { pageNum: 1, pageSize: 10 };
      this.bomFailedPage = { pageNum: 1, pageSize: 10 };
    },
    // 格式化BOM错误信息
    formatBomErrorReason(reason) {
      if (!reason) return "";
      // 如果 reason 是 JSON 字符串，尝试解析并格式化
      try {
        const parsed = JSON.parse(reason);
        if (Array.isArray(parsed)) {
          return parsed
            .map((item) => {
              const field = item.FieldName || "";
              const message = item.Message || "";
              return field ? `${field}: ${message}` : message;
            })
            .join("; ");
        }
        return reason;
      } catch (e) {
        // 如果不是 JSON，直接返回原字符串
        return reason;
      }
    },
    formatFraction(value) {
      // 如果本身就是分数字符串，直接返回
      if (typeof value === "string" && value.includes("/")) {
        return value;
      }
      // 如果是数字或数字字符串，直接原样返回（或保留三位小数）
      if (!isNaN(value)) {
        return parseFloat(value).toFixed(3);
      }
      return value; // 兜底
    },

    // 处理分数值，用于数据处理阶段
    formatFractionValue(value) {
      // 如果本身就是分数字符串，直接返回
      if (typeof value === "string" && value.includes("/")) {
        return value;
      }
      // 如果是数字，转换为数字
      if (!isNaN(value) && value !== null && value !== undefined) {
        return Number(value);
      }
      return 0; // 兜底
    },
    handleCommand(command, row) {
      if ("生产计划" === command) {
        this.$router.push({
          path: "/system/operationPlanningMain",
          query: {
            hbytSclh: row && row.productionOrderNo ? row.productionOrderNo : undefined,
            batchNo: row && row.productionOrderNo ? row.productionOrderNo : undefined
          }
        });
        return;
      }
      if ("BOM" === command) {
        this.handleBOM(row.productionOrderNo);
      }
      if ("修改" === command) {
        this.handleUpdate(row);
      }
      if ("删除" === command) {
        this.handleDelete(row);
      }
      if ("清空项目" === command) {
        this.clearProject(row);
      }
    },
    handleClose(done) {
      done();
    },
    /** 获取图纸类型选项 */
    getDrawingTypeOptions() {
      getDrawingTypeDict()
        .then((response) => {
          this.drawingTypeOptions = response.rows.map((item) => ({
            fileType: item.fileType,
            fileUrl: item.fileUrl,
          }));
        })
        .catch(() => {
          this.drawingTypeOptions = [
            { fileType: "机械图纸", fileUrl: "mechanical" },
            { fileType: "电气图纸", fileUrl: "electrical" },
            { fileType: "工艺图纸", fileUrl: "process" },
            { fileType: "装配图纸", fileUrl: "assembly" },
          ];
        });
    },
    productionCodeHandle(row) {
      this.productionObj = row;
      this.drawer = true;
      // 打开抽屉后加载表格数据
      this.loadChainTableData();
      // 自动加载工艺上传日志
      if (row && row.id) {
        this.fetchProcessLog();
      }
      // 自动加载BOM上传日志
      if (row && row.productionOrderNo) {
        this.fetchBomLog();
      }
    },
    /** 获取图纸类型标签 */
    getDrawingTypeLabel(value) {
      const option = this.drawingTypeOptions.find(
        (item) => item.fileType === value
      );
      if (option) {
        return option.fileType;
      }
      // 如果没有找到匹配的选项，尝试从路径中提取文件名
      if (value && typeof value === "string") {
        const fileName = value.split("/").pop() || value.split("\\").pop();
        return fileName || value;
      }
      return value;
    },
    /** 项目类型缩写文本 */
    getProjectTypeLabel(code) {
      if (!code) return "-";
      const map = {
        CP: "产品",
        FB: "非标",
        BG: "变更",
        QB: "研发",
        EY: "研发",
        GZ: "固资",
        ZP: "展品",
      };
      return map[String(code)] || code;
    },
    /** 查询项目令号列表 */
    async getList() {
      this.loading = true;
      try {
        const base = process.env.VUE_APP_BASE_API || "";
        const url = `${base}/system/orderPro/refreshDelayStatus`;
        await axios.get(url, { headers: { ...(this.uploadHeaders || {}) } });
      } catch (e) {
        /* ignore refresh errors to not block list */
      }
      const params = { ...this.queryParams };
      if (Array.isArray(params.isEnterpriseStandardList) && params.isEnterpriseStandardList.length > 0) {
        params.isEnterpriseStandardList = params.isEnterpriseStandardList.join(",");
      }

      delete params.isEnterpriseStandard;
      try {
        const response = await listOrderPro(params);
        this.orderProList = response.rows;
        this.total = response.total;
      } finally {
        this.loading = false;
      }
    },
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },

    // 表单重置
    reset() {
      this.form = {
        id: undefined,
        productionOrderNo: undefined,
        productionName: undefined,
        drawingNo: undefined,
        drawingName: undefined,
        createTime: undefined,
        updateTime: undefined,
        createBy: undefined,
        updateBy: undefined,
        bomStatus: undefined,
        routeStatus: undefined,
        planEndTime: undefined,
        planStartTime: undefined,
        drawingType: undefined,
        projectEndTime: undefined,
        unit: undefined,
        quantity: 0,
        isEnterpriseStandard: 0, // 默认为否
        drawingPath: undefined,
        drawer: undefined,
        drawUptime: undefined,
        routeUptime: undefined,
        planUptime: undefined,
        bomUptime: undefined
      };
      this.resetForm("form");
    },
    handleDetail(productionOrderNo) {
      this.processParams = { productionOrderNo: productionOrderNo };
      this.processDrawer = true;
    },
    handleBOM(productionOrderNo) {
      this.bomParams = { productionOrderNo: productionOrderNo };
      this.bomDrawer = true;
    },
    /** MRP运算结果复查相关方法 */
    // 获取MRP运算结果
    async getMRPResults() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.error("请先选择生产令号");
        return;
      }

      // 只显示一次开始提示，不重复弹窗
      this.$message({
        message:
          "MRP运算已开始，由于数据量较大，可能需要较长时间，请耐心等待...",
        type: "info",
        duration: 6000, // 减少到6秒
        showClose: true,
      });

      this.loadingMRP = true;
      this.mrpOperationStatus = "MRP运算已开始，正在处理数据...";

      // 只更新状态，不弹窗提醒
      const progressTimer = setInterval(() => {
        this.mrpOperationStatus = "MRP运算仍在进行中，请继续等待...";
        // 移除弹窗提示，只更新状态显示
      }, 30000);

      try {
        const response = await getMRPResults(this.productionObj.id);

        // 清除定时器
        clearInterval(progressTimer);

        if (response && response.code === 200) {
          this.$message.success("MRP运算完成！");
          // 运算完成后刷新MRP数据
          await this.getMrpData();
        } else {
          this.$message.error(response?.msg || "MRP运算失败");
        }
      } catch (error) {
        // 清除定时器
        clearInterval(progressTimer);

        // 简化错误提示，避免重复弹窗
        if (
          error.code === "ECONNABORTED" ||
          error.message.includes("timeout")
        ) {
          this.$message.error("MRP运算超时，请稍后重试");
        } else {
          this.$message.error("MRP运算失败，请重试");
        }
      } finally {
        this.loadingMRP = false;
        this.mrpOperationStatus = ""; // 清空状态
      }
    },

    // 获取MRP数据
    async getMrpData() {
      this.mrpLoading = true;
      try {
        this.mrpQueryParams.orderProId = this.productionObj.id;
        const response = await listMrp(this.mrpQueryParams);

        if (response && response.rows) {
          // 统一转Number，避免 toFixed 报错
          const processedRows = response.rows.map((item) => ({
            ...item,
            availableStock: Number(item.availableStock) || 0,
            realTimeStock: Number(item.realTimeStock) || 0,
            prodNotInStock: Number(item.prodNotInStock) || 0,
            purchaseRequestQty: Number(item.purchaseRequestQty) || 0,
            purchaseNotInStock: this.formatFractionValue(
              item.purchaseNotInStock
            ),
            childNotPickedQty: Number(item.childNotPickedQty) || 0,
            createTime: item.createTime
              ? dayjs(item.createTime).format("YYYY-MM-DD HH:mm:ss")
              : "",
            updateTime: item.updateTime
              ? dayjs(item.updateTime).format("YYYY-MM-DD HH:mm:ss")
              : "",
          }));
          this.mrpResultData = processedRows;
          this.mrpTotal = response.total || processedRows.length;
          // 统计计算也用处理过的数字数据
          this.calculateMrpStats(processedRows);
        }
      } catch (error) {
        console.error("获取MRP数据失败:", error);
        this.$message.error("获取MRP数据失败");
      } finally {
        this.mrpLoading = false;
      }
    },

    // MRP分页大小改变
    handleMrpSizeChange(size) {
      this.mrpQueryParams.pageSize = size;
      this.mrpQueryParams.pageNum = 1;
      this.getMrpData();
    },

    // MRP当前页改变
    handleMrpCurrentChange(page) {
      this.mrpQueryParams.pageNum = page;
      this.getMrpData();
    },

    // 格式化日期时间
    formatDateTime(date) {
      if (!date) return "-";
      if (typeof date === "string") {
        date = new Date(date);
      }
      if (isNaN(date.getTime())) return "-";

      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, "0");
      const day = String(date.getDate()).padStart(2, "0");
      const hours = String(date.getHours()).padStart(2, "0");
      const minutes = String(date.getMinutes()).padStart(2, "0");

      return `${year}-${month}-${day} ${hours}:${minutes}`;
    },

    normalizeIncomingDate(value) {
      if (!value) return "";
      const s = typeof value === "string" ? value.trim() : value;
      if (
        typeof s === "string" &&
        (s === "0000-00-00 00:00:00" || s.startsWith("1970-01-01"))
      ) {
        return "";
      }
      const d = dayjs(s);
      if (!d.isValid()) return "";
      return d.format("YYYY-MM-DD HH:mm:ss");
    },

    normalizeDateTime(value) {
      if (!value) return null;
      if (typeof value === "string") {
        const v = value.trim();
        if (v === "" || v === "0000-00-00 00:00:00") return null;
        if (/^\d{4}-\d{2}-\d{2}$/.test(v)) {
          const now = dayjs();
          return `${v} ${now.format("HH:mm:ss")}`;
        }
        const d = dayjs(v);
        if (!d.isValid()) return null;
        return d.format("YYYY-MM-DD HH:mm:ss");
      }
      const d = dayjs(value);
      if (!d.isValid()) return null;
      return d.format("YYYY-MM-DD HH:mm:ss");
    },

    normalizeDateTimeNonZero(value) {
      const s = this.normalizeDateTime(value);
      if (!s) return s;
      if (/^\d{4}-\d{2}-\d{2} 00:00:00$/.test(s)) {
        const now = dayjs();
        return `${s.slice(0, 10)} ${now.format("HH:mm:ss")}`;
      }
      return s;
    },

    // MRP搜索处理
    handleMrpSearch() {
      this.mrpQueryParams.pageNum = 1;
      this.mrpQueryParams.materialCode = this.mrpSearchKeyword;
      console.log("MRP搜索参数:", this.mrpQueryParams);
      this.getMrpData();
    },

    // 清空MRP搜索
    clearMrpSearch() {
      this.mrpSearchKeyword = "";
      this.mrpQueryParams.materialCode = "";
      this.mrpQueryParams.pageNum = 1;
      this.getMrpData();
    },

    // 过滤MRP数据（前端过滤，作为备用方案）
    filterMrpData(data) {
      if (!this.mrpSearchKeyword) {
        return data;
      }

      const keyword = this.mrpSearchKeyword.toLowerCase();
      return data.filter(
        (item) =>
          item.materialName.toLowerCase().includes(keyword) ||
          item.materialCode.toLowerCase().includes(keyword)
      );
    },

    // 计算MRP统计信息
    calculateMrpStats(data) {
      const total = data.length;
      const positive = data.filter((item) => item.availableStock > 0).length;
      const negative = data.filter((item) => item.availableStock < 0).length;
      const zero = data.filter((item) => item.availableStock === 0).length;

      this.mrpStats = {
        totalMaterials: total,
        positiveStock: positive,
        negativeStock: negative,
        zeroStock: zero,
      };
    },

    // 获取抽屉标题
    getDrawerTitle() {
      if (!this.productionObj) {
        return "项目详情";
      }

      const orderNo = this.productionObj.productionOrderNo || "";
      const projectName = this.productionObj.productionName || "";

      if (orderNo && projectName) {
        return `项目详情 - ${orderNo} | ${projectName}`;
      } else if (orderNo) {
        return `项目详情 - ${orderNo}`;
      } else if (projectName) {
        return `项目详情 - ${projectName}`;
      } else {
        return "项目详情";
      }
    },

    // 刷新MRP数据
    handleRefreshMrpData() {
      this.mrpQueryParams.pageNum = 1;
      this.getMrpData();
    },

    // 导出MRP数据
    async handleExportMrp() {
      // 增强参数验证
      if (!this.productionObj) {
        this.$message.error("请先选择生产令号");
        return;
      }

      if (!this.productionObj.id) {
        this.$message.error("生产令号ID无效，请重新选择");
        return;
      }

      if (!this.productionObj.productionOrderNo) {
        this.$message.error("生产令号信息不完整，请重新选择");
        return;
      }

      // 调试信息
      console.log("productionObj:", this.productionObj);
      console.log("productionObj.id:", this.productionObj.id);
      console.log("mrpResultData.length:", this.mrpResultData?.length || 0);

      // 显示导出确认信息
      const exportInfo = `即将导出生产令号"${this.productionObj.productionOrderNo}"的MRP数据`;
      const searchInfo = this.mrpSearchKeyword
        ? `，包含搜索关键词"${this.mrpSearchKeyword}"`
        : "";
      const dataInfo =
        this.mrpResultData && this.mrpResultData.length > 0
          ? `，当前显示${this.mrpResultData.length}条记录`
          : "（后台将查询所有相关数据）";

      try {
        await this.$confirm(
          `${exportInfo}${searchInfo}${dataInfo}。\n\n确定要导出吗？`,
          "确认导出",
          {
            confirmButtonText: "确定导出",
            cancelButtonText: "取消",
            type: "info",
          }
        );
      } catch (error) {
        // 用户取消导出
        return;
      }

      this.exportLoading = true;

      try {
        // 准备导出参数，主要传递orderProId给后台
        const exportParams = {
          orderProId: this.productionObj.id, // 核心参数：生产令号ID
          materialCode: this.mrpSearchKeyword || "", // 可选的物料编码搜索
          materialName: "", // 清空名称搜索，避免冲突
          pageSize: 9999, // 后台查询时获取所有数据
          pageNum: 1,
        };

        // 详细调试信息
        console.log("导出参数:", exportParams);
        console.log("orderProId类型:", typeof exportParams.orderProId);
        console.log("orderProId值:", exportParams.orderProId);
        console.log("materialCode:", exportParams.materialCode);

        const response = await exportMrp(exportParams);

        // 创建下载链接
        const blob = new Blob([response], {
          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        });
        const url = window.URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = url;
        link.download = `MRP运算结果_${this.productionObj.productionOrderNo
          }_${new Date().getTime()}.xlsx`;
        link.click();
        window.URL.revokeObjectURL(url);

        this.$message.success(
          `导出成功！文件已下载：MRP运算结果_${this.productionObj.productionOrderNo
          }_${new Date().getTime()}.xlsx`
        );
      } catch (error) {
        console.error("导出失败:", error);
        this.$message.error("导出失败: " + (error.message || error));
      } finally {
        this.exportLoading = false;
      }
    },

    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map((item) => item.id);
      this.single = selection.length !== 1;
      this.multiple = !selection.length;
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.currentPid = null; // 新增时，不依赖当前行
      this.chainForm.productionCode = "";
      this.reset();
      this.open = true;
      this.title = "添加项目令号";
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.loading = true;
      this.reset();
      const id = row.id || this.ids;
      getOrderPro(id).then((response) => {
        this.loading = false;
        this.form = response.data || {};
        this.form.isEnterpriseStandard =
          this.form.isEnterpriseStandard != null
            ? Number(this.form.isEnterpriseStandard)
            : 0;
        this.form.drawer = this.parseDesignerArray(this.form && this.form.drawingBy);
        this.form.drawUptime = this.normalizeIncomingDate(
          this.form && this.form.drawingTime
        );
        this.form.projectEndTime = this.normalizeIncomingDate(
          this.form && this.form.projectEndTime
        );
        this.form.routeUptime = this.normalizeIncomingDate(
          this.form && this.form.routeUptime
        );
        this.form.planUptime = this.normalizeIncomingDate(
          this.form && this.form.planUptime
        );
        this.form.bomUptime = this.normalizeIncomingDate(
          this.form && this.form.bomUptime
        );
        // 保持后端返回的 drawingPath（图片ID串）原样，避免将 URL 误传给 ImageUpload
        this.open = true;
        this.title = "修改项目令号";
      });
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          this.buttonLoading = true;
          const payload = {
            ...this.form,
            isEnterpriseStandard:
              this.form.isEnterpriseStandard != null
                ? Number(this.form.isEnterpriseStandard)
                : 0,
            drawingBy: Array.isArray(this.form.drawer)
              ? this.form.drawer.join(",")
              : this.form.drawer,
            drawingTime: this.normalizeDateTime(this.form.drawUptime),
            projectEndTime: this.normalizeDateTime(this.form.projectEndTime),
            routeUptime: this.normalizeDateTime(this.form.routeUptime),
            planUptime: this.normalizeDateTime(this.form.planUptime),
            bomUptime: this.normalizeDateTime(this.form.bomUptime),
            drawingPath: this.form.drawingPath || "",
          };
          if (this.form.id != null) {
            updateOrderPro(payload)
              .then((response) => {
                this.$modal.msgSuccess("修改成功");
                this.open = false;
                this.getList();
              })
              .finally(() => {
                this.buttonLoading = false;
              });
          } else {
            addOrderPro(payload)
              .then((response) => {
                this.$modal.msgSuccess("新增成功");
                this.open = false;
                this.getList();
              })
              .finally(() => {
                this.buttonLoading = false;
              });
          }
        }
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const ids = row.id || this.ids;
      this.$modal
        .confirm('是否确认删除项目令号编号为"' + ids + '"的数据项？')
        .then(() => {
          this.loading = true;
          return delOrderPro(ids);
        })
        .then(() => {
          this.loading = false;
          this.getList();
          this.$modal.msgSuccess("删除成功");
        })
        .catch(() => { })
        .finally(() => {
          this.loading = false;
        });
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download(
        "system/orderPro/export",
        {
          ...this.queryParams,
        },
        `orderPro_${new Date().getTime()}.xlsx`
      );
    },
    openAddChainDialog() {
      this.isEditChain = false;
      this.chainForm = {
        pid: this.id,
        productionCode: this.productionObj.productionOrderNo,
        productType: "",
        figureNumber: "",
        figureName: "",
        figureNum: "",
        jdInventory: "",
        axialType: "",
        boxType: "",
        selectedFileType: "",
        drawPath: "",
        fileType: "",
        drawBy: "",
        drawTime: "",
        id: undefined,
      };
      this.chainDialogVisible = true;
    },
    editChain(index, row) {
      this.isEditChain = true;
      this.editChainIndex = index;
      this.chainForm = {
        pid: this.productionObj && this.productionObj.id,
        productionCode:
          this.productionObj && this.productionObj.productionOrderNo,
        productType: (row && (row.productType || row.type)) || "",
        figureNumber: (row && row.figureNumber) || "",
        figureName: (row && (row.figureName || row.productName)) || "",
        figureNum: (row && row.figureNum) || "",
        jdInventory: (row && row.jdInventory) || "",
        axialType: (row && row.axialType) || "",
        boxType: (row && row.boxType) || "",
        selectedFileType: (row && (row.fileType || row.selectedFileType)) || "",
        drawPath: (row && row.drawPath) || "",
        fileType: (row && row.fileType) || "",
        drawBy: (row && row.drawBy) || "",
        drawTime: (row && row.drawTime) || "",
        id: row && row.id,
      };
      // 同步文件类型选择：优先使用后端返回的 fileType；若无，则根据当前路径匹配配置项
      if (!this.chainForm.selectedFileType) {
        this.setSelectedFileTypeFromPath(this.chainForm.drawPath);
      }
      this.chainDialogVisible = true;
    },
    removeChain(index) {
      const row = this.chainTable[index];
      this.$modal
        .confirm("是否确认删除该销齿链型号？")
        .then(() => {
          return delSave(row.id);
        })
        .then(() => {
          this.$modal.msgSuccess("删除成功");
          // 删除成功后刷新表格数据
          this.loadChainTableData();
        })
        .catch(() => {
          // 删除失败时不显示错误信息，因为用户可能取消了操作
        });
    },
    handleChainSelectionChange(selection) {
      this.selectedChainIds = selection.map((item) => item.id).filter(Boolean);
    },
    beforeBatchDwgUpload(file) {
      const name = file.name || "";
      const suffix = name.substring(name.lastIndexOf(".") + 1).toLowerCase();
      if (!this.selectedChainIds.length) {
        this.$message.warning("请先勾选需要上传的行");
        return false;
      }
      if (suffix !== "dwg") {
        this.$message.error("仅允许上传 DWG 文件");
        return false;
      }
      return true;
    },
    async batchUploadDwg({ file }) {
      try {
        const formData = new FormData();
        // Spring 接收 List<Long> ids，使用重复键形式提交
        this.selectedChainIds.forEach((id) => formData.append("ids", id));
        formData.append("file", file);

        const res = await axios.post(this.dwgUploadUrl, formData, {
          headers: {
            ...this.uploadHeaders,
          },
        });

        const payload = res && res.data ? res.data : {};
        const ok = payload.code === 200 || payload.success === true;
        const msg = payload.msg || "上传完成";
        if (ok) {
          this.$message.success(msg);
          this.loadChainTableData();
        } else {
          this.$message.error(msg || "上传失败");
        }
      } catch (e) {
        const status = e && e.response && e.response.status;
        const serverMsg =
          (e &&
            e.response &&
            e.response.data &&
            (e.response.data.msg ||
              e.response.data.message ||
              e.response.data)) ||
          "";
        if (status === 413) {
          this.$message.error("文件过大，已超过服务器限制");
        } else if (status === 401) {
          this.$message.error("未认证或登录已过期");
        } else if (status === 403) {
          this.$message.error("权限不足，无法上传");
        } else if (status === 404) {
          this.$message.error("上传接口不存在或路径错误");
        } else if (status === 415) {
          this.$message.error("服务器不支持当前文件类型");
        } else if (status >= 500) {
          this.$message.error(
            "服务器内部错误：" + (serverMsg || "请联系管理员")
          );
        } else {
          this.$message.error(
            "上传失败：" + (serverMsg || e.message || "请重试")
          );
        }
      }
    },
    beforeElectricalBomUpload(file) {
      const name = file.name || "";
      const suffix = name.substring(name.lastIndexOf(".") + 1).toLowerCase();
      if (!(suffix === "xls" || suffix === "xlsx")) {
        this.$message.error("仅支持Excel文件(xls/xlsx)");
        return false;
      }
      return true;
    },
    async uploadElectricalBom({ file }) {
      try {
        const formData = new FormData();
        formData.append("file", file);
        const res = await importElectricalBom(formData);
        if (res && (res.code === 200 || res.success)) {
          let payload = res.data || res.msg || {};
          if (typeof payload === "string") {
            try {
              payload = JSON.parse(payload);
            } catch (_) {
              payload = {};
            }
          }
          const total = payload.totalRows ?? payload.total ?? 0;
          const parsed = payload.parsedRows ?? 0;
          const saved = payload.savedRows ?? payload.saved ?? 0;
          const message = payload.message || "导入完成";
          this.$message.success(
            `${message}（总:${total} 解析:${parsed} 保存:${saved}）`
          );
          this.fetchBomLog();
        } else {
          this.$message.error(res?.msg || "导入失败");
        }
      } catch (e) {
        this.$message.error("导入失败: " + (e.message || e));
      }
    },
    async importProducts({ file }) {
      try {
        const formData = new FormData();
        formData.append("file", file);
        const base = process.env.VUE_APP_BASE_API || "";
        const url = `${base}/system/save/importData`;
        const res = await axios.post(`${url}?updateSupport=false`, formData, {
          headers: {
            "Content-Type": "multipart/form-data",
            ...(this.uploadHeaders || {}),
          },
        });
        const data = res && res.data ? res.data : {};
        if (data && (data.code === 200 || data.success)) {
          const msg = data.msg || data.message || data.data || "导入成功";
          this.$message.success(msg);
          this.loadChainTableData();
        } else {
          this.$message.error(data.msg || data.message || "导入失败");
        }
      } catch (e) {
        this.$message.error("导入失败: " + (e.message || e));
      }
    },
    async downloadSelectedDwgZip() {
      if (!Array.isArray(this.selectedChainIds) || this.selectedChainIds.length === 0) {
        this.$message.warning("请先勾选需要下载的行");
        return;
      }
      try {
        const base = process.env.VUE_APP_BASE_API || "";
        const firstId = this.selectedChainIds[0];
        const url = `${base}/system/orderPro/downloadDwg/${firstId}`;
        const res = await axios.get(url, {
          params: { ids: this.selectedChainIds },
          responseType: "blob",
          headers: { ...(this.uploadHeaders || {}) },
        });
        const contentType = res && res.headers && res.headers["content-type"];
        if (contentType && contentType.includes("application/json")) {
          // 错误信息
          const text = await new Response(res.data).text();
          try {
            const json = JSON.parse(text);
            this.$message.error(json.msg || json.message || "下载失败");
          } catch (_) {
            this.$message.error(text || "下载失败");
          }
          return;
        }
        // 提取文件名
        let filename = `${(this.productionObj && this.productionObj.productionOrderNo) || "DWG"}_ZIP.zip`;
        const cd = res && res.headers && res.headers["content-disposition"];
        if (cd) {
          const m = cd.match(/filename\*?=UTF-8''([^;]+)|filename="?([^";]+)"?/i);
          const raw = (m && (m[1] || m[2])) || "";
          if (raw) {
            try {
              filename = decodeURIComponent(raw.replace(/\+/g, "%20"));
            } catch (_) {
              filename = raw;
            }
          }
        }
        const blob = new Blob([res.data], { type: "application/zip" });
        const link = document.createElement("a");
        const urlObj = window.URL.createObjectURL(blob);
        link.href = urlObj;
        link.download = filename;
        link.click();
        window.URL.revokeObjectURL(urlObj);
        this.$message.success("图纸ZIP下载成功");
      } catch (e) {
        this.$message.error("下载失败：" + (e.message || e));
      }
    },
    async pushDingTalkCard() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.warning("请先选择生产令号");
        return;
      }
      try {
        const base = process.env.VUE_APP_BASE_API || "";
        const url = `${base}/system/orderPro/pushDingTalkCard/${this.productionObj.id}`;
        const res = await axios.post(url, null, { headers: { ...(this.uploadHeaders || {}) } });
        const ct = res && res.headers && res.headers["content-type"];
        if (ct && ct.includes("application/json")) {
          const data = res.data;
          if (data && (data.code === 200 || data.success)) {
            this.$message.success("已推送钉钉卡片");
          } else {
            this.$message.error((data && (data.msg || data.message)) || "推送失败");
          }
        } else {
          this.$message.success("已推送钉钉卡片");
        }
      } catch (e) {
        this.$message.error("推送失败：" + (e.message || e));
      }
    },
    async clearProject(row) {
      if (!row || !row.id) {
        this.$message.warning("未获取到项目ID");
        return;
      }
      try {
        await this.$confirm(
          `确认清空项目【${row.productionOrderNo || row.id}】的关联数据？（保留项目本身）`,
          "提示",
          { type: "warning" }
        );
      } catch (e) {
        return;
      }
      try {
        const base = process.env.VUE_APP_BASE_API || "";
        const url = `${base}/system/orderPro/clear/${row.id}`;
        await axios.delete(url, { headers: { ...(this.uploadHeaders || {}) } });
        this.$message.success("清空项目数据成功");
        this.getList();
      } catch (e) {
        this.$message.error("清空失败：" + (e.message || e));
      }
    },
    async saveChainForm() {
      if (!this.chainForm.productionCode) {
        this.$message.warning("生产令号不能为空");
        return;
      }
      if (!this.chainForm.figureNumber) {
        this.$message.warning("图号不能为空");
        return;
      }
      if (!this.chainForm.figureName) {
        this.$message.warning("名称不能为空");
        return;
      }
      if (!this.chainForm.figureNum || Number(this.chainForm.figureNum) < 1) {
        this.$message.warning("数量不能为空且必须大于0");
        return;
      }
      const payload = {
        id: this.chainForm.id,
        pid: this.productionObj.id,
        productionCode: this.chainForm.productionCode,
        productType: this.chainForm.productType,
        figureNumber: this.chainForm.figureNumber,
        figureName: this.chainForm.figureName,
        figureNum: this.chainForm.figureNum,
        jdInventory: this.chainForm.jdInventory || undefined,
        axialType: this.chainForm.axialType || undefined,
        boxType: this.chainForm.boxType || undefined,
        selectedFileType: this.chainForm.selectedFileType || "",
        drawPath: this.chainForm.drawPath || "",
        fileType: this.chainForm.fileType || "",
        drawBy: this.chainForm.drawBy || "",
        drawTime: this.normalizeDateTimeNonZero(this.chainForm.drawTime) || null,
      };

      if (this.isEditChain && this.chainForm.id) {
        payload.id = this.chainForm.id;
      }
      console.log("saveChainForm........", payload);
      try {
        if (this.isEditChain) {
          await this.$api.updateProduct(payload);
          this.$message.success("修改成功");
        } else {
          await this.$api.addProduct(payload);
          this.$message.success("添加成功");
        }
        this.chainDialogVisible = false;
        this.loadChainTableData();
      } catch (e) {
        const msg = this.isEditChain ? "修改失败" : "添加失败";
        this.$message.error(msg);
      }
    },
    searchRigidChain(query) {
      if (!query) {
        this.rigidChainOptions = [];
        this.typeOptions = [];
        this.journeyOptions = [];
        return;
      }
      this.chainLoading = true;
      listRigidChain({ typeName: query })
        .then((res) => {
          this.rigidChainOptions = (res.rows || []).slice(0, 10);
          const types = new Set();
          const journeys = new Set();
          this.rigidChainOptions.forEach((item) => {
            if (item.type) types.add(item.type);
            if (item.typeName) types.add(item.typeName);
            if (item.journey) journeys.add(item.journey);
          });
          this.typeOptions = Array.from(types);
          this.journeyOptions = Array.from(journeys);
          this.chainLoading = false;
        })
        .catch(() => {
          this.chainLoading = false;
        });
    },
    onRigidChainSelect(id) {
      if (!id) {
        this.chainForm.productType = "";
        this.chainForm.figureNumber = "";
        this.chainForm.figureName = "";
        this.chainForm.drawPath = "";
        return;
      }

      const selected = this.rigidChainOptions.find((item) => item.id === id);

      if (selected) {
        this.chainForm.productType = selected.type || "";
        this.chainForm.figureNumber =
          selected.typeName || selected.figureNumber || "";
        this.chainForm.figureName = selected.productName || "";
        this.chainForm.drawPath = selected.drawPath || "";
        if (!this.chainForm.figureNum) {
          this.chainForm.figureNum = 1;
        }
        if (!this.isEditChain) {
          this.chainForm.id = undefined;
        }
      }
    },
    async executeDrawCommand() {
        this.drawLoading = true;
        this.drawLog = "";

        try {
          const res = await executDrawing(this.productionObj);

          if (res.code === 200) {
            this.time = setInterval(async () => {
              const data = await getstate();

              this.drawLog = data.msg;

              // 进行中
              if (data.processstate === 3) {
                const msg = data.msg.split(",");

                if (msg.length > 1) {
                  const progress = msg[1]
                    .replace(".", "")
                    .replace(/\s/g, "")
                    .split("/");

                  const current = Number(progress[0]);
                  const total = Number(progress[1]);

                  if (total > 0) {
                    this.percentage = Number(
                      ((current / total) * 100).toFixed(2)
                    );
                  }
                }
              }

              // 完成
              if (data.processstate === 4) {
                this.percentage = 100;

                clearInterval(this.time);
                this.time = null;

                this.drawLoading = false;
              }
            }, 1000);
          }
        } catch (e) {
          this.drawLog = "出图失败：" + (e.msg || e.message || e);
          this.drawLoading = false;
        }
      },
    // 加载销齿链型号表格数据
    async loadChainTableData() {
      if (!this.productionObj || !this.productionObj.id) {
        this.chainTable = [];
        return;
      }
      try {
        this.getMrpData(this.productionObj.id); // 获取MRP数据
        const response = await listSave({ pid: this.productionObj.id });
        this.chainTable = response.rows || [];
      } catch (error) {
        console.error("加载表格数据失败:", error);
        this.chainTable = [];
      }
    },
    getConfigIniOptions() {
      listConfigIni().then((res) => {
        this.configIniOptions = (res.rows || []).map((item) => ({
          fileType: item.fileType,
          fileUrl: item.fileUrl,
        }));
      });
    },
    onConfigIniSelect(fileType) {
      const selected = this.configIniOptions.find(
        (item) => item.fileType === fileType
      );
      if (selected) {
        this.chainForm.drawPath = selected.fileUrl;
      }
    },
    setSelectedFileTypeFromPath(path) {
      if (!path) {
        this.chainForm.selectedFileType = "";
        return;
      }
      const match =
        this.configIniOptions &&
        this.configIniOptions.find((opt) => opt.fileUrl === path);
      if (match) {
        this.chainForm.selectedFileType = match.fileType;
      } else if (!this.configIniOptions || this.configIniOptions.length === 0) {
        // 配置尚未加载，拉取后再匹配
        this.getConfigIniOptions();
        this.$nextTick(() => {
          const m = this.configIniOptions.find((opt) => opt.fileUrl === path);
          if (m) this.chainForm.selectedFileType = m.fileType;
        });
      }
    },
    getFullImageUrl(row) {
      if (!row) return "";
      let url = row.drawingPathUrl || row.drawingPath || "";
      if (!url) return "";
      if (typeof url === "string") {
        url = url.replace(/[`'"]/g, "").trim();
      }
      return url;
    },
    getImageList(row) {
      if (!row) return [];
      const raw = row.drawingPathUrl || row.drawingPath || "";
      if (!raw) return [];
      if (Array.isArray(raw)) return raw.filter(Boolean).map((s) => String(s).trim());
      if (typeof raw === "string") {
        return raw
          .split(",")
          .map((s) => s.trim())
          .filter((s) => s.length > 0);
      }
      return [];
    },
    beforeDwgUpload(file, row) {
      // 验证文件格式
      const ext = file.name.split(".").pop().toLowerCase();
      if (ext !== "dwg") {
        this.$message.error("仅支持dwg格式文件上传");
        return false;
      }

      // 验证行数据
      if (!row || !row.id) {
        this.$message.error("行数据无效，无法上传");
        return false;
      }

      // 验证文件大小（根据前端阈值，默认 100MB），提示包含所选文件大小
      const limitMB = Number(this.dwgMaxSizeMB) || 100;
      const maxSize = limitMB * 1024 * 1024;
      const sizeMB = (file.size / (1024 * 1024)).toFixed(2);
      if (file.size > maxSize) {
        this.$message.error(
          `文件过大（所选 ${sizeMB}MB，限制 ${limitMB}MB）。请压缩后重试或联系管理员调整服务器限制`
        );
        return false;
      }

      return true;
    },
    handleDwgUploadSuccess(response, file, row) {
      if (response && response.code === 200) {
        // 刷新列表
        this.loadChainTableData();
        this.$message.success(`图纸上传成功：${file.name}`);

        // 可以在这里更新行的drawPath字段
        if (response.data && response.data.filePath) {
          row.drawPath = response.data.filePath;
        }
      } else {
        this.$message.error((response && response.msg) || "上传失败");
      }
    },
    handleDwgUploadError(error, file, row) {
      console.error("图纸上传失败:", error);
      const status =
        (error &&
          (error.status || (error.response && error.response.status))) ||
        0;
      if (status === 413) {
        this.$message.error(
          "文件过大（超出服务器限制）。请联系管理员提高上传大小限制或压缩文件后重试"
        );
        return;
      }
      this.$message.error(`图纸上传失败：${file.name}`);
    },
    downloadPDF() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.error("请先选择生产令号");
        return;
      }

      this.$message.success("开始下载PDF文件");
      console.log("开始下载，生产令号ID:", this.productionObj.id);

      // 调用API获取文件流
      uploadPDF(this.productionObj.id)
        .then((response) => {
          console.log("API响应:", response);
          console.log("响应类型:", typeof response);
          console.log("是否为Blob:", response instanceof Blob);

          // 检查响应是否为blob类型
          if (!(response instanceof Blob)) {
            console.error("响应不是文件流:", response);
            this.$message.error("服务器返回的不是文件流");
            return;
          }

          // 检查文件大小
          console.log("文件大小:", response.size);
          if (response.size === 0) {
            this.$message.error("文件为空或下载失败");
            return;
          }

          // 创建blob对象并下载
          let blob = new Blob([response], { type: "application/zip" });
          let url = window.URL.createObjectURL(blob);
          const link = document.createElement("a");
          link.href = url;
          link.download = `${this.productionObj.productionOrderNo}_PDF.zip`;
          link.click();
          window.URL.revokeObjectURL(url);

          this.$message.success("PDF文件下载成功");
        })
        .catch((error) => {
          this.$message.error("PDF文件下载失败: " + (error.message || error));
        });
    },
    downloadRoute() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.error("请先选择生产令号");
        return;
      }

      this.$message.success("开始下载生产工艺计划表");
      console.log("开始下载，生产令号ID:", this.productionObj.id);

      // 调用API获取文件流
      exportRoute(this.productionObj.id)
        .then((response) => {
          // 检查文件大小
          if (response.size === 0) {
            this.$message.error("文件为空或下载失败");
            return;
          }

          // 创建blob对象并下载
          let blob = new Blob([response], {
            type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          });
          let url = window.URL.createObjectURL(blob);
          const link = document.createElement("a");
          link.href = url;
          link.download = `${this.productionObj.productionOrderNo}_生产工艺计划表.xlsx`;
          link.click();
          window.URL.revokeObjectURL(url);

          this.$message.success("生产工艺计划表下载成功");
        })
        .catch((error) => {
          console.error("下载失败:", error);
          // 后端异常会直接抛出，显示错误信息
          this.$message.error("下载失败: " + (error.message || "项目未出图"));
        });
    },
    downloadRoute3() {
      this.$message.success("开始导出工艺计划表");
      console.log("开始下载，生产令号ID:", this.productionObj.id);
      exportRoute3(this.productionObj.id)
        .then((response) => {
          if (response.size === 0) {
            this.$message.error("文件为空或下载失败");
            return;
          }
          const blob = new Blob([response], {
            type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          });
          const url = window.URL.createObjectURL(blob);
          const link = document.createElement("a");
          link.href = url;
          link.download = `${this.productionObj.productionOrderNo}_工艺计划表.xlsx`;
          link.click();
          window.URL.revokeObjectURL(url);
          this.$message.success("工艺计划表下载成功");
        })
        .catch((error) => {
          console.error("下载失败:", error);
          this.$message.error("下载失败: " + (error.message || "项目未出图"));
        });
    },
    async pushProcessInbound() {
      if (!this.productionObj || !this.productionObj.productionOrderNo) {
        this.$message.error("请先选择生产令号");
        return;
      }
      try {
        const { value } = await this.$prompt(
          "请输入入库编码（仓库编号）",
          "推送工序计划单入库",
          {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            inputPlaceholder: "例如：CK001",
            inputValidator: (val) => !!(val && String(val).trim()),
            inputErrorMessage: "入库编码不能为空",
          }
        );
        const code = String(value).trim();
        const res = await updateProductionOrders(
          this.productionObj.productionOrderNo,
          code
        );
        if (res && (res.code === 200 || res.success)) {
          const list = res.data || [];
          const msg = Array.isArray(list)
            ? `成功：${list.length} 项`
            : "推送成功";
          this.$message.success(msg);
        } else {
          this.$message.error(res?.msg || "推送失败");
        }
      } catch (e) {
        // 用户取消或异常
        if (e && e === "cancel") return;
        this.$message.error("推送失败：" + (e.message || e));
      }
    },
    async pushPurchaseInbound() {
      if (!this.productionObj || !this.productionObj.productionOrderNo) {
        this.$message.error("请先选择生产令号");
        return;
      }
      try {
        const { value } = await this.$prompt(
          "请输入入库编码（仓库编号）",
          "推送入库采购申请",
          {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            inputPlaceholder: "例如：CK001",
            inputValidator: (val) => !!(val && String(val).trim()),
            inputErrorMessage: "入库编码不能为空",
          }
        );
        const code = String(value).trim();
        const res = await updateCgOrders(
          this.productionObj.productionOrderNo,
          code
        );
        if (res && (res.code === 200 || res.success)) {
          const list = res.data || [];
          const msg = Array.isArray(list)
            ? `成功：${list.length} 项`
            : "推送成功";
          this.$message.success(msg);
        } else {
          this.$message.error(res?.msg || "推送失败");
        }
      } catch (e) {
        if (e && e === "cancel") return;
        this.$message.error("推送失败：" + (e.message || e));
      }
    },
    formatStepTime(row, field, fmt) {
      const val = row && row[field];
      if (!this.isValidTime(val)) return "—";
      const format = fmt || "{y}-{m}-{d} {h}:{i}:{s}";
      return this.parseTime(val, format);
    }, drawingTime(row, field, fmt) {
      const val = row && row[field];
      if (!this.isValidTime(val)) return "—";
      const format = fmt || "{y}-{m}-{d} {h}:{i}:{s}";
      return this.parseTime(val, format);
    },
    isValidTime(val) {
      if (!val) return false;
      const d = new Date(val);
      if (isNaN(d.getTime())) return false;
      const year = d.getFullYear();
      return year > 1971;
    },
    parseDesignerArray(v) {
      if (!v) return [];
      if (Array.isArray(v)) return v.filter(Boolean).map((x) => String(x));
      if (typeof v === "string") {
        return v
          .split(",")
          .map((s) => s.trim())
          .filter((s) => s.length > 0);
      }
      return [];
    },
    getDesignerLabels(value) {
      const opts =
        (this.dict && this.dict.type && this.dict.type.plan_proer) || [];
      const codes = this.parseDesignerArray(value);
      return codes.map((c) => {
        const m =
          opts.find(
            (o) =>
              String(o.value) === String(c) ||
              String(o.dictValue || "") === String(c)
          ) || null;
        return m ? m.label || m.dictLabel || c : c;
      });
    },
  },
  beforeMount() {
    clearInterval(this.time);
    this.time = null;
  },
};
</script>

<style scoped>
/* 移除按钮样式，使用默认样式 */

/* 图纸类型下拉选项样式 */
.drawing-option {
  padding: 8px 0;
  line-height: 1.4;
  width: 100%;
}

.drawing-header {
  display: flex;
  align-items: center;
  margin-bottom: 6px;
}

.drawing-header i {
  color: #409eff;
  margin-right: 8px;
  font-size: 14px;
}

.drawing-type {
  font-weight: 600;
  color: #303133;
  font-size: 14px;
}

.drawing-path {
  display: flex;
  align-items: flex-start;
  color: #909399;
  font-size: 12px;
  line-height: 1.3;
  margin-top: 6px;
  width: 100%;
  padding-left: 22px;
  /* 与上面的图标对齐 */
}

.drawing-path i {
  color: #c0c4cc;
  margin-right: 8px;
  margin-top: 1px;
  font-size: 12px;
  flex-shrink: 0;
}

.path-text {
  flex: 1;
  word-break: break-all;
  white-space: normal;
  line-height: 1.4;
  color: #909399;
  font-size: 12px;
  display: block;
  overflow: visible;
}

/* 全局下拉框宽度控制 */
:global(.drawing-select-dropdown) {
  min-width: 600px !important;
  max-width: 800px !important;
}

:global(.drawing-select-dropdown .el-select-dropdown__item) {
  padding: 12px 16px !important;
  min-height: 80px !important;
}

:global(.drawing-select-dropdown .el-select-dropdown__item .drawing-option) {
  width: 100% !important;
}

:global(.drawing-select-dropdown .el-select-dropdown__item .drawing-path) {
  display: flex !important;
  align-items: flex-start !important;
  margin-top: 6px !important;
  padding-left: 22px !important;
}

:global(.drawing-select-dropdown .el-select-dropdown__item .path-text) {
  flex: 1 !important;
  word-break: break-all !important;
  white-space: normal !important;
  line-height: 1.4 !important;
  color: #909399 !important;
  font-size: 12px !important;
  display: block !important;
  overflow: visible !important;
}

:global(.drawing-select-dropdown .el-select-dropdown__item.selected .drawing-path) {
  color: rgba(255, 255, 255, 0.8) !important;
}

:global(.drawing-select-dropdown .el-select-dropdown__item.selected .path-text) {
  color: rgba(255, 255, 255, 0.8) !important;
}

.chain-suggestion-item {
  padding: 8px 10px;
  border-bottom: 1px solid #1e0d0d;
}

.chain-suggestion-item:last-child {
  border-bottom: none;
}

.chain-name {
  font-size: 14px;
  color: #124dc4;
  margin-bottom: 4px;
}

.chain-info {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  color: #909399;
  font-size: 12px;
  margin-top: 2px;
  line-height: 1.3;
  word-break: break-all;
}

.chain-info span {
  margin-right: 8px;
}

.el-select {
  width: 100%;
  margin-bottom: 20px;
}

.project-name-column {
  color: #008b8b;
  /* 深青绿色 */
  font-weight: 500;
}

.project-name-column {
  color: #008b8b;
  /* 深青绿色 */
  font-weight: 500;
}

/* MRP运算结果复查表格样式 */
.mrp-result-card .el-card__header {
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  border-bottom: 2px solid #409eff;
}

.mrp-result-card .el-table {
  margin-top: 10px;
}

.mrp-result-card .el-table th {
  background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%) !important;
  color: #606266;
  font-weight: 600;
}

.mrp-result-card .el-table--striped .el-table__body tr.el-table__row--striped td {
  background-color: #fafafa;
}

.mrp-result-card .el-table__body tr:hover>td {
  background-color: #f0f9ff !important;
}

/* 数值列的颜色样式 */
.mrp-result-card .el-table .cell {
  font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
  font-size: 13px;
}

/* 分页样式 */
.mrp-result-card .el-pagination {
  margin-top: 20px;
  text-align: right;
}

.mrp-result-card .el-pagination .el-pager li {
  background: #fff;
  border: 1px solid #d9d9d9;
  color: #606266;
}

.mrp-result-card .el-pagination .el-pager li.active {
  background: #409eff;
  color: #fff;
  border-color: #409eff;
}

/* 负库存行样式 */
.mrp-result-card .el-table .negative-stock-row {
  background-color: #fef0f0 !important;
}

.mrp-result-card .el-table .negative-stock-row:hover>td {
  background-color: #fde2e2 !important;
}

/* 零库存行样式 */
.mrp-result-card .el-table .zero-stock-row {
  background-color: #fdf6ec !important;
}

.mrp-result-card .el-table .zero-stock-row:hover>td {
  background-color: #faecd8 !important;
}

/* MRP运算状态指示器样式 */
.mrp-status-indicator {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
  border: 1px solid #bae6fd;
  border-radius: 6px;
  color: #0369a1;
  font-size: 13px;
  font-weight: 500;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.mrp-status-indicator i {
  color: #0ea5e9;
  font-size: 14px;
}

/* MRP核算按钮样式 */
.mrp-calculate-btn {
  min-width: 100px;
  height: 36px;
  font-weight: 500;
  border-radius: 6px;
  box-shadow: 0 2px 4px rgba(64, 158, 255, 0.2);
  transition: all 0.3s ease;
}

.mrp-calculate-btn:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 4px 8px rgba(64, 158, 255, 0.3);
}

.mrp-calculate-btn:disabled {
  opacity: 0.7;
  cursor: not-allowed;
}

/* MRP导出按钮样式 */
.mrp-export-btn {
  min-width: 90px;
  height: 32px;
  font-weight: 500;
  border-radius: 6px;
  box-shadow: 0 2px 4px rgba(103, 194, 58, 0.2);
  transition: all 0.3s ease;
}

.mrp-export-btn:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 4px 8px rgba(103, 194, 58, 0.3);
}

.mrp-export-btn:disabled {
  opacity: 0.7;
  cursor: not-allowed;
}

/* MRP搜索输入框样式 */
.mrp-search-input {
  transition: all 0.3s ease;
}

.mrp-search-input:hover .el-input__inner {
  border-color: #409eff;
}

.mrp-search-input .el-input__inner:focus {
  border-color: #409eff;
  box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
</style>
