<template>
  <div class="order-pro-page app-container">
    <div class="kpi-panel" v-loading="overviewStatsLoading" element-loading-background="rgba(255, 255, 255, 0.75)">
      <div class="kpi-card kpi-card--total">
        <i class="el-icon-folder-opened kpi-card__icon"></i>
        <div class="kpi-card__body">
          <span class="kpi-card__value">{{ overviewStats.total }}</span>
          <span class="kpi-card__label">项目总数</span>
        </div>
      </div>
      <div class="kpi-card kpi-card--delay">
        <i class="el-icon-warning-outline kpi-card__icon"></i>
        <div class="kpi-card__body">
          <span class="kpi-card__value">{{ overviewStats.delayed }}</span>
          <span class="kpi-card__label">延期数</span>
        </div>
      </div>
      <div class="kpi-card kpi-card--change">
        <i class="el-icon-refresh kpi-card__icon"></i>
        <div class="kpi-card__body">
          <span class="kpi-card__value">{{ overviewStats.change }}</span>
          <span class="kpi-card__label">变更数</span>
        </div>
      </div>
      <div class="kpi-card kpi-card--production">
        <i class="el-icon-s-operation kpi-card__icon"></i>
        <div class="kpi-card__body">
          <span class="kpi-card__value">{{ overviewStats.inProduction }}</span>
          <span class="kpi-card__label">生产中数量</span>
        </div>
      </div>
    </div>

    <el-card class="search-panel" shadow="never" v-show="showSearch" :body-style="{ padding: '14px 20px' }">
      <div class="search-form" ref="queryForm">
        <div class="search-form__main">
          <div class="search-field">
            <span class="search-field__label">生产令号</span>
            <el-input
              v-model="queryParams.productionOrderNo"
              size="small"
              clearable
              placeholder="请输入生产令号"
              @keyup.enter.native="handleQuery"
            />
          </div>
          <div class="search-field">
            <span class="search-field__label">项目名称</span>
            <el-input
              v-model="queryParams.productionName"
              size="small"
              clearable
              placeholder="请输入项目名称"
              @keyup.enter.native="handleQuery"
            />
          </div>
          <div class="search-field">
            <span class="search-field__label">来源</span>
            <el-select v-model="queryParams.source" size="small" clearable placeholder="请选择">
              <el-option label="手动" value="手动" />
              <el-option label="PLM" value="PLM" />
            </el-select>
          </div>
          <div class="search-form__actions">
            <el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
            <el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
            <button
              type="button"
              class="search-form__toggle"
              @click="showAdvancedSearch = !showAdvancedSearch"
            >
              <span>{{ showAdvancedSearch ? '收起筛选' : '更多筛选' }}</span>
              <i :class="showAdvancedSearch ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
              <em v-if="!showAdvancedSearch && advancedFilterCount > 0" class="search-form__count">{{ advancedFilterCount }}</em>
            </button>
          </div>
        </div>

        <transition name="el-fade-in-linear">
          <div v-if="showAdvancedSearch" class="search-form__advanced">
            <div class="search-form__advanced-head">
              <span class="search-form__advanced-title">更多筛选</span>
              <span class="search-form__advanced-tip">支持组合条件过滤项目</span>
            </div>
            <div class="search-form__advanced-grid">
              <div class="search-field">
                <span class="search-field__label">图号</span>
                <el-input
                  v-model="queryParams.drawingNo"
                  size="small"
                  clearable
                  placeholder="请输入图号"
                  @keyup.enter.native="handleQuery"
                />
              </div>
              <div class="search-field">
                <span class="search-field__label">名称</span>
                <el-input
                  v-model="queryParams.drawingName"
                  size="small"
                  clearable
                  placeholder="请输入名称"
                  @keyup.enter.native="handleQuery"
                />
              </div>
              <div class="search-field">
                <span class="search-field__label">是否延期</span>
                <el-select v-model="queryParams.whetherDelay" size="small" clearable placeholder="请选择">
                  <el-option label="是" :value="1" />
                  <el-option label="否" :value="0" />
                </el-select>
              </div>
              <div class="search-field">
                <span class="search-field__label">项目状态</span>
                <el-select v-model="queryParams.bomStatus" size="small" clearable placeholder="请选择项目状态">
                  <el-option
                    v-for="dict in dict.type.process_status"
                    :key="dict.value"
                    :label="dict.label"
                    :value="dict.value"
                  />
                </el-select>
              </div>
              <div class="search-field">
                <span class="search-field__label search-field__label--wide">工艺审核</span>
                <el-select v-model="queryParams.routeStatus" size="small" clearable placeholder="请选择">
                  <el-option label="未审核" :value="0" />
                  <el-option label="已审核" :value="1" />
                  <el-option label="已拒绝" :value="2" />
                </el-select>
              </div>
              <div class="search-field">
                <span class="search-field__label search-field__label--wide">计划表发送</span>
                <el-select v-model="queryParams.routePlanSendFilter" size="small" clearable placeholder="请选择">
                  <el-option label="未发送 (0次)" value="0" />
                  <el-option label="已发送 (≥1次)" value="sent" />
                  <el-option label="1次" value="1" />
                  <el-option label="2次" value="2" />
                  <el-option label="3次及以上" value="ge3" />
                </el-select>
              </div>
              <div class="search-field">
                <span class="search-field__label search-field__label--wide">是否做工艺</span>
                <el-select v-model="queryParams.needProcess" size="small" clearable placeholder="请选择">
                  <el-option label="是" :value="1" />
                  <el-option label="否" :value="0" />
                </el-select>
              </div>
              <div class="search-field search-field--type">
                <span class="search-field__label">项目类型</span>
                <el-select
                  v-model="queryParams.isEnterpriseStandardList"
                  size="small"
                  clearable
                  multiple
                  collapse-tags
                  placeholder="请选择"
                >
                  <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>
              </div>
            </div>
          </div>
        </transition>
      </div>
    </el-card>

    <div class="toolbar-panel">
      <div class="toolbar-panel__left">
        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
          v-hasPermi="['system:orderPro:add']">新增
        </el-button>
        <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate"
          v-hasPermi="['system:orderPro:edit']">修改
        </el-button>
        <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete"
          v-hasPermi="['system:orderPro:remove']">删除
        </el-button>
        <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
          v-hasPermi="['system:orderPro:export']">导出
        </el-button>
        <el-button type="info" plain icon="el-icon-s-flag" size="mini" :disabled="multiple"
          @click="handleSetStatus" v-hasPermi="['system:orderPro:edit']">设置状态
        </el-button>
      </div>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </div>

    <el-card class="table-panel" shadow="never">
      <el-table
        v-loading="loading"
        :data="orderProList"
        ref="orderTable"
        row-key="id"
        stripe
        border
        size="small"
        class="order-pro-table"
        :header-cell-style="orderTableHeaderStyle"
        :row-class-name="getOrderRowClass"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="48" align="center" />
        <el-table-column label="生产令号" align="left" prop="productionOrderNo" min-width="140" fixed="left">
          <template slot-scope="scope">
            <el-link class="order-no-link" @click="productionCodeHandle(scope.row)" type="primary">
              <i class="el-icon-document"></i>
              {{ scope.row.productionOrderNo }}
            </el-link>
          </template>
        </el-table-column>

        <el-table-column label="执行单图" align="center" min-width="120">
          <template slot-scope="scope">
            <div v-if="getImageList(scope.row).length" class="exec-image-entry">
              <el-button type="text" size="mini" class="exec-image-trigger" @click="openExecImagePreview(scope.row)">
                <i class="el-icon-picture-outline"></i>
                查看图片
              </el-button>
            </div>
            <span v-else class="text-muted">暂无执行单图</span>
          </template>
        </el-table-column>

        <el-table-column label="项目状态" align="center" prop="imCategory" width="120">
          <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="routeStatus" width="96">
          <template slot-scope="scope">
            <span
              class="status-pill"
              :class="routeStatusPillClass(scope.row.routeStatus)"
            >{{ formatRouteStatus(scope.row.routeStatus) }}</span>
          </template>
        </el-table-column>
        <el-table-column label="生产完成" align="center" prop="productionComplete" width="96">
          <template slot-scope="scope">
            <span
              v-if="scope.row.productionComplete === true"
              class="status-pill status-pill--success"
            >已完成</span>
            <span
              v-else-if="scope.row.productionComplete === false"
              class="status-pill status-pill--warning"
            >未完成</span>
            <span v-else class="status-pill status-pill--muted">无计划</span>
          </template>
        </el-table-column>
        <el-table-column label="项目名称" align="left" prop="productionName" min-width="140" show-overflow-tooltip>
          <template slot-scope="scope">
            <span class="project-name-cell">{{ scope.row.productionName }}</span>
          </template>
        </el-table-column>
        <el-table-column label="来源" align="center" prop="source" width="80">
          <template slot-scope="scope">
            <span class="source-badge" :class="'source-badge--' + getSourceKey(scope.row.source)">
              {{ formatSource(scope.row.source) }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="是否延期" align="center" prop="whetherDelay" width="88">
          <template slot-scope="scope">
            <span
              class="status-pill"
              :class="Number(scope.row.whetherDelay) === 1 ? 'status-pill--danger' : 'status-pill--success'"
            >
              {{ Number(scope.row.whetherDelay) === 1 ? '延期' : '正常' }}
            </span>
          </template>
        </el-table-column>
         <el-table-column label="金蝶执行" align="center" min-width="168" class-name="kingdee-need-col">
          <template slot-scope="scope">
            <div
              class="kingdee-need-group"
              :class="{ 'kingdee-need-group--clickable': hasAnyKingdeeExecuted(scope.row) }"
              @click="hasAnyKingdeeExecuted(scope.row) && openKingdeeDrawer(scope.row)"
            >
              <span
                class="kingdee-need-tag kingdee-need-tag--mo"
                :class="isKingdeeExecuted(getKingdeeField(scope.row, 'prd')) ? 'kingdee-need-tag--active' : 'kingdee-need-tag--idle'"
                :title="isKingdeeExecuted(getKingdeeField(scope.row, 'prd')) ? '生产订单已执行，点击查看' : '生产订单未执行'"
                @click.stop="handleKingdeeChipClick(scope.row, 'prd', $event)"
              >
                <i class="el-icon-s-cooperation"></i>
                <em>生产</em>
                <b>{{ formatKingdeeExecStatus(getKingdeeField(scope.row, 'prd')) }}</b>
              </span>
              <span
                class="kingdee-need-tag kingdee-need-tag--po"
                :class="isKingdeeExecuted(getKingdeeField(scope.row, 'po')) ? 'kingdee-need-tag--active' : 'kingdee-need-tag--idle'"
                :title="isKingdeeExecuted(getKingdeeField(scope.row, 'po')) ? '采购订单已执行，点击查看' : '采购订单未执行'"
                @click.stop="handleKingdeeChipClick(scope.row, 'po', $event)"
              >
                <i class="el-icon-shopping-cart-2"></i>
                <em>采购</em>
                <b>{{ formatKingdeeExecStatus(getKingdeeField(scope.row, 'po')) }}</b>
              </span>
            </div>
          </template>
        </el-table-column>
        <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="延期天数" align="center" prop="delayDay" width="88">
          <template slot-scope="scope">
            <span class="delay-days" :class="{ 'delay-days--active': scope.row.delayDay > 0 }">
              {{ scope.row.delayDay != null ? scope.row.delayDay : '—' }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="计量单位" align="center" prop="unit" width="80" />
        <el-table-column label="保留零件图" align="center" prop="quantity" width="96">
          <template slot-scope="scope">
            <el-tag size="mini" :type="Number(scope.row.quantity) === 1 ? 'success' : 'info'" effect="plain">
              {{ Number(scope.row.quantity) === 1 ? '是' : '否' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="下载图纸" align="center" prop="isDownload" width="88">
          <template slot-scope="scope">
            <el-tag size="mini" :type="Number(scope.row.isDownload) === 1 ? 'success' : 'info'" effect="plain">
              {{ Number(scope.row.isDownload) === 1 ? '是' : '否' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="是否做工艺" align="center" prop="needProcess" width="96">
          <template slot-scope="scope">
            <el-tag size="mini" :type="Number(scope.row.needProcess) === 1 ? 'success' : 'info'" effect="plain">
              {{ Number(scope.row.needProcess) === 1 ? '是' : '否' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="项目类型" align="center" prop="isEnterpriseStandard" width="96">
          <template slot-scope="scope">
            <span class="type-badge" :class="'type-badge--' + getProjectTypeKey(scope.row.isEnterpriseStandard)">
              {{ getProjectTypeLabel(scope.row.isEnterpriseStandard) }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="计划表发送" align="center" width="96">
          <template slot-scope="scope">
            <span v-if="getRowRoutePlanSendCount(scope.row) === null" class="text-muted">—</span>
            <span
              v-else
              class="send-count-badge"
              :class="{ 'send-count-badge--sent': getRowRoutePlanSendCount(scope.row) > 0 }"
            >
              {{ getRowRoutePlanSendCount(scope.row) }}次
            </span>
          </template>
        </el-table-column>

        <el-table-column label="创建人" align="center" prop="createBy" width="90" show-overflow-tooltip />
        <el-table-column label="出图时间" align="center" prop="drawingTime" width="110">
          <template slot-scope="scope">
            <span class="date-cell">{{ drawingTime(scope.row, 'drawingTime', '{y}-{m}-{d}') }}</span>
          </template>
        </el-table-column>
        <el-table-column label="项目完成时间" align="center" prop="projectEndTime" width="110">
          <template slot-scope="scope">
            <span class="date-cell">{{ formatStepTime(scope.row, 'projectEndTime', '{y}-{m}-{d}') }}</span>
          </template>
        </el-table-column>
        <el-table-column label="创建时间" align="center" prop="createTime" width="160">
          <template slot-scope="scope">
            <span class="date-cell">{{ formatStepTime(scope.row, 'createTime', '{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="168">
          <template slot-scope="scope">
            <el-dropdown
              :split-button="true"
              size="small"
              type="primary"
              class="action-dropdown"
              @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>

      <div class="table-panel__footer">
        <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
          @pagination="getList" />
      </div>
    </el-card>
    <el-drawer
      :title="getDrawerTitle()"
      :visible.sync="drawer"
      :direction="direction"
      :before-close="handleClose"
      size="90%"
      append-to-body
      :modal-append-to-body="true"
      :destroy-on-close="false"
      custom-class="order-pro-drawer"
    >
      <div class="drawer-body">
        <el-tabs v-model="drawerActiveTab" class="drawer-main-tabs" @tab-click="handleDrawerTabClick">
          <el-tab-pane label="项目出图" name="project">
        <div class="drawer-section-title drawer-section-title--green drawer-section-title--block">
          <i class="el-icon-picture"></i>
          <span>选择产品</span>
        </div>
        <!-- 第一部分：销齿链型号表格管理 -->
        <div class="drawer-toolbar">
          <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>

          <!-- 出图状态展示按钮 -->
          <el-button
            :type="plotStatusType"
            size="mini"
            @click="fetchPlotStatus"
            style="margin-left: 10px;"
            title="点击刷新出图状态"
          >
            <i class="el-icon-picture-outline" style="margin-right: 4px;"></i>
             出图状态：{{ plotStatusText }}
             <span v-if="plotProjectName" style="margin-left: 8px;">项目：{{ plotProjectName }}</span>
             <el-tooltip v-if="plotOutputPath || plotTargetDrawing || plotStateMsg" effect="dark" :content="plotDetailText" placement="top">
               <i class="el-icon-info" style="margin-left: 6px;"></i>
             </el-tooltip>
            <i v-if="plotStatusLoading" class="el-icon-loading" style="margin-left: 4px;"></i>
          </el-button>
        </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="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="创建人" min-width="100" :show-overflow-tooltip="true">
            <template slot-scope="scope">
              <span>{{ scope.row.createBy || scope.row.create_by || '—' }}</span>
            </template>
          </el-table-column>
          <el-table-column label="修改人" min-width="100" :show-overflow-tooltip="true">
            <template slot-scope="scope">
              <span>{{ scope.row.updateBy || scope.row.update_by || '—' }}</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" class="drawer-divider">
          <div class="drawer-section-title drawer-section-title--green">
            <i class="el-icon-picture"></i>
            <span>执行出图</span>
          </div>
        </el-divider>
        <!-- 第二部分：执行出图命令及日志展示 -->
        <div class="drawer-action-block">
          <div class="drawer-toolbar drawer-toolbar--wrap">
            <el-button type="success" :loading="drawLoading" @click="executeDrawCommand"
              v-hasPermi="['system:orderPro:executDrawing']">
              执行出图
            </el-button>
            <el-button type="success" plain :loading="sendProcessRoutePlanLoading" @click="sendProcessRoutePlan"
              v-hasPermi="['system:orderPro:sendProcessRoutePlanExcel']"
              title="发送工艺计划表（钉钉推送分类 BOM Excel），括号内为已成功发送次数">
              发送计划表（{{ routePlanSendCount }}次）
            </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="warning"
              plain
              :loading="drawingWorkloadLoading"
              @click="syncDrawingWorkload"
              v-hasPermi="['system:orderPro:drawingWorkload']"
            >
              读取图纸工作量
            </el-button>

            <el-button type="danger" @click="pushProcessInbound" v-hasPermi="['system:route:updateProductionOrders']">
              推送工序计划单入库
            </el-button>
            <el-button
              type="warning"
              :loading="downloadPlmDwgLoading"
              @click="downloadPlmDwgZip"
              v-hasPermi="['system:orderPro:downloadZip']"
            >
              下载PLMDWG图纸
            </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" class="draw-progress"></el-progress>
          <el-card shadow="never" class="draw-log-card">
            <div class="draw-log-content">
              <template v-if="drawLog">{{ drawLog }}</template>
              <template v-else>暂无日志</template>
            </div>
          </el-card>
        </div>
        <el-divider content-position="center" class="drawer-divider">
          <div class="drawer-section-title drawer-section-title--blue">
            <i class="el-icon-data-analysis"></i>
            <span>MRP运算结果复查</span>
          </div>
        </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>
          </el-tab-pane>

          <el-tab-pane label="生产订单" name="prdMo">
            <div class="kingdee-tab-toolbar">
              <el-button type="primary" size="mini" icon="el-icon-refresh" :loading="kingdeePrdMoLoading" @click="fetchKingdeePrdMo">
                刷新
              </el-button>
            </div>
            <el-table
              :data="kingdeePrdMoList"
              v-loading="kingdeePrdMoLoading"
              border
              stripe
              size="small"
              style="width: 100%"
              empty-text="暂无生产订单数据"
            >
              <el-table-column label="生产订单编号" min-width="130" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.FBillNo || '—' }}</template>
              </el-table-column>
              <el-table-column label="工艺路线编码" min-width="120" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FRoutingId.FNumber'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="生产车间" min-width="100" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FWorkShopID.FName'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="物料编码" min-width="120" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FMaterialId.FNumber'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="物料名称" min-width="140" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.FMaterialName || '—' }}</template>
              </el-table-column>
              <el-table-column label="数量" width="80" align="right">
                <template slot-scope="scope">{{ scope.row.FQty != null ? scope.row.FQty : '—' }}</template>
              </el-table-column>
              <el-table-column label="材质" min-width="90" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.F_UCHN_BaseProperty_qtr || '—' }}</template>
              </el-table-column>
              <el-table-column label="单位" width="70" align="center">
                <template slot-scope="scope">{{ scope.row['FUnitId.FName'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="计划开工" min-width="110" align="center">
                <template slot-scope="scope">{{ formatKingdeeDate(scope.row.FPlanStartDate) }}</template>
              </el-table-column>
              <el-table-column label="计划完工" min-width="110" align="center">
                <template slot-scope="scope">{{ formatKingdeeDate(scope.row.FPlanFinishDate) }}</template>
              </el-table-column>
              <el-table-column label="领料状态" width="100" align="center">
                <template slot-scope="scope">
                  <el-tag size="mini" :type="getPickMtrlStatusTag(scope.row.FPickMtrlStatus)">
                    {{ formatPickMtrlStatus(scope.row.FPickMtrlStatus) }}
                  </el-tag>
                </template>
              </el-table-column>
            </el-table>
          </el-tab-pane>

          <el-tab-pane label="采购订单" name="purchaseOrder">
            <div class="kingdee-tab-toolbar">
              <el-button type="primary" size="mini" icon="el-icon-refresh" :loading="kingdeePurchaseLoading" @click="fetchKingdeePurchaseOrder">
                刷新
              </el-button>
            </div>
            <el-table
              :data="kingdeePurchaseOrderList"
              v-loading="kingdeePurchaseLoading"
              border
              stripe
              size="small"
              style="width: 100%"
              empty-text="暂无采购订单数据"
            >
              <el-table-column label="采购订单编号" min-width="130" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.FBillNo || '—' }}</template>
              </el-table-column>
              <el-table-column label="物料编码" min-width="120" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FMaterialId.FNumber'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="物料名称" min-width="140" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.FMaterialName || '—' }}</template>
              </el-table-column>
              <el-table-column label="采购数量" width="90" align="right">
                <template slot-scope="scope">{{ scope.row.FQty != null ? scope.row.FQty : '—' }}</template>
              </el-table-column>
              <el-table-column label="订单日期" min-width="110" align="center">
                <template slot-scope="scope">{{ formatKingdeeDate(scope.row.FDate) }}</template>
              </el-table-column>
              <el-table-column label="交货日期" min-width="110" align="center">
                <template slot-scope="scope">{{ formatKingdeeDate(scope.row.FDeliveryDate) }}</template>
              </el-table-column>
              <el-table-column label="剩余未入库" width="100" align="right">
                <template slot-scope="scope">{{ scope.row.FRemainStockINQty != null ? scope.row.FRemainStockINQty : '—' }}</template>
              </el-table-column>
              <el-table-column label="MRP关闭状态" width="110" align="center">
                <template slot-scope="scope">
                  <el-tag size="mini" :type="scope.row.FMRPCloseStatus === 'B' ? 'info' : 'success'">
                    {{ formatMrpCloseStatus(scope.row.FMRPCloseStatus) }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="供应商" min-width="140" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FSupplierId.FName'] || '—' }}</template>
              </el-table-column>
            </el-table>
          </el-tab-pane>
        </el-tabs>
      </div>
    </el-drawer>

    <el-drawer
      :title="getKingdeeDrawerTitle()"
      :visible.sync="kingdeeDrawer"
      direction="rtl"
      size="78%"
      append-to-body
      :modal-append-to-body="true"
      :before-close="handleKingdeeDrawerClose"
      custom-class="order-pro-drawer kingdee-order-drawer"
    >
      <div class="kingdee-drawer-body">
        <div v-if="productionObj" class="kingdee-drawer-summary">
          <div class="kingdee-drawer-summary__item">
            <span class="kingdee-drawer-summary__label">生产令号</span>
            <span class="kingdee-drawer-summary__value">{{ productionObj.productionOrderNo || '—' }}</span>
          </div>
          <div class="kingdee-drawer-summary__item">
            <span class="kingdee-drawer-summary__label">项目名称</span>
            <span class="kingdee-drawer-summary__value">{{ productionObj.productionName || '—' }}</span>
          </div>
          <div class="kingdee-drawer-summary__flags">
            <span
              class="kingdee-drawer-flag"
              :class="isKingdeeExecuted(getKingdeeField(productionObj, 'prd')) ? 'kingdee-drawer-flag--done' : 'kingdee-drawer-flag--idle'"
            >
              <i class="el-icon-s-cooperation"></i>
              生产订单 {{ formatKingdeeExecStatus(getKingdeeField(productionObj, 'prd')) }}
            </span>
            <span
              class="kingdee-drawer-flag"
              :class="isKingdeeExecuted(getKingdeeField(productionObj, 'po')) ? 'kingdee-drawer-flag--done' : 'kingdee-drawer-flag--idle'"
            >
              <i class="el-icon-shopping-cart-2"></i>
              采购订单 {{ formatKingdeeExecStatus(getKingdeeField(productionObj, 'po')) }}
            </span>
          </div>
        </div>

        <el-tabs v-model="kingdeeActiveTab" class="kingdee-order-tabs" @tab-click="handleKingdeeTabClick">
          <el-tab-pane name="prdMo">
            <span slot="label" class="kingdee-tab-label">
              <i class="el-icon-s-cooperation"></i>
              生产订单
              <span v-if="isKingdeeExecuted(getKingdeeField(productionObj, 'prd'))" class="kingdee-tab-dot"></span>
            </span>
            <div class="kingdee-tab-toolbar">
              <el-button type="primary" size="mini" icon="el-icon-refresh" :loading="kingdeePrdMoLoading" @click="fetchKingdeePrdMo">
                刷新
              </el-button>
            </div>
            <el-table
              :data="kingdeePrdMoList"
              v-loading="kingdeePrdMoLoading"
              border
              stripe
              size="small"
              class="kingdee-order-table"
              empty-text="暂无生产订单数据"
            >
              <el-table-column label="生产订单编号" min-width="130" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.FBillNo || '—' }}</template>
              </el-table-column>
              <el-table-column label="工艺路线编码" min-width="120" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FRoutingId.FNumber'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="生产车间" min-width="100" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FWorkShopID.FName'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="物料编码" min-width="120" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FMaterialId.FNumber'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="物料名称" min-width="140" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.FMaterialName || '—' }}</template>
              </el-table-column>
              <el-table-column label="数量" width="80" align="right">
                <template slot-scope="scope">{{ scope.row.FQty != null ? scope.row.FQty : '—' }}</template>
              </el-table-column>
              <el-table-column label="材质" min-width="90" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.F_UCHN_BaseProperty_qtr || '—' }}</template>
              </el-table-column>
              <el-table-column label="单位" width="70" align="center">
                <template slot-scope="scope">{{ scope.row['FUnitId.FName'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="计划开工" min-width="110" align="center">
                <template slot-scope="scope">{{ formatKingdeeDate(scope.row.FPlanStartDate) }}</template>
              </el-table-column>
              <el-table-column label="计划完工" min-width="110" align="center">
                <template slot-scope="scope">{{ formatKingdeeDate(scope.row.FPlanFinishDate) }}</template>
              </el-table-column>
              <el-table-column label="领料状态" width="100" align="center">
                <template slot-scope="scope">
                  <el-tag size="mini" :type="getPickMtrlStatusTag(scope.row.FPickMtrlStatus)">
                    {{ formatPickMtrlStatus(scope.row.FPickMtrlStatus) }}
                  </el-tag>
                </template>
              </el-table-column>
            </el-table>
          </el-tab-pane>

          <el-tab-pane name="purchaseOrder">
            <span slot="label" class="kingdee-tab-label">
              <i class="el-icon-shopping-cart-2"></i>
              采购订单
              <span v-if="isKingdeeExecuted(getKingdeeField(productionObj, 'po'))" class="kingdee-tab-dot"></span>
            </span>
            <div class="kingdee-tab-toolbar">
              <el-button type="primary" size="mini" icon="el-icon-refresh" :loading="kingdeePurchaseLoading" @click="fetchKingdeePurchaseOrder">
                刷新
              </el-button>
            </div>
            <el-table
              :data="kingdeePurchaseOrderList"
              v-loading="kingdeePurchaseLoading"
              border
              stripe
              size="small"
              class="kingdee-order-table"
              empty-text="暂无采购订单数据"
            >
              <el-table-column label="采购订单编号" min-width="130" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.FBillNo || '—' }}</template>
              </el-table-column>
              <el-table-column label="物料编码" min-width="120" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FMaterialId.FNumber'] || '—' }}</template>
              </el-table-column>
              <el-table-column label="物料名称" min-width="140" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row.FMaterialName || '—' }}</template>
              </el-table-column>
              <el-table-column label="采购数量" width="90" align="right">
                <template slot-scope="scope">{{ scope.row.FQty != null ? scope.row.FQty : '—' }}</template>
              </el-table-column>
              <el-table-column label="订单日期" min-width="110" align="center">
                <template slot-scope="scope">{{ formatKingdeeDate(scope.row.FDate) }}</template>
              </el-table-column>
              <el-table-column label="交货日期" min-width="110" align="center">
                <template slot-scope="scope">{{ formatKingdeeDate(scope.row.FDeliveryDate) }}</template>
              </el-table-column>
              <el-table-column label="剩余未入库" width="100" align="right">
                <template slot-scope="scope">{{ scope.row.FRemainStockINQty != null ? scope.row.FRemainStockINQty : '—' }}</template>
              </el-table-column>
              <el-table-column label="MRP关闭状态" width="110" align="center">
                <template slot-scope="scope">
                  <el-tag size="mini" :type="scope.row.FMRPCloseStatus === 'B' ? 'info' : 'success'">
                    {{ formatMrpCloseStatus(scope.row.FMRPCloseStatus) }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="供应商" min-width="140" show-overflow-tooltip>
                <template slot-scope="scope">{{ scope.row['FSupplierId.FName'] || '—' }}</template>
              </el-table-column>
            </el-table>
          </el-tab-pane>
        </el-tabs>
      </div>
    </el-drawer>

    <el-drawer
      :title="bomDrawerTitle"
      :visible.sync="bomDrawer"
      direction="rtl"
      size="92%"
      append-to-body
      :modal-append-to-body="true"
      custom-class="order-pro-drawer bom-detail-drawer"
    >
      <div class="drawer-body drawer-body--bom">
        <bomInfo v-if="bomDrawer" :params="bomParams" />
      </div>
    </el-drawer>

    <el-drawer
      title="工艺路线"
      :visible.sync="processDrawer"
      direction="rtl"
      size="90%"
      append-to-body
      :modal-append-to-body="true"
      custom-class="order-pro-drawer"
    >
      <div class="drawer-body">
        <processInfo v-if="processDrawer" :params="processParams" />
      </div>
    </el-drawer>

    <!-- 添加或修改项目令号对话框 -->
    <el-dialog
      :visible.sync="open"
      width="680px"
      append-to-body
      custom-class="order-pro-form-dialog"
      :close-on-click-modal="false"
    >
      <div slot="title" class="order-form-dialog__title">
        <span class="order-form-dialog__title-icon">
          <i :class="form.id != null ? 'el-icon-edit-outline' : 'el-icon-folder-add'"></i>
        </span>
        <div class="order-form-dialog__title-text">
          <h3>{{ title }}</h3>
          <p>{{ form.id != null ? '更新项目基础信息与图纸配置' : '创建新项目，金蝶执行状态默认为未执行' }}</p>
        </div>
      </div>

      <div class="order-form-dialog__body">
        <el-form ref="form" :model="form" :rules="rules" label-width="108px" class="order-form-dialog__form">
          <section class="order-form-section">
            <header class="order-form-section__head">
              <i class="el-icon-document"></i>
              <span>基本信息</span>
            </header>
            <div class="order-form-section__grid order-form-section__grid--2">
              <el-form-item label="生产令号" prop="productionOrderNo">
                <el-input v-model="form.productionOrderNo" placeholder="请输入生产令号" prefix-icon="el-icon-tickets" />
              </el-form-item>
              <el-form-item label="项目名称" prop="productionName">
                <el-input v-model="form.productionName" placeholder="请输入项目名称" prefix-icon="el-icon-office-building" />
              </el-form-item>
              <el-form-item label="来源" prop="source">
                <el-select v-model="form.source" placeholder="请选择来源" style="width: 100%">
                  <el-option label="手动" value="手动" />
                  <el-option label="PLM" value="PLM" />
                </el-select>
              </el-form-item>
              <el-form-item label="计量单位" prop="unit">
                <el-input v-model="form.unit" placeholder="请输入计量单位" prefix-icon="el-icon-c-scale-to-original" />
              </el-form-item>
            </div>
          </section>

          <section class="order-form-section">
            <header class="order-form-section__head order-form-section__head--green">
              <i class="el-icon-picture-outline"></i>
              <span>图纸设置</span>
            </header>
            <el-form-item label="执行单图" class="order-form-item--full">
              <div class="order-form-upload-wrap">
                <image-upload v-model="form.drawingPath" :limit="9" :file-size="5" />
              </div>
            </el-form-item>
            <div class="order-form-toggle-grid">
              <div class="order-form-toggle-card">
                <div class="order-form-toggle-card__info">
                  <i class="el-icon-files"></i>
                  <div>
                    <strong>保留零件图</strong>
                    <span>是否在系统中保留零件图纸</span>
                  </div>
                </div>
                <el-switch
                  v-model="form.quantity"
                  :active-value="1"
                  :inactive-value="0"
                  active-text="是"
                  inactive-text="否"
                />
              </div>
              <div class="order-form-toggle-card">
                <div class="order-form-toggle-card__info">
                  <i class="el-icon-download"></i>
                  <div>
                    <strong>下载图纸</strong>
                    <span>是否允许下载项目图纸</span>
                  </div>
                </div>
                <el-switch
                  v-model="form.isDownload"
                  :active-value="1"
                  :inactive-value="0"
                  active-text="是"
                  inactive-text="否"
                />
              </div>
              <div class="order-form-toggle-card">
                <div class="order-form-toggle-card__info">
                  <i class="el-icon-s-operation"></i>
                  <div>
                    <strong>是否做工艺</strong>
                    <span>该项目是否需要编制工艺</span>
                  </div>
                </div>
                <el-switch
                  v-model="form.needProcess"
                  :active-value="1"
                  :inactive-value="0"
                  active-text="是"
                  inactive-text="否"
                />
              </div>
            </div>
          </section>

          <section class="order-form-section order-form-section--last">
            <header class="order-form-section__head order-form-section__head--slate">
              <i class="el-icon-time"></i>
              <span>时间与进度</span>
            </header>
            <div class="order-form-section__grid order-form-section__grid--2">
              <el-form-item label="完成时间" prop="projectEndTime" class="order-form-item--full-span">
                <el-date-picker
                  clearable
                  v-model="form.projectEndTime"
                  type="datetime"
                  value-format="yyyy-MM-dd HH:mm:ss"
                  placeholder="请选择项目完成时间"
                  style="width: 100%"
                />
              </el-form-item>
              <el-form-item label="工艺上传更新" prop="routeUptime">
                <el-date-picker
                  clearable
                  v-model="form.routeUptime"
                  type="datetime"
                  value-format="yyyy-MM-dd HH:mm:ss"
                  placeholder="请选择时间"
                  style="width: 100%"
                />
              </el-form-item>
              <el-form-item label="排产计划更新" prop="planUptime">
                <el-date-picker
                  clearable
                  v-model="form.planUptime"
                  type="datetime"
                  value-format="yyyy-MM-dd HH:mm:ss"
                  placeholder="请选择时间"
                  style="width: 100%"
                />
              </el-form-item>
              <el-form-item label="BOM/物料更新" prop="bomUptime" class="order-form-item--full-span">
                <el-date-picker
                  clearable
                  v-model="form.bomUptime"
                  type="datetime"
                  value-format="yyyy-MM-dd HH:mm:ss"
                  placeholder="请选择时间"
                  style="width: 100%"
                />
              </el-form-item>
            </div>
          </section>
        </el-form>
      </div>

      <div slot="footer" class="order-form-dialog__footer">
        <el-button class="order-form-dialog__btn-cancel" @click="cancel">取 消</el-button>
        <el-button :loading="buttonLoading" type="primary" class="order-form-dialog__btn-submit" @click="submitForm">
          <i v-if="!buttonLoading" class="el-icon-check"></i>
          {{ form.id != null ? '保存修改' : '确认新增' }}
        </el-button>
      </div>
    </el-dialog>

    <el-dialog
      :visible.sync="execImagePreviewVisible"
      :title="execImagePreviewTitle"
      width="860px"
      append-to-body
      custom-class="exec-image-dialog"
    >
      <div v-if="execImagePreviewList.length" class="exec-image-dialog__grid">
        <el-image
          v-for="(img, idx) in execImagePreviewList"
          :key="idx"
          :src="img"
          :preview-src-list="execImagePreviewList"
          fit="cover"
          class="exec-image-dialog__thumb"
        />
      </div>
      <el-empty v-else description="暂无执行单图" :image-size="72" />
    </el-dialog>

    <el-dialog
      :visible.sync="drawingWorkloadVisible"
      :title="drawingWorkloadTitle"
      width="960px"
      append-to-body
      top="6vh"
    >
      <div v-if="drawingWorkload" class="drawing-workload">
        <div class="drawing-workload__meta">
          <span>生产令号：{{ drawingWorkload.productionOrderNo || '-' }}</span>
          <span>同步时间：{{ drawingWorkload.syncTime || '-' }}</span>
          <span>明细：{{ drawingWorkload.itemCount || 0 }} 条</span>
          <span>等效 A4 合计：<b>{{ drawingWorkload.equivalentA4Total != null ? drawingWorkload.equivalentA4Total : '-' }}</b></span>
        </div>
        <el-table
          :data="drawingWorkloadSummaryRows"
          border
          size="mini"
          style="width: 100%; margin-bottom: 14px"
        >
          <el-table-column
            v-for="col in drawingWorkloadSummaryCols"
            :key="col.prop"
            :prop="col.prop"
            :label="col.label"
            align="center"
            min-width="88"
          />
        </el-table>
        <el-table
          :data="drawingWorkload.items || []"
          border
          size="mini"
          max-height="420"
          style="width: 100%"
        >
          <el-table-column type="index" label="#" width="50" align="center" />
          <el-table-column prop="drawingNo" label="图号" min-width="160" show-overflow-tooltip />
          <el-table-column prop="drawingName" label="名称" min-width="160" show-overflow-tooltip />
          <el-table-column prop="frameName" label="图框名称" min-width="120" show-overflow-tooltip />
          <el-table-column prop="paperSize" label="图纸幅面" width="110" align="center" />
          <el-table-column prop="equivalentA4" label="等效A4" width="90" align="center" />
        </el-table>
      </div>
      <el-empty v-else description="暂无图纸工作量数据" :image-size="72" />
      <div slot="footer" class="dialog-footer">
        <el-button
          type="primary"
          :loading="drawingWorkloadLoading"
          @click="syncDrawingWorkload"
          v-hasPermi="['system:orderPro:drawingWorkload']"
        >重新读取</el-button>
        <el-button @click="drawingWorkloadVisible = false">关 闭</el-button>
      </div>
    </el-dialog>

    <!-- 批量设置项目状态 -->
    <el-dialog
      title="设置项目状态"
      :visible.sync="statusDialogVisible"
      width="420px"
      append-to-body
      @closed="statusForm.bomStatus = undefined"
    >
      <el-form ref="statusForm" :model="statusForm" :rules="statusRules" label-width="90px">
        <el-form-item label="已选项目">
          <span>{{ ids.length }} 个</span>
        </el-form-item>
        <el-form-item label="项目状态" prop="bomStatus">
          <el-select
            v-model="statusForm.bomStatus"
            placeholder="请选择项目状态"
            clearable
            style="width: 100%"
          >
            <el-option
              v-for="dict in dict.type.process_status"
              :key="dict.value"
              :label="dict.label"
              :value="dict.value"
            />
          </el-select>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" :loading="statusSubmitting" @click="submitStatusForm">确 定</el-button>
        <el-button @click="statusDialogVisible = false">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<style>
/* ── 新增/修改项目对话框 ── */
.order-pro-form-dialog {
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(26, 41, 66, 0.18), 0 4px 16px rgba(26, 41, 66, 0.08);
}

.order-pro-form-dialog .el-dialog__header {
  margin: 0;
  padding: 20px 24px 18px;
  background: linear-gradient(135deg, #1a2942 0%, #2c5282 100%);
  border-bottom: none;
}

.order-pro-form-dialog .el-dialog__headerbtn {
  top: 18px;
  right: 18px;
}

.order-pro-form-dialog .el-dialog__headerbtn .el-dialog__close {
  color: rgba(255, 255, 255, 0.75);
  font-size: 18px;
}

.order-pro-form-dialog .el-dialog__headerbtn:hover .el-dialog__close {
  color: #fff;
}

.order-pro-form-dialog .el-dialog__body {
  padding: 0;
  background: #f4f6f9;
}

.order-pro-form-dialog .el-dialog__footer {
  padding: 14px 24px 18px;
  background: #fff;
  border-top: 1px solid #e4e9f0;
}

.order-form-dialog__title {
  display: flex;
  align-items: center;
  gap: 14px;
}

.order-form-dialog__title-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: #fff;
  font-size: 22px;
  flex-shrink: 0;
}

.order-form-dialog__title-text h3 {
  margin: 0 0 4px;
  font-size: 17px;
  font-weight: 600;
  color: #fff;
  letter-spacing: 0.02em;
}

.order-form-dialog__title-text p {
  margin: 0;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.65);
  line-height: 1.4;
}

.order-form-dialog__body {
  max-height: calc(88vh - 180px);
  overflow-y: auto;
  padding: 18px 20px 8px;
}

.order-form-dialog__body::-webkit-scrollbar {
  width: 6px;
}

.order-form-dialog__body::-webkit-scrollbar-thumb {
  background: #c5cdd8;
  border-radius: 3px;
}

.order-form-dialog__form .el-form-item {
  margin-bottom: 16px;
}

.order-form-dialog__form .el-form-item__label {
  color: #3d4f66;
  font-weight: 500;
  line-height: 32px;
}

.order-form-dialog__form .el-input__inner,
.order-form-dialog__form .el-select .el-input__inner {
  border-radius: 8px;
  border-color: #dce3ec;
  transition: border-color 0.2s, box-shadow 0.2s;
}

.order-form-dialog__form .el-input__inner:focus,
.order-form-dialog__form .el-select .el-input.is-focus .el-input__inner {
  border-color: #2c5282;
  box-shadow: 0 0 0 2px rgba(44, 82, 130, 0.12);
}

.order-form-section {
  margin-bottom: 14px;
  padding: 16px 18px 4px;
  background: #fff;
  border: 1px solid #e4e9f0;
  border-radius: 10px;
  box-shadow: 0 1px 4px rgba(26, 41, 66, 0.04);
  animation: orderFormSectionIn 0.35s ease-out both;
}

.order-form-section:nth-child(2) { animation-delay: 0.05s; }
.order-form-section:nth-child(3) { animation-delay: 0.1s; }
.order-form-section:nth-child(4) { animation-delay: 0.15s; }

.order-form-section--last {
  margin-bottom: 6px;
}

@keyframes orderFormSectionIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

.order-form-section__head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid #eef2f6;
  font-size: 13px;
  font-weight: 600;
  color: #2c5282;
  letter-spacing: 0.04em;
}

.order-form-section__head i {
  font-size: 15px;
  opacity: 0.85;
}

.order-form-section__head--green { color: #1e7e4a; }
.order-form-section__head--amber { color: #b45309; }
.order-form-section__head--slate { color: #475569; }

.order-form-section__grid {
  display: grid;
  gap: 0 16px;
}

.order-form-section__grid--2 {
  grid-template-columns: 1fr 1fr;
}

.order-form-item--full {
  margin-bottom: 12px;
}

.order-form-item--full >>> .el-form-item__content {
  line-height: normal;
}

.order-form-item--full-span {
  grid-column: 1 / -1;
}

.order-form-upload-wrap {
  padding: 12px;
  border-radius: 8px;
  background: #f8fafc;
  border: 1px dashed #dce3ec;
}

.order-form-toggle-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 12px;
}

.order-form-toggle-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 14px;
  border-radius: 8px;
  background: #f8fafc;
  border: 1px solid #e4e9f0;
  transition: border-color 0.2s, background 0.2s;
}

.order-form-toggle-card:hover {
  border-color: #b3cce8;
  background: #f0f5fa;
}

.order-form-toggle-card__info {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
}

.order-form-toggle-card__info > i {
  margin-top: 2px;
  font-size: 16px;
  color: #2c5282;
  flex-shrink: 0;
}

.order-form-toggle-card__info strong {
  display: block;
  font-size: 13px;
  color: #1a2942;
  margin-bottom: 2px;
}

.order-form-toggle-card__info span {
  display: block;
  font-size: 11px;
  color: #909399;
  line-height: 1.35;
}

.order-form-kingdee-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 8px;
}

.order-form-kingdee-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  border-radius: 10px;
  border: 1px solid #e4e9f0;
  background: #fafbfc;
  transition: border-color 0.25s, box-shadow 0.25s, background 0.25s;
}

.order-form-kingdee-card--mo.order-form-kingdee-card--on {
  border-color: #93c5fd;
  background: linear-gradient(160deg, #eff6ff 0%, #dbeafe 100%);
  box-shadow: 0 4px 14px rgba(37, 99, 235, 0.12);
}

.order-form-kingdee-card--po.order-form-kingdee-card--on {
  border-color: #fcd34d;
  background: linear-gradient(160deg, #fffbeb 0%, #fef3c7 100%);
  box-shadow: 0 4px 14px rgba(217, 119, 6, 0.12);
}

.order-form-kingdee-card__icon {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  font-size: 16px;
}

.order-form-kingdee-card--mo .order-form-kingdee-card__icon {
  color: #2563eb;
  background: rgba(37, 99, 235, 0.12);
}

.order-form-kingdee-card--po .order-form-kingdee-card__icon {
  color: #d97706;
  background: rgba(217, 119, 6, 0.12);
}

.order-form-kingdee-card__body strong {
  display: block;
  font-size: 13px;
  color: #1a2942;
  margin-bottom: 3px;
}

.order-form-kingdee-card__body span {
  display: block;
  font-size: 11px;
  color: #909399;
  line-height: 1.4;
}

.order-form-kingdee-card >>> .el-switch {
  align-self: flex-end;
}

.order-form-dialog__footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

.order-form-dialog__btn-cancel {
  border-radius: 8px;
  padding: 10px 22px;
  border-color: #dce3ec;
  color: #5c6b7f;
}

.order-form-dialog__btn-cancel:hover {
  color: #2c5282;
  border-color: #b3cce8;
  background: #f0f5fa;
}

.order-form-dialog__btn-submit {
  border-radius: 8px;
  padding: 10px 24px;
  background: linear-gradient(135deg, #2c5282 0%, #3a6ea5 100%);
  border: none;
  font-weight: 600;
  letter-spacing: 0.03em;
  box-shadow: 0 4px 12px rgba(44, 82, 130, 0.28);
  transition: box-shadow 0.2s, transform 0.15s;
}

.order-form-dialog__btn-submit:hover,
.order-form-dialog__btn-submit:focus {
  background: linear-gradient(135deg, #234a75 0%, #2c5282 100%);
  box-shadow: 0 6px 16px rgba(44, 82, 130, 0.35);
}

.order-form-dialog__btn-submit:active {
  transform: translateY(1px);
}

@media (max-width: 720px) {
  .order-pro-form-dialog {
    width: 94vw !important;
  }

  .order-form-section__grid--2,
  .order-form-toggle-grid,
  .order-form-kingdee-grid {
    grid-template-columns: 1fr;
  }
}

.order-pro-page .action-dropdown.el-dropdown {
  vertical-align: middle;
}

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

.order-pro-page .el-icon-arrow-down {
  font-size: 12px;
}

.order-pro-drawer.el-drawer.rtl {
  display: flex;
  flex-direction: column;
  max-width: 1500px;
}

.order-pro-drawer .el-drawer__header {
  flex-shrink: 0;
  margin-bottom: 0;
  padding: 18px 24px;
  border-bottom: 1px solid #e4e9f0;
  background: linear-gradient(135deg, #f8fafc 0%, #eef2f7 100%);
  color: #1a2942;
  font-weight: 600;
  font-size: 16px;
}

.order-pro-drawer .el-drawer__body {
  flex: 1;
  min-height: 0;
  padding: 0;
  background: #f4f6f9;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}
</style>

<script>
import {
  listOrderPro,
  getOrderPro,
  delOrderPro,
  addOrderPro,
  updateOrderPro,
  getDrawingTypeDict,
  listSave,
  executDrawing,
  delSave,
  uploadPDF,
  downloadPlmDwg,
  exportRoute,
  exportRoute3,
  sendProcessRoutePlanExcel,
  getRoutePlanSendCount,
  syncDrawingWorkload as syncDrawingWorkloadApi,
  getMRPResults,
  getRouteLog,
  getBomUploadStatus,
  getPlotState,
  getKingdeePrdMo,
  getKingdeePurchaseOrder,
} from "@/api/system/orderPro";
import { processList } from "../../../api/system/orderPro";
import { listRigidChain } from "@/api/system/pcRigidChain";
import { addProduct, updateProduct } from "@/api/system/orderPro";
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",
      drawerActiveTab: "project",
      kingdeeDrawer: false,
      kingdeeActiveTab: "prdMo",
      kingdeePrdMoList: [],
      kingdeePrdMoLoading: false,
      kingdeePurchaseOrderList: [],
      kingdeePurchaseLoading: false,
      // 按钮loading
      buttonLoading: false,
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      showAdvancedSearch: false,
      // 总条数
      total: 0,
      // 项目令号表格数据
      orderProList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      execImagePreviewVisible: false,
      execImagePreviewTitle: "执行单图",
      execImagePreviewList: [],
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        productionOrderNo: undefined,
        productionName: undefined,
        drawingNo: undefined,
        drawingName: undefined,
        bomStatus: undefined,
        routePlanSendFilter: undefined,
        routeStatus: undefined,
        planEndTime: undefined,
        planStartTime: undefined,
        drawingType: undefined,
        projectEndTime: undefined,
        createBy: undefined,
        unit: undefined,
        quantity: undefined,
        isEnterpriseStandard: undefined,
        isEnterpriseStandardList: undefined,
        whetherDelay: undefined,
        source: undefined,
        needProcess: 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" },
        ],
        isDownload: [
          { required: true, message: "是否允许下载不能为空", trigger: "change" },
        ],
        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
      sendProcessRoutePlanLoading: false,
      downloadPlmDwgLoading: false,
      drawingWorkloadLoading: false,
      drawingWorkloadVisible: false,
      drawingWorkload: null,
      statusDialogVisible: false,
      statusSubmitting: false,
      statusForm: {
        bomStatus: undefined,
      },
      statusRules: {
        bomStatus: [
          { required: true, message: "请选择项目状态", trigger: "change" },
        ],
      },
      /** 工艺计划表钉钉已成功发送次数，默认 0（当前抽屉项目） */
      routePlanSendCount: 0,
      /** 列表各行计划表发送次数，key 为项目 id */
      routePlanSendCountMap: {},
      /** 顶部概览统计 */
      overviewStatsLoading: false,
      overviewStats: {
        total: 0,
        delayed: 0,
        change: 0,
        inProduction: 0,
      },
      plotStatusText: "待获取",
      plotStatusType: "info",
      plotStatusLoading: false,
      plotOutputPath: "",
      plotProjectName: "",
      plotTargetDrawing: "",
      plotStateMsg: "",
      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.fetchOverviewStats();
    this.getDrawingTypeOptions();
    this.getConfigIniOptions(); // 新增

    this.$api = { addProduct, updateProduct };
    this.fetchPlotStatus();
  },
  watch: {
    "dict.type.process_status"() {
      this.fetchOverviewStats();
    },
  },
  computed: {
    /** 折叠区内已选筛选项数量（收起时角标） */
    advancedFilterCount() {
      const q = this.queryParams || {};
      let count = 0;
      if (q.drawingNo) count++;
      if (q.drawingName) count++;
      if (q.whetherDelay === 0 || q.whetherDelay === 1) count++;
      if (q.bomStatus !== undefined && q.bomStatus !== null && q.bomStatus !== "") count++;
      if (q.routeStatus === 0 || q.routeStatus === 1 || q.routeStatus === 2) count++;
      if (q.routePlanSendFilter) count++;
      if (q.needProcess === 0 || q.needProcess === 1) count++;
      if (Array.isArray(q.isEnterpriseStandardList) && q.isEnterpriseStandardList.length > 0) count++;
      return count;
    },
    orderTableHeaderStyle() {
      return {
        background: "#eef2f7",
        color: "#1a2942",
        fontWeight: "600",
        fontSize: "13px",
        borderBottom: "2px solid #c9a227",
      };
    },
    drawingWorkloadTitle() {
      const no = this.drawingWorkload && this.drawingWorkload.productionOrderNo;
      return no ? `图纸工作量 · ${no}` : "图纸工作量";
    },
    drawingWorkloadSummaryCols() {
      return [
        { prop: "a4Count", label: "A4" },
        { prop: "a3Count", label: "A3" },
        { prop: "a3HalfCount", label: "A3+1/2" },
        { prop: "a3Plus1Count", label: "A3+1" },
        { prop: "a2Count", label: "A2" },
        { prop: "a2HalfCount", label: "A2+1/2" },
        { prop: "a1Count", label: "A1" },
        { prop: "a1HalfCount", label: "A1+1/2" },
        { prop: "a0Count", label: "A0" },
        { prop: "a0HalfCount", label: "A0+1/2" },
        { prop: "equivalentA4Total", label: "等效A4合计" },
      ];
    },
    drawingWorkloadSummaryRows() {
      if (!this.drawingWorkload) {
        return [];
      }
      const w = this.drawingWorkload;
      return [{
        a4Count: w.a4Count,
        a3Count: w.a3Count,
        a3HalfCount: w.a3HalfCount,
        a3Plus1Count: w.a3Plus1Count,
        a2Count: w.a2Count,
        a2HalfCount: w.a2HalfCount,
        a1Count: w.a1Count,
        a1HalfCount: w.a1HalfCount,
        a0Count: w.a0Count,
        a0HalfCount: w.a0HalfCount,
        equivalentA4Total: w.equivalentA4Total,
      }];
    },
    bomDrawerTitle() {
      const orderNo = this.bomParams.productionOrderNo || (this.productionObj && this.productionObj.productionOrderNo);
      const name = this.bomParams.productionName || (this.productionObj && this.productionObj.productionName);
      if (orderNo && name) {
        return "BOM明细 · " + orderNo + " · " + name;
      }
      return orderNo ? "BOM明细 · " + orderNo : "BOM明细";
    },
    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);
    },
    plotDetailText() {
      const parts = [];
      if (this.plotOutputPath) parts.push(`路径: ${this.plotOutputPath}`);
      if (this.plotProjectName) parts.push(`项目: ${this.plotProjectName}`);
      if (this.plotTargetDrawing) parts.push(`图纸: ${this.plotTargetDrawing}`);
      if (this.plotStateMsg) parts.push(`提示: ${this.plotStateMsg}`);
      return parts.join(' | ');
    },
  },
  methods: {
    // 解析 /system/orderPro/getstate 返回的出图状态（兼容拦截器与字符串 JSON）
    parsePlotStateResponse(res) {
      if (!res) {
        return null;
      }
      let stateObj = null;
      if (res.code === 200 && typeof res.data === "string") {
        try {
          stateObj = JSON.parse(res.data);
        } catch (e) {
          console.error("Failed to parse getstate data:", e);
        }
      } else if (typeof res === "string") {
        try {
          stateObj = JSON.parse(res);
        } catch (e) {
          stateObj = null;
        }
      } else if (res.data && typeof res.data === "object") {
        stateObj = res.data;
      } else if (res.processstate !== undefined) {
        stateObj = res;
      }
      return stateObj;
    },
    normalizePlotProcessState(state) {
      if (state === null || state === undefined || state === "") {
        return null;
      }
      const n = Number(state);
      return Number.isFinite(n) ? n : null;
    },
    clearDrawProgressTimer() {
      if (this.time) {
        clearInterval(this.time);
        this.time = null;
      }
    },
    applyPlotProgressFromMsg(msg) {
      if (!msg) {
        return;
      }
      const parts = String(msg).split(",");
      if (parts.length <= 1) {
        return;
      }
      const progress = parts[1]
        .replace(".", "")
        .replace(/\s/g, "")
        .split("/");
      const current = Number(progress[0]);
      const total = Number(progress[1]);
      if (total > 0 && Number.isFinite(current)) {
        this.percentage = Number(((current / total) * 100).toFixed(2));
      }
    },
    // 获取出图状态
    fetchPlotStatus() {
      this.plotStatusLoading = true;
      getPlotState().then(res => {
        this.plotStatusLoading = false;

        const stateObj = this.parsePlotStateResponse(res);

        if (stateObj && stateObj.processstate !== undefined) {
          this.plotOutputPath = stateObj.outputpath || "";
          const path = String(this.plotOutputPath || "");
          const normalized = path.replace(/\\\\/g, "/");
          this.plotProjectName = normalized.split("/").filter(Boolean).pop() || "";
          const msg = stateObj.msg || "";
          this.plotStateMsg = msg || "";
          const match = String(msg).match(/未找到装配图[:：]\s*([^,，。\s"]+)/);
          this.plotTargetDrawing = (match && match[1]) ? match[1] : (stateObj.sourcedrawing || "");

          const status = stateObj.processstate;
          switch (String(status)) {
            case '0':
              this.plotStatusText = '无任务';
              this.plotStatusType = 'info';
              break;
            case '1':
              this.plotStatusText = '生成图纸中';
              this.plotStatusType = 'primary';
              break;
            case '2':
              this.plotStatusText = '导出明细中';
              this.plotStatusType = 'primary';
              break;
            case '3':
              this.plotStatusText = '导出PDF中';
              this.plotStatusType = 'primary';
              break;
            case '4':
              this.plotStatusText = '已完成';
              this.plotStatusType = 'success';
              break;
            case '5':
              this.plotStatusText = '出图失败';
              this.plotStatusType = 'danger';
              break;
            default:
              this.plotStatusText = '异常';
              this.plotStatusType = 'warning';
          }
        } else {
          this.plotStatusText = '解析状态失败';
          this.plotStatusType = 'warning';
        }
      }).catch(err => {
        this.plotStatusLoading = false;
        this.plotStatusText = '获取失败';
        this.plotStatusType = 'danger';
      });
    },
    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) {
      this.drawerActiveTab = "project";
      this.kingdeePrdMoList = [];
      this.kingdeePurchaseOrderList = [];
      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.drawerActiveTab = "project";
      this.kingdeePrdMoList = [];
      this.kingdeePurchaseOrderList = [];
      this.routePlanSendCount = Object.prototype.hasOwnProperty.call(this.routePlanSendCountMap, row.id)
        ? this.routePlanSendCountMap[row.id]
        : 0;
      this.drawer = true;
      // 打开抽屉后加载表格数据
      this.loadChainTableData();
      // 自动加载工艺上传日志
      if (row && row.id) {
        this.fetchRoutePlanSendCount();
        this.fetchProcessLog();
      }
      // 自动加载BOM上传日志
      if (row && row.productionOrderNo) {
        this.fetchBomLog();
      }
    },
    /** 项目类型缩写文本 */
    getProjectTypeLabel(code) {
      if (!code) return "-";
      const map = {
        CP: "产品",
        FB: "非标",
        BG: "变更",
        QB: "研发",
        EY: "研发",
        GZ: "固资",
        ZP: "展品",
        KC: "库存",
        产品: "产品",
        非标: "非标",
        变更: "变更",
        研发: "研发",
        企标: "企标",
        库存: "库存",
        固资: "固资",
        展品: "展品",
      };
      return map[String(code)] || code;
    },
    /** 项目类型样式 key */
    getProjectTypeKey(code) {
      const label = this.getProjectTypeLabel(code);
      const keyMap = {
        产品: "product",
        非标: "custom",
        变更: "change",
        研发: "rd",
        企标: "standard",
        库存: "stock",
        固资: "asset",
        展品: "exhibit",
      };
      return keyMap[label] || "default";
    },
    /** 来源显示文本 */
    formatSource(value) {
      const text = value != null ? String(value).trim() : "";
      return text || "手动";
    },
    /** 工艺审核状态文案：0未审核 1已审核 2已拒绝 */
    formatRouteStatus(value) {
      const n = value === null || value === undefined || value === "" ? null : Number(value);
      if (n === 0) return "未审核";
      if (n === 1) return "已审核";
      if (n === 2) return "已拒绝";
      return "-";
    },
    routeStatusPillClass(value) {
      const n = value === null || value === undefined || value === "" ? null : Number(value);
      if (n === 1) return "status-pill--success";
      if (n === 2) return "status-pill--danger";
      if (n === 0) return "status-pill--warning";
      return "status-pill--muted";
    },
    /** 来源样式 key */
    getSourceKey(value) {
      return String(value || "手动").trim().toUpperCase() === "PLM" ? "plm" : "manual";
    },
    /** 解析计划表发送次数接口返回值 */
    parseRoutePlanSendCount(res) {
      const n = res && res.data !== undefined && res.data !== null ? Number(res.data) : 0;
      return Number.isFinite(n) && n >= 0 ? n : 0;
    },
    /** 解析「生产中」对应的项目状态字典值 */
    resolveProductionBomStatus() {
      const options = (this.dict && this.dict.type && this.dict.type.process_status) || [];
      const hit = options.find((o) => String(o.label || "").includes("生产中"));
      return hit ? hit.value : undefined;
    },
    /** 加载顶部概览统计（全库，不受当前筛选影响） */
    async fetchOverviewStats() {
      this.overviewStatsLoading = true;
      try {
        const base = { pageNum: 1, pageSize: 1 };
        const productionStatus = this.resolveProductionBomStatus();
        const tasks = [
          listOrderPro({ ...base }),
          listOrderPro({ ...base, whetherDelay: 1 }),
          listOrderPro({ ...base, isEnterpriseStandardList: "变更" }),
        ];
        if (productionStatus !== undefined && productionStatus !== null && productionStatus !== "") {
          tasks.push(listOrderPro({ ...base, bomStatus: productionStatus }));
        }
        const results = await Promise.all(tasks);
        this.overviewStats = {
          total: results[0].total || 0,
          delayed: results[1].total || 0,
          change: results[2].total || 0,
          inProduction: results[3] ? results[3].total || 0 : 0,
        };
      } catch (e) {
        /* 统计失败时不阻断页面 */
      } finally {
        this.overviewStatsLoading = false;
      }
    },
    /** 列表行计划表发送次数，未加载完返回 null */
    getRowRoutePlanSendCount(row) {
      if (!row || row.id == null) return null;
      if (!Object.prototype.hasOwnProperty.call(this.routePlanSendCountMap, row.id)) {
        return null;
      }
      return this.routePlanSendCountMap[row.id];
    },
    /** 批量加载当前页各行计划表发送次数 */
    async loadRoutePlanSendCountsForList(rows) {
      const list = (rows || []).filter((r) => r && r.id != null);
      if (!list.length) return;
      await Promise.all(
        list.map(async (row) => {
          try {
            const res = await getRoutePlanSendCount(row.id);
            this.$set(this.routePlanSendCountMap, row.id, this.parseRoutePlanSendCount(res));
          } catch (e) {
            this.$set(this.routePlanSendCountMap, row.id, 0);
          }
        })
      );
    },
    /** 表格行样式：延期高亮 */
    getOrderRowClass({ row }) {
      if (Number(row.whetherDelay) === 1) {
        return "order-row--delayed";
      }
      return "";
    },
    /** 组装列表/导出查询参数（含计划表发送筛选项映射） */
    buildOrderProQueryParams() {
      const params = { ...this.queryParams };
      if (Array.isArray(params.isEnterpriseStandardList) && params.isEnterpriseStandardList.length > 0) {
        params.isEnterpriseStandardList = params.isEnterpriseStandardList.join(",");
      }
      delete params.isEnterpriseStandard;
      const sendFilter = params.routePlanSendFilter;
      delete params.routePlanSendFilter;
      delete params.routePlanSendCount;
      delete params.routePlanSendCountMin;
      if (sendFilter === "0") {
        params.routePlanSendCount = 0;
      } else if (sendFilter === "sent") {
        params.routePlanSendCountMin = 1;
      } else if (sendFilter === "1" || sendFilter === "2") {
        params.routePlanSendCount = Number(sendFilter);
      } else if (sendFilter === "ge3") {
        params.routePlanSendCountMin = 3;
      }
      return params;
    },
    /** 查询项目令号列表 */
    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 */
      }
      try {
        const response = await listOrderPro(this.buildOrderProQueryParams());
        this.orderProList = response.rows;
        this.total = response.total;
        this.loadRoutePlanSendCountsForList(response.rows);
      } 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,
        source: "手动",
        quantity: 0,
        isDownload: 0,
        needProcess: 1,
        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,
        productionName: this.productionObj && this.productionObj.productionName,
      };
      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 "项目详情";
      }
    },
    handleDrawerTabClick(tab) {
      if (!this.productionObj || !this.productionObj.id) return;
      if (tab.name === "prdMo" && !this.kingdeePrdMoList.length && !this.kingdeePrdMoLoading) {
        this.fetchKingdeePrdMo();
      }
      if (tab.name === "purchaseOrder" && !this.kingdeePurchaseOrderList.length && !this.kingdeePurchaseLoading) {
        this.fetchKingdeePurchaseOrder();
      }
    },
    getKingdeeField(row, type) {
      if (!row) return 0;
      if (type === "prd") {
        const raw = row.need_kingdee_prd_mo != null ? row.need_kingdee_prd_mo : row.needKingdeePrdMo;
        return raw != null ? Number(raw) : 0;
      }
      const raw = row.need_kingdee_purchase_order != null
        ? row.need_kingdee_purchase_order
        : row.needKingdeePurchaseOrder;
      return raw != null ? Number(raw) : 0;
    },
    isKingdeeExecuted(value) {
      return Number(value) === 1;
    },
    formatKingdeeExecStatus(value) {
      return this.isKingdeeExecuted(value) ? "已执行" : "未执行";
    },
    hasAnyKingdeeExecuted(row) {
      if (!row) return false;
      return this.isKingdeeExecuted(this.getKingdeeField(row, "prd"))
        || this.isKingdeeExecuted(this.getKingdeeField(row, "po"));
    },
    getKingdeeDrawerTitle() {
      if (!this.productionObj) return "金蝶订单";
      const orderNo = this.productionObj.productionOrderNo || "";
      const projectName = this.productionObj.productionName || "";
      if (orderNo && projectName) return `金蝶订单 · ${orderNo} | ${projectName}`;
      if (orderNo) return `金蝶订单 · ${orderNo}`;
      return "金蝶订单";
    },
    openKingdeeDrawer(row, tab) {
      if (!row || !this.hasAnyKingdeeExecuted(row)) return;
      this.productionObj = row;
      this.kingdeeActiveTab = tab
        || (this.isKingdeeExecuted(this.getKingdeeField(row, "prd")) ? "prdMo" : "purchaseOrder");
      this.kingdeePrdMoList = [];
      this.kingdeePurchaseOrderList = [];
      this.kingdeeDrawer = true;
      this.$nextTick(() => {
        this.loadKingdeeDrawerTabData(this.kingdeeActiveTab);
      });
    },
    handleKingdeeChipClick(row, type, event) {
      if (event) event.stopPropagation();
      if (!this.isKingdeeExecuted(this.getKingdeeField(row, type === "prd" ? "prd" : "po"))) return;
      const tab = type === "prd" ? "prdMo" : "purchaseOrder";
      this.openKingdeeDrawer(row, tab);
    },
    loadKingdeeDrawerTabData(tabName) {
      if (!this.productionObj || !this.productionObj.id) return;
      if (tabName === "prdMo" && !this.kingdeePrdMoList.length && !this.kingdeePrdMoLoading) {
        this.fetchKingdeePrdMo();
      }
      if (tabName === "purchaseOrder" && !this.kingdeePurchaseOrderList.length && !this.kingdeePurchaseLoading) {
        this.fetchKingdeePurchaseOrder();
      }
    },
    handleKingdeeTabClick(tab) {
      this.loadKingdeeDrawerTabData(tab.name);
    },
    handleKingdeeDrawerClose(done) {
      this.kingdeeActiveTab = "prdMo";
      this.kingdeePrdMoList = [];
      this.kingdeePurchaseOrderList = [];
      done();
    },
    /** 解析金蝶接口 data 数组 */
    parseKingdeeListResponse(res) {
      if (Array.isArray(res)) return res;
      if (Array.isArray(res && res.data)) return res.data;
      return [];
    },
    formatKingdeeDate(value) {
      if (!value) return "—";
      const d = dayjs(value);
      return d.isValid() ? d.format("YYYY-MM-DD") : String(value);
    },
    formatPickMtrlStatus(value) {
      const map = {
        1: "未领料",
        2: "部分领料",
        3: "全部领料",
        4: "超额领料",
      };
      const key = value != null ? String(value) : "";
      return map[key] || value || "—";
    },
    getPickMtrlStatusTag(value) {
      const key = value != null ? String(value) : "";
      if (key === "3") return "success";
      if (key === "2") return "warning";
      if (key === "4") return "danger";
      return "info";
    },
    formatMrpCloseStatus(value) {
      const map = { A: "未关闭", B: "已关闭" };
      const key = value != null ? String(value).toUpperCase() : "";
      return map[key] || value || "—";
    },
    async fetchKingdeePrdMo() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.warning("请先选择项目");
        return;
      }
      this.kingdeePrdMoLoading = true;
      try {
        const res = await getKingdeePrdMo(this.productionObj.id);
        this.kingdeePrdMoList = this.parseKingdeeListResponse(res);
      } catch (e) {
        this.kingdeePrdMoList = [];
      } finally {
        this.kingdeePrdMoLoading = false;
      }
    },
    async fetchKingdeePurchaseOrder() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.warning("请先选择项目");
        return;
      }
      this.kingdeePurchaseLoading = true;
      try {
        const res = await getKingdeePurchaseOrder(this.productionObj.id);
        this.kingdeePurchaseOrderList = this.parseKingdeeListResponse(res);
      } catch (e) {
        this.kingdeePurchaseOrderList = [];
      } finally {
        this.kingdeePurchaseLoading = false;
      }
    },

    // 刷新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() {
      const pageSize = this.queryParams.pageSize;
      this.queryParams = {
        pageNum: 1,
        pageSize: pageSize || 10,
        productionOrderNo: undefined,
        productionName: undefined,
        drawingNo: undefined,
        drawingName: undefined,
        bomStatus: undefined,
        routePlanSendFilter: undefined,
        routeStatus: undefined,
        planEndTime: undefined,
        planStartTime: undefined,
        drawingType: undefined,
        projectEndTime: undefined,
        createBy: undefined,
        unit: undefined,
        quantity: undefined,
        isEnterpriseStandard: undefined,
        isEnterpriseStandardList: undefined,
        whetherDelay: undefined,
        source: undefined,
        needProcess: undefined,
        drawingPath: undefined,
        routeUptime: undefined,
        planUptime: undefined,
        bomUptime: undefined,
        drawingTime: undefined,
        bomCount: undefined,
        materialCount: undefined,
        routeCount: undefined,
        drawingBy: undefined,
      };
      this.handleQuery();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map((item) => item.id);
      this.single = selection.length !== 1;
      this.multiple = !selection.length;
    },
    /** 打开批量设置项目状态弹窗 */
    handleSetStatus() {
      if (!this.ids || !this.ids.length) {
        this.$modal.msgWarning("请先选择要设置状态的项目");
        return;
      }
      this.statusForm.bomStatus = undefined;
      this.statusDialogVisible = true;
      this.$nextTick(() => {
        if (this.$refs.statusForm) {
          this.$refs.statusForm.clearValidate();
        }
      });
    },
    /** 提交批量设置项目状态 */
    submitStatusForm() {
      this.$refs.statusForm.validate((valid) => {
        if (!valid) return;
        const bomStatus = Number(this.statusForm.bomStatus);
        if (Number.isNaN(bomStatus)) {
          this.$modal.msgWarning("项目状态无效");
          return;
        }
        const statusLabel =
          ((this.dict.type.process_status || []).find(
            (d) => String(d.value) === String(this.statusForm.bomStatus)
          ) || {}).label || this.statusForm.bomStatus;
        this.$modal
          .confirm(
            `确认将选中的 ${this.ids.length} 个项目状态设置为「${statusLabel}」？`
          )
          .then(() => {
            this.statusSubmitting = true;
            const tasks = this.ids.map((id) =>
              updateOrderPro({ id, bomStatus })
            );
            return Promise.all(tasks);
          })
          .then(() => {
            this.$modal.msgSuccess("状态设置成功");
            this.statusDialogVisible = false;
            this.getList();
            this.fetchOverviewStats();
          })
          .catch(() => {})
          .finally(() => {
            this.statusSubmitting = false;
          });
      });
    },
    /** 新增按钮操作 */
    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.isDownload =
          this.form.isDownload != null ? Number(this.form.isDownload) : 1;
        this.form.needProcess =
          this.form.needProcess != null ? Number(this.form.needProcess) : 1;
        this.form.source = this.form.source || "手动";
        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,
            isDownload:
              this.form.isDownload != null
                ? Number(this.form.isDownload)
                : 1,
            needProcess:
              this.form.needProcess != null
                ? Number(this.form.needProcess)
                : 1,
            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 || "",
          };
          delete payload.needKingdeePrdMo;
          delete payload.needKingdeePurchaseOrder;
          delete payload.need_kingdee_prd_mo;
          delete payload.need_kingdee_purchase_order;
          if (this.form.id != null) {
            updateOrderPro(payload)
              .then((response) => {
                this.$modal.msgSuccess("修改成功");
                this.open = false;
                this.getList();
                this.fetchOverviewStats();
              })
              .finally(() => {
                this.buttonLoading = false;
              });
          } else {
            addOrderPro({
              ...payload,
              need_kingdee_prd_mo: 0,
              need_kingdee_purchase_order: 0,
            })
              .then((response) => {
                this.$modal.msgSuccess("新增成功");
                this.open = false;
                this.getList();
                this.fetchOverviewStats();
              })
              .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.fetchOverviewStats();
          this.$modal.msgSuccess("删除成功");
        })
        .catch(() => { })
        .finally(() => {
          this.loading = false;
        });
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download(
        "system/orderPro/export",
        this.buildOrderProQueryParams(),
        `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();
        this.fetchOverviewStats();
      } 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 = "";
        this.percentage = 0;
        this.clearDrawProgressTimer();

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

          if (res.code !== 200) {
            this.drawLog = res.msg || "出图请求失败";
            this.drawLoading = false;
            return;
          }

          this.time = setInterval(async () => {
            try {
              const stateObj = this.parsePlotStateResponse(await getPlotState());
              if (!stateObj || stateObj.processstate === undefined) {
                return;
              }

              this.drawLog = stateObj.msg || "";
              const ps = this.normalizePlotProcessState(stateObj.processstate);

              if (ps === 3) {
                this.applyPlotProgressFromMsg(stateObj.msg);
              }

              // 完成 / 失败：兼容 processstate 为字符串 "4"/"5"
              if (ps === 4) {
                this.percentage = 100;
                this.clearDrawProgressTimer();
                this.drawLoading = false;
                this.fetchPlotStatus();
              } else if (ps === 5) {
                this.clearDrawProgressTimer();
                this.drawLoading = false;
                this.drawLog = stateObj.msg || "出图失败";
                this.fetchPlotStatus();
              }
            } catch (pollErr) {
              console.error("出图进度轮询失败:", pollErr);
            }
          }, 1000);
        } catch (e) {
          this.drawLog = "出图失败：" + (e.msg || e.message || e);
          this.drawLoading = false;
          this.clearDrawProgressTimer();
        }
      },
    // 加载销齿链型号表格数据
    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 [];
    },
    openExecImagePreview(row) {
      const list = this.getImageList(row);
      if (!list.length) {
        this.$message.warning("暂无执行单图");
        return;
      }
      this.execImagePreviewList = list;
      this.execImagePreviewTitle = `执行单图${row && row.productionOrderNo ? ` - ${row.productionOrderNo}` : ""}`;
      this.execImagePreviewVisible = true;
    },
    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}`);
    },
    async downloadPDF() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.error("请先选择生产令号");
        return;
      }

      const loading = this.$loading({
        text: "正在生成PDF，请稍候...",
        spinner: "el-icon-loading",
        background: "rgba(0, 0, 0, 0.7)",
      });
      try {
        const response = await uploadPDF(this.productionObj.id);
        if (!(response instanceof Blob)) {
          this.$message.error("PDF下载失败：服务器返回异常");
          return;
        }

        // 业务失败：后端返回 application/json，解析并展示真实原因
        const contentType = (response.type || "").toLowerCase();
        if (contentType.includes("application/json")) {
          const text = await response.text();
          try {
            const json = JSON.parse(text);
            this.$message.error(json.msg || "PDF下载失败");
          } catch (_) {
            this.$message.error(text || "PDF下载失败");
          }
          return;
        }

        if (response.size === 0) {
          this.$message.error("PDF下载失败：文件为空");
          return;
        }

        const blob = new Blob([response], { type: "application/zip" });
        const 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) {
        let msg = "PDF下载失败";
        try {
          const data = error && error.response && error.response.data;
          if (data instanceof Blob) {
            const text = await data.text();
            const json = JSON.parse(text);
            msg = json.msg || msg;
          } else if (error && error.message && !/status code|系统接口\d+异常/i.test(error.message)) {
            msg = error.message;
          }
        } catch (_) {
          // ignore parse error
        }
        this.$message.error(msg);
      } finally {
        loading.close();
      }
    },
    async downloadPlmDwgZip() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.error("请先选择生产令号");
        return;
      }
      if (Number(this.productionObj.isDownload) === 0) {
        this.$message.warning("当前项目不允许下载图纸");
        return;
      }
      this.downloadPlmDwgLoading = true;
      try {
        const response = await downloadPlmDwg(this.productionObj.id);
        if (!(response instanceof Blob)) {
          this.$message.error("下载失败：服务器返回异常");
          return;
        }
        const contentType = (response.type || "").toLowerCase();
        if (contentType.includes("application/json")) {
          const text = await response.text();
          try {
            const json = JSON.parse(text);
            this.$message.error(json.msg || "下载失败");
          } catch (_) {
            this.$message.error(text || "下载失败");
          }
          return;
        }
        if (response.size === 0) {
          this.$message.error("下载失败：文件为空");
          return;
        }
        const blob = new Blob([response], { type: "application/zip" });
        const url = window.URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = url;
        link.download = `${this.productionObj.productionOrderNo || "PLM"}_PLM_DWG.zip`;
        link.click();
        window.URL.revokeObjectURL(url);
        this.$message.success("PLM DWG图纸下载成功");
      } catch (error) {
        let msg = "下载失败";
        try {
          const data = error && error.response && error.response.data;
          if (data instanceof Blob) {
            const text = await data.text();
            const json = JSON.parse(text);
            msg = json.msg || msg;
          } else if (error && error.message && !/status code|系统接口\d+异常/i.test(error.message)) {
            msg = error.message;
          }
        } catch (_) {
          // ignore
        }
        this.$message.error(msg);
      } finally {
        this.downloadPlmDwgLoading = false;
      }
    },
    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 syncDrawingWorkload() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.warning("请先选择生产令号");
        return;
      }
      this.drawingWorkloadLoading = true;
      try {
        const res = await syncDrawingWorkloadApi(this.productionObj.id);
        this.drawingWorkload = (res && res.data) || null;
        this.drawingWorkloadVisible = true;
        this.$message.success((res && res.msg) || "图纸工作量读取成功");
      } catch (e) {
        /* 拦截器已提示 */
      } finally {
        this.drawingWorkloadLoading = false;
      }
    },
    async fetchRoutePlanSendCount() {
      if (!this.productionObj || !this.productionObj.id) {
        this.routePlanSendCount = 0;
        return;
      }
      try {
        const res = await getRoutePlanSendCount(this.productionObj.id);
        this.routePlanSendCount = this.parseRoutePlanSendCount(res);
        this.$set(this.routePlanSendCountMap, this.productionObj.id, this.routePlanSendCount);
      } catch (e) {
        this.routePlanSendCount = 0;
        this.$set(this.routePlanSendCountMap, this.productionObj.id, 0);
      }
    },
    async sendProcessRoutePlan() {
      if (!this.productionObj || !this.productionObj.id) {
        this.$message.warning("请先选择生产令号");
        return;
      }
      this.sendProcessRoutePlanLoading = true;
      try {
        const res = await sendProcessRoutePlanExcel(this.productionObj.id);
        this.$message.success((res && res.msg) || "工艺计划表已发送");
        await this.fetchRoutePlanSendCount();
      } catch (e) {
        /* 500 等已由 request 拦截器提示 */
      } finally {
        this.sendProcessRoutePlanLoading = false;
      }
    },
    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() {
    this.clearDrawProgressTimer();
  },
};
</script>

<style scoped>
/* ── Design tokens: industrial refined ── */
.order-pro-page {
  --op-bg: #f0f2f5;
  --op-surface: #ffffff;
  --op-ink: #1a2942;
  --op-ink-muted: #5c6b7f;
  --op-accent: #c9a227;
  --op-accent-soft: rgba(201, 162, 39, 0.12);
  --op-steel: #2c5282;
  --op-steel-dark: #1a2942;
  --op-border: #dce3ec;
  --op-danger: #c0392b;
  --op-danger-soft: #fdf2f0;
  --op-success: #1e7e4a;
  --op-success-soft: #edf7f1;
  --op-radius: 10px;
  --op-shadow: 0 1px 3px rgba(26, 41, 66, 0.06), 0 8px 24px rgba(26, 41, 66, 0.04);
  animation: opPageIn 0.45s ease-out both;
}

@keyframes opPageIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* ── KPI 概览卡片 ── */
.kpi-panel {
  display: grid;
  grid-template-columns: repeat(4, minmax(120px, 1fr));
  gap: 12px;
  margin-bottom: 16px;
  animation: opPageIn 0.45s ease-out both;
}

.kpi-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 18px;
  border-radius: var(--op-radius);
  border: 1px solid var(--op-border);
  background: var(--op-surface);
  box-shadow: var(--op-shadow);
  transition: box-shadow 0.2s, border-color 0.2s;
}

.kpi-card:hover {
  box-shadow: 0 4px 16px rgba(26, 41, 66, 0.08);
  border-color: #c5d0de;
}

.kpi-card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  font-size: 20px;
  flex-shrink: 0;
}

.kpi-card__body {
  min-width: 0;
}

.kpi-card__value {
  display: block;
  font-size: 24px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
  color: var(--op-ink);
}

.kpi-card__label {
  display: block;
  margin-top: 4px;
  font-size: 12px;
  color: var(--op-ink-muted);
  white-space: nowrap;
}

.kpi-card--total .kpi-card__icon {
  color: var(--op-steel);
  background: #e8f0fa;
}

.kpi-card--delay .kpi-card__icon {
  color: var(--op-danger);
  background: var(--op-danger-soft);
}

.kpi-card--delay .kpi-card__value {
  color: var(--op-danger);
}

.kpi-card--change .kpi-card__icon {
  color: #7c3aed;
  background: #f3e8ff;
}

.kpi-card--change .kpi-card__value {
  color: #7c3aed;
}

.kpi-card--production .kpi-card__icon {
  color: #b45309;
  background: #fef3e2;
}

.kpi-card--production .kpi-card__value {
  color: #b45309;
}

@media (max-width: 1200px) {
  .kpi-panel {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* ── Search panel ── */
.search-panel {
  margin-bottom: 16px;
  border: 1px solid var(--op-border);
  border-radius: var(--op-radius);
  box-shadow: var(--op-shadow);
  animation: opPageIn 0.5s ease-out 0.05s both;
}

.search-panel ::v-deep .el-card__body {
  padding: 14px 20px;
  overflow: visible;
}

.search-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.search-form__main {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)) 320px;
  gap: 12px 18px;
  align-items: center;
}

.search-form__advanced {
  padding: 14px 16px 16px;
  border: 1px solid #e8edf5;
  border-radius: 10px;
  background: linear-gradient(180deg, #fbfcff 0%, #f7faff 100%);
}

.search-form__advanced-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.search-form__advanced-title {
  font-size: 13px;
  font-weight: 600;
  color: #2f3a4a;
}

.search-form__advanced-tip {
  font-size: 12px;
  color: #8b95a7;
}

.search-form__advanced-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px 18px;
}

.search-field {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  align-items: center;
  align-items: center;
  min-height: 32px;
}

.search-field__label {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  width: 100%;
  min-height: 32px;
  padding-right: 8px;
  color: #606266;
  font-size: 13px;
  line-height: 32px;
  white-space: nowrap;
  text-align: right;
  flex-shrink: 0;
}

.search-field__label--wide {
  width: 100%;
}

.search-field ::v-deep .el-input,
.search-field ::v-deep .el-select {
  width: 100%;
}

.search-field--type ::v-deep .el-select {
  width: 100%;
}

.search-field ::v-deep .el-input__inner,
.search-field ::v-deep .el-select .el-input__inner {
  height: 32px;
  line-height: 32px;
  border-radius: 6px;
}

.search-field ::v-deep .el-input__icon {
  line-height: 32px;
}

.search-field--actions {
  min-height: 32px;
}

.search-form__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 8px;
  min-height: 32px;
}

.search-form__actions ::v-deep .el-button {
  margin-left: 0;
  height: 32px;
  padding: 0 14px;
  line-height: 30px;
}

.search-form__actions ::v-deep .el-button--primary {
  background: linear-gradient(135deg, var(--op-steel) 0%, #3a6ea5 100%);
  border-color: var(--op-steel);
}

.search-form__toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 32px;
  margin: 0;
  padding: 0 4px;
  border: 0;
  background: transparent;
  color: var(--op-steel);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}

.search-form__toggle:hover {
  color: #3a6ea5;
}

.search-form__count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 8px;
  background: var(--op-steel);
  color: #fff;
  font-size: 11px;
  font-style: normal;
  line-height: 1;
}

@media (min-width: 1280px) {
  .search-field--type {
    grid-column: span 2;
  }
}

@media (max-width: 1440px) {
  .search-form__main {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .search-form__actions {
    grid-column: span 2;
    justify-content: flex-end;
  }

  .search-form__advanced-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 1100px) {
  .search-form__main {
    grid-template-columns: 1fr;
  }

  .search-form__actions {
    grid-column: auto;
    justify-content: flex-start;
  }

  .search-field {
    grid-template-columns: 82px minmax(0, 1fr);
  }

  .search-form__advanced-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .search-field--type {
    grid-column: auto;
  }
}

@media (max-width: 768px) {
  .search-form__advanced {
    padding: 12px;
  }

  .search-form__advanced-head {
    align-items: flex-start;
    flex-direction: column;
    margin-bottom: 10px;
  }

  .search-form__advanced-grid {
    grid-template-columns: 1fr;
  }

  .search-form__actions {
    justify-content: flex-start;
  }
}

/* ── Toolbar ── */
.toolbar-panel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
  flex-wrap: wrap;
  animation: opPageIn 0.5s ease-out 0.1s both;
}

.toolbar-panel__left {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.toolbar-panel__left >>> .el-button {
  border-radius: 6px;
  font-weight: 500;
  transition: box-shadow 0.15s;
}

.toolbar-panel__left >>> .el-button:hover:not(:disabled) {
  box-shadow: 0 3px 8px rgba(26, 41, 66, 0.1);
}

/* ── Table panel ── */
.table-panel {
  border: 1px solid var(--op-border);
  border-radius: var(--op-radius);
  box-shadow: var(--op-shadow);
  animation: opPageIn 0.5s ease-out 0.15s both;
}

.table-panel >>> .el-card__body {
  padding: 0;
  overflow-x: auto;
}

.table-panel__footer {
  padding: 12px 16px;
  border-top: 1px solid var(--op-border);
  background: #fafbfc;
}

.order-pro-table >>> .el-table__body tr:hover > td {
  background-color: #f0f5fa !important;
}

.order-pro-table >>> .order-row--delayed > td {
  background-color: var(--op-danger-soft) !important;
}

.order-pro-table >>> .order-row--delayed:hover > td {
  background-color: #fce8e4 !important;
}

.order-no-link {
  font-weight: 600;
  font-family: "Consolas", "Monaco", "Courier New", monospace;
  font-size: 13px;
}

.order-no-link i {
  margin-right: 4px;
  font-size: 12px;
  opacity: 0.7;
}

.project-name-cell {
  color: var(--op-ink);
  font-weight: 500;
}

.exec-image-entry {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.exec-image-trigger {
  padding: 0;
}

.exec-image-count {
  font-size: 12px;
  color: #606266;
  white-space: nowrap;
}

.exec-image-group {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: center;
}

.exec-image-thumb {
  width: 72px;
  height: 72px;
  border-radius: 6px;
  border: 1px solid var(--op-border);
  transition: transform 0.2s, box-shadow 0.2s;
  cursor: zoom-in;
}

.exec-image-thumb:hover {
  transform: scale(1.05);
  box-shadow: 0 4px 12px rgba(26, 41, 66, 0.15);
}

.exec-image-dialog__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
  max-height: 62vh;
  overflow-y: auto;
}

.exec-image-dialog__thumb {
  width: 100%;
  height: 180px;
  border-radius: 8px;
  border: 1px solid var(--op-border);
  background: #f8fafc;
  cursor: zoom-in;
}

.drawing-workload__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 20px;
  margin-bottom: 12px;
  padding: 10px 12px;
  background: #f5f7fa;
  border-radius: 6px;
  color: #606266;
  font-size: 13px;
}

.drawing-workload__meta b {
  color: #303133;
}

.text-muted {
  color: #909399;
  font-size: 12px;
}

.status-pill {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.status-pill--danger {
  background: var(--op-danger-soft);
  color: var(--op-danger);
  border: 1px solid rgba(192, 57, 43, 0.25);
}

.status-pill--success {
  background: var(--op-success-soft);
  color: var(--op-success);
  border: 1px solid rgba(30, 126, 74, 0.25);
}

.status-pill--warning {
  background: rgba(217, 148, 42, 0.12);
  color: #b87a12;
  border: 1px solid rgba(217, 148, 42, 0.3);
}

.status-pill--muted {
  background: rgba(120, 130, 140, 0.1);
  color: var(--op-ink-muted);
  border: 1px solid rgba(120, 130, 140, 0.2);
}

.delay-days {
  font-variant-numeric: tabular-nums;
  color: var(--op-ink-muted);
  font-weight: 500;
}

.delay-days--active {
  color: var(--op-danger);
  font-weight: 700;
}

.date-cell {
  font-size: 12px;
  color: var(--op-ink-muted);
  font-variant-numeric: tabular-nums;
}

.type-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  border: 1px solid transparent;
}

.type-badge--product { background: #e8f4fd; color: #1a6fb5; border-color: #b3d9f2; }
.type-badge--custom { background: #fef3e2; color: #b45309; border-color: #fcd9a8; }
.type-badge--change { background: #f3e8ff; color: #7c3aed; border-color: #ddd6fe; }
.type-badge--rd { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.type-badge--standard { background: #f1f5f9; color: #475569; border-color: #cbd5e1; }
.type-badge--stock { background: #fff7ed; color: #c2410c; border-color: #fed7aa; }
.type-badge--asset { background: #f0fdf4; color: #15803d; border-color: #bbf7d0; }
.type-badge--exhibit { background: #fdf4ff; color: #a21caf; border-color: #f5d0fe; }
.type-badge--default { background: #f4f6f9; color: #5c6b7f; border-color: #dce3ec; }

.source-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  border: 1px solid transparent;
}

.source-badge--plm {
  color: #6d28d9;
  background: #f3e8ff;
  border-color: #ddd6fe;
}

.source-badge--manual {
  color: #475569;
  background: #f1f5f9;
  border-color: #cbd5e1;
}

.send-count-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: #909399;
  background: #f4f4f5;
  border: 1px solid #e4e7ed;
}

.send-count-badge--sent {
  color: #2c5282;
  background: #e8f0fa;
  border-color: #b3cce8;
}

.action-dropdown >>> .el-button-group .el-button {
  border-radius: 6px 0 0 6px;
}

/* ── Drawer ── */
.drawer-body {
  flex: 1;
  min-height: 0;
  padding: 12px 24px 32px;
  overflow-x: auto;
  overflow-y: auto;
}

.drawer-body--bom {
  padding: 0;
  overflow: hidden;
}

.bom-detail-drawer .el-drawer__body {
  display: flex;
  flex-direction: column;
}

.drawer-main-tabs >>> .el-tabs__header {
  margin-bottom: 16px;
}

.drawer-main-tabs >>> .el-tabs__item {
  font-weight: 500;
}

.kingdee-tab-toolbar {
  margin-bottom: 12px;
}

/* ── 金蝶待建列表列 ── */
.kingdee-need-group {
  display: inline-flex;
  flex-direction: column;
  gap: 4px;
  padding: 2px 0;
}

.kingdee-need-group--clickable {
  cursor: pointer;
}

.kingdee-need-group--clickable:hover .kingdee-need-tag--active {
  transform: translateY(-1px);
  box-shadow: 0 4px 10px rgba(44, 82, 130, 0.18);
}

.kingdee-need-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 6px;
  font-size: 11px;
  line-height: 1.4;
  border: 1px solid transparent;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
  user-select: none;
}

.kingdee-need-tag i {
  font-size: 12px;
  opacity: 0.85;
}

.kingdee-need-tag em {
  font-style: normal;
  font-weight: 500;
}

.kingdee-need-tag b {
  font-size: 10px;
  font-weight: 600;
  min-width: 38px;
  text-align: center;
  padding: 1px 5px;
  border-radius: 4px;
  font-style: normal;
}

.kingdee-need-tag--mo.kingdee-need-tag--active {
  color: #1a6fb5;
  background: linear-gradient(135deg, #e8f4fd 0%, #dbeafe 100%);
  border-color: #93c5fd;
}

.kingdee-need-tag--mo.kingdee-need-tag--active b {
  color: #fff;
  background: #2563eb;
}

.kingdee-need-tag--po.kingdee-need-tag--active {
  color: #b45309;
  background: linear-gradient(135deg, #fef3e2 0%, #fde68a 100%);
  border-color: #fcd34d;
}

.kingdee-need-tag--po.kingdee-need-tag--active b {
  color: #fff;
  background: #d97706;
}

.kingdee-need-tag--idle {
  color: #94a3b8;
  background: #f8fafc;
  border-color: #e2e8f0;
}

.kingdee-need-tag--idle b {
  color: #64748b;
  background: #e2e8f0;
}

.kingdee-need-tag--active {
  cursor: pointer;
}

/* ── 金蝶订单独立抽屉 ── */
.kingdee-order-drawer >>> .el-drawer__header {
  margin-bottom: 0;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--op-border);
}

.kingdee-drawer-body {
  padding: 0 4px 24px;
}

.kingdee-drawer-summary {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: 16px;
  align-items: center;
  margin-bottom: 18px;
  padding: 14px 18px;
  border-radius: var(--op-radius);
  background: linear-gradient(135deg, #f8fafc 0%, #eef2f7 100%);
  border: 1px solid var(--op-border);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
}

.kingdee-drawer-summary__label {
  display: block;
  font-size: 11px;
  color: var(--op-ink-muted);
  margin-bottom: 4px;
  letter-spacing: 0.04em;
}

.kingdee-drawer-summary__value {
  font-size: 14px;
  font-weight: 600;
  color: var(--op-ink);
  word-break: break-all;
}

.kingdee-drawer-summary__flags {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 148px;
}

.kingdee-drawer-flag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  border: 1px solid transparent;
}

.kingdee-drawer-flag--done {
  color: #1e7e4a;
  background: var(--op-success-soft);
  border-color: rgba(30, 126, 74, 0.25);
}

.kingdee-drawer-flag--idle {
  color: #94a3b8;
  background: #f8fafc;
  border-color: #e2e8f0;
}

.kingdee-order-tabs >>> .el-tabs__header {
  margin-bottom: 14px;
}

.kingdee-order-tabs >>> .el-tabs__item {
  font-weight: 600;
  color: var(--op-ink-muted);
}

.kingdee-order-tabs >>> .el-tabs__item.is-active {
  color: var(--op-steel);
}

.kingdee-tab-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.kingdee-tab-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #d97706;
  box-shadow: 0 0 0 2px rgba(217, 119, 6, 0.25);
  animation: kingdeeDotPulse 1.8s ease-in-out infinite;
}

@keyframes kingdeeDotPulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.65; transform: scale(0.85); }
}

.kingdee-order-table {
  width: 100%;
  border-radius: 8px;
  overflow: hidden;
}

.kingdee-order-table >>> .el-table__header th {
  background: linear-gradient(180deg, #f5f7fa 0%, #eef2f6 100%) !important;
  color: var(--op-ink);
  font-weight: 600;
}

@media (max-width: 960px) {
  .kingdee-drawer-summary {
    grid-template-columns: 1fr;
  }
}

.drawer-section-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 16px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 20px;
}

.drawer-section-title--block {
  display: flex;
  margin-bottom: 16px;
}

.drawer-section-title--green {
  color: #1e7e4a;
  background: var(--op-success-soft);
  border: 1px solid rgba(30, 126, 74, 0.2);
}

.drawer-section-title--blue {
  color: var(--op-steel);
  background: #e8f0fa;
  border: 1px solid rgba(44, 82, 130, 0.2);
}

.drawer-divider {
  margin: 24px 0 16px;
}

.drawer-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

.drawer-toolbar--wrap {
  margin-bottom: 10px;
}

.drawer-action-block {
  margin-bottom: 20px;
}

.draw-progress {
  margin: 12px 0;
}

.draw-log-card {
  background: #1a2942;
  border: none;
  border-radius: 8px;
  min-height: 120px;
  max-height: 220px;
  overflow-y: auto;
}

.draw-log-card >>> .el-card__body {
  padding: 14px 16px;
}

.draw-log-content {
  font-family: "Consolas", "Monaco", "Courier New", monospace;
  font-size: 12px;
  line-height: 1.6;
  color: #a8d4a0;
  white-space: pre-line;
}

/* 图纸类型下拉选项样式 */
.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 #eee;
}

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

.chain-name {
  font-size: 14px;
  color: var(--op-steel, #2c5282);
  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: #2c7a7b;
  font-weight: 500;
}

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

.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>
