<template>
  <div class="model-page">
    <div class="top-section">
      <div class="model-card">
        <div class="section-header">
          <span>双机架冷轧深度模型</span>
          <el-tag size="mini" effect="plain">{{ deepModel.version }}</el-tag>
        </div>
        <div class="theory-grid">
          <div
            v-for="item in theoryItems"
            :key="item.title"
            class="theory-item"
          >
            <div class="theory-title">{{ item.title }}</div>
            <div class="theory-formula">{{ item.formula }}</div>
            <div class="theory-desc">{{ item.desc }}</div>
          </div>
        </div>
      </div>

      <div class="summary-card">
        <div class="section-header">
          <span>预测结论</span>
          <el-button
            size="mini"
            type="primary"
            icon="el-icon-cpu"
            @click="runPrediction"
          >深度推理</el-button>
        </div>
        <div class="summary-grid">
          <div class="summary-cell">
            <span>最终厚度预测</span>
            <b>{{ formatNumber(summary.finalThickness, 4) }}</b>
            <em>mm</em>
          </div>
          <div class="summary-cell">
            <span>平均置信度</span>
            <b>{{ formatNumber(summary.avgConfidence, 2) }}</b>
            <em>%</em>
          </div>
          <div class="summary-cell">
            <span>总轧制力</span>
            <b>{{ formatNumber(summary.totalForce, 1) }}</b>
            <em>kN</em>
          </div>
          <div class="summary-cell">
            <span>总功率</span>
            <b>{{ formatNumber(summary.totalPower, 1) }}</b>
            <em>kW</em>
          </div>
        </div>
        <div class="risk-line">
          <span>综合风险</span>
          <el-tag
            size="mini"
            :type="riskTagType(summary.riskLevel)"
          >{{ summary.riskLevel || '未计算' }}</el-tag>
          <span class="risk-text">{{ summary.riskText }}</span>
        </div>
      </div>
    </div>

    <div class="line-section">
      <div class="section-header">
        <span>双机架产线动态图</span>
        <div class="header-actions">
          <el-switch
            v-model="lineRunning"
            size="mini"
            active-text="运行"
            inactive-text="暂停"
          />
          <el-button
            size="mini"
            icon="el-icon-refresh-right"
            @click="syncLineFromEquipment"
          >同步设备</el-button>
        </div>
      </div>
      <div
        class="line-canvas"
        :class="{ 'line-running': lineRunning }"
      >
        <div class="line-strip">
          <span class="strip-core" />
        </div>
        <button
          v-for="node in lineNodes"
          :key="node.key"
          type="button"
          :class="lineNodeClass(node)"
          :style="{ left: node.left }"
          @click="selectLineNode(node)"
        >
          <span class="node-icon">{{ node.icon }}</span>
          <b>{{ node.name }}</b>
          <em>{{ node.metric }}</em>
        </button>
      </div>
      <div class="line-info">
        <span>当前节点：<b>{{ activeLineNodeLabel }}</b></span>
        <span>带钢速度：<b>{{ formatNumber(lineSpeed, 1) }}</b> m/min</span>
        <span>F1/F2 压下：<b>{{ formatNumber(resultRows[0] && resultRows[0].reductionRate, 2) }}</b>% /
          <b>{{ formatNumber(resultRows[1] && resultRows[1].reductionRate, 2) }}</b>%</span>
        <span>设备健康：<b>{{ selectedEquipment ? selectedEquipment.healthScore : '--' }}</b></span>
      </div>
    </div>

    <div class="middle-section">
      <div class="input-card">
        <div class="section-header">
          <span>计划与特征输入</span>
          <div class="header-actions">
            <el-button
              size="mini"
              icon="el-icon-refresh"
              @click="loadPlanList"
            >刷新计划</el-button>
            <el-button
              size="mini"
              icon="el-icon-delete"
              @click="resetAll"
            >清空</el-button>
          </div>
        </div>

        <el-form
          size="mini"
          label-width="92px"
          class="compact-form"
        >
          <el-form-item label="生产计划">
            <el-select
              v-model="selectedPlanId"
              filterable
              clearable
              placeholder="选择轧制队列计划"
              style="width: 100%"
              @change="handlePlanChange"
            >
              <el-option
                v-for="plan in planOptions"
                :key="plan.planId"
                :label="planOptionLabel(plan)"
                :value="plan.planId"
              />
            </el-select>
          </el-form-item>
          <el-row :gutter="10">
            <el-col :span="12">
              <el-form-item label="计划号">
                <el-input v-model="coil.planNo" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="钢卷号">
                <el-input v-model="coil.coilNo" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="合金牌号">
                <el-input v-model="coil.alloyNo" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="合金编码">
                <el-input-number
                  v-model="coil.alloyCode"
                  :min="0"
                  :precision="2"
                  :controls="false"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="入口厚度">
                <el-input-number
                  v-model="coil.entryThickness"
                  :min="0"
                  :precision="4"
                  :controls="false"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="目标厚度">
                <el-input-number
                  v-model="coil.targetThickness"
                  :min="0"
                  :precision="4"
                  :controls="false"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="宽度">
                <el-input-number
                  v-model="coil.width"
                  :min="0"
                  :precision="2"
                  :controls="false"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="来料重量">
                <el-input-number
                  v-model="coil.weight"
                  :min="0"
                  :precision="2"
                  :controls="false"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>

        <div class="section-subtitle">深度模型在线适配参数</div>
        <el-form
          size="mini"
          label-width="116px"
          class="compact-form"
        >
          <el-row :gutter="10">
            <el-col :span="12">
              <el-form-item label="F1力缩放">
                <el-input-number
                  v-model="model.forceScaleF1"
                  :min="0.2"
                  :precision="4"
                  :step="0.01"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="F2力缩放">
                <el-input-number
                  v-model="model.forceScaleF2"
                  :min="0.2"
                  :precision="4"
                  :step="0.01"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="F1厚度偏置">
                <el-input-number
                  v-model="model.thicknessBiasF1"
                  :precision="4"
                  :step="0.001"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="F2厚度偏置">
                <el-input-number
                  v-model="model.thicknessBiasF2"
                  :precision="4"
                  :step="0.001"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="在线学习率">
                <el-input-number
                  v-model="model.learningRate"
                  :min="0"
                  :max="1"
                  :precision="2"
                  :step="0.05"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="置信度下限">
                <el-input-number
                  v-model="model.confidenceFloor"
                  :min="0"
                  :max="1"
                  :precision="2"
                  :step="0.05"
                  style="width: 100%"
                />
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>

      <div class="stand-card">
        <div class="section-header">
          <span>双机架深度推理</span>
          <div class="header-actions">
            <el-button
              size="mini"
              icon="el-icon-magic-stick"
              @click="distributeReduction"
            >分配压下</el-button>
            <el-button
              size="mini"
              type="primary"
              icon="el-icon-cpu"
              @click="runPrediction"
            >计算</el-button>
          </div>
        </div>

        <el-table
          :data="resultRows"
          border
          size="mini"
          class="stand-table"
          height="318"
          :row-class-name="standRowClass"
        >
          <el-table-column
            label="机架"
            prop="standName"
            width="58"
            align="center"
            fixed
          />
          <el-table-column
            label="入口厚"
            width="104"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.entryThickness"
                :min="0"
                :precision="4"
                :controls="false"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="目标厚"
            width="104"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.targetThickness"
                :min="0"
                :precision="4"
                :controls="false"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="宽度"
            width="98"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.width"
                :min="0"
                :precision="2"
                :controls="false"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="辊径"
            width="98"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.rollDiameter"
                :min="1"
                :precision="1"
                :controls="false"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="摩擦"
            width="86"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.friction"
                :min="0.001"
                :precision="4"
                :step="0.005"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="入口张力"
            width="106"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.entryTension"
                :min="0"
                :precision="2"
                :controls="false"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="出口张力"
            width="106"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.exitTension"
                :min="0"
                :precision="2"
                :controls="false"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="速度"
            width="96"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.speed"
                :min="0"
                :precision="1"
                :controls="false"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="设定力"
            width="98"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.setForce"
                :min="0"
                :precision="1"
                :controls="false"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="弯辊力"
            width="98"
          >
            <template slot-scope="{ row }">
              <el-input-number
                v-model="row.source.bendForce"
                :precision="1"
                :controls="false"
                size="mini"
              />
            </template>
          </el-table-column>
          <el-table-column
            label="预测轧制力"
            width="108"
            align="right"
          >
            <template slot-scope="{ row }">
              <span class="calc-val">{{ formatNumber(row.force, 1) }}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="预测出口厚"
            width="108"
            align="right"
          >
            <template slot-scope="{ row }">
              <span class="calc-val">{{ formatNumber(row.predictedExit, 4) }}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="置信度"
            width="86"
            align="right"
          >
            <template slot-scope="{ row }">
              <span :class="confidenceClass(row.confidence)">{{ formatPercent(row.confidence) }}</span>
            </template>
          </el-table-column>
          <el-table-column
            label="风险"
            min-width="160"
          >
            <template slot-scope="{ row }">
              <el-tag
                size="mini"
                :type="riskTagType(row.riskLevel)"
              >{{ row.riskLevel }}</el-tag>
              <span class="risk-cell">{{ row.riskText }}</span>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </div>

    <div class="bottom-section">
      <div class="actual-card">
        <div class="section-header">
          <span>实绩对比与在线校正</span>
          <div class="header-actions">
            <el-button
              size="mini"
              icon="el-icon-search"
              @click="loadActualList"
            >加载实绩</el-button>
            <el-button
              size="mini"
              type="warning"
              icon="el-icon-set-up"
              :disabled="!correction.canApply"
              @click="applyActualCorrection"
            >更新适配层</el-button>
            <el-button
              size="mini"
              type="primary"
              icon="el-icon-edit-outline"
              @click="openTuningDialog(1)"
            >录入微调</el-button>
          </div>
        </div>

        <div class="actual-body">
          <div class="actual-list">
            <el-table
              :data="actualList"
              border
              size="mini"
              height="224"
              highlight-current-row
              @current-change="handleActualSelect"
            >
              <el-table-column
                label="成品卷号"
                prop="exitMatId"
                width="116"
              />
              <el-table-column
                label="来料卷号"
                prop="entryMatId"
                width="116"
              />
              <el-table-column
                label="计划号"
                prop="planNo"
                width="105"
              />
              <el-table-column
                label="成品厚度"
                prop="exitThickness"
                width="92"
                align="right"
              />
              <el-table-column
                label="成品宽度"
                prop="exitWidth"
                width="92"
                align="right"
              />
              <el-table-column
                label="实际重量"
                prop="actualWeight"
                min-width="88"
                align="right"
              />
            </el-table>
          </div>

          <div class="actual-edit">
            <el-table
              :data="resultRows"
              border
              size="mini"
              height="224"
              class="actual-table"
            >
              <el-table-column
                label="机架"
                prop="standName"
                width="58"
                align="center"
              />
              <el-table-column
                label="实绩力"
                width="112"
              >
                <template slot-scope="{ row }">
                  <el-input-number
                    v-model="row.source.actualForce"
                    :min="0"
                    :precision="1"
                    :controls="false"
                    size="mini"
                  />
                </template>
              </el-table-column>
              <el-table-column
                label="实绩出口厚"
                width="124"
              >
                <template slot-scope="{ row }">
                  <el-input-number
                    v-model="row.source.actualExitThickness"
                    :min="0"
                    :precision="4"
                    :controls="false"
                    size="mini"
                  />
                </template>
              </el-table-column>
              <el-table-column
                label="力误差"
                width="86"
                align="right"
              >
                <template slot-scope="{ row }">
                  <span :class="errorClass(row.forceError)">{{ formatSigned(row.forceError, 1) }}</span>
                </template>
              </el-table-column>
              <el-table-column
                label="厚差"
                width="82"
                align="right"
              >
                <template slot-scope="{ row }">
                  <span :class="errorClass(row.thicknessError)">{{ formatSigned(row.thicknessError, 4) }}</span>
                </template>
              </el-table-column>
              <el-table-column
                label="建议力缩放"
                min-width="96"
                align="right"
              >
                <template slot-scope="{ row }">
                  <span class="calc-val">{{ formatNumber(row.forceScaleSuggested, 4) }}</span>
                </template>
              </el-table-column>
              <el-table-column
                label="操作"
                width="72"
                align="center"
              >
                <template slot-scope="{ row }">
                  <el-button
                    size="mini"
                    type="text"
                    @click="openTuningDialog(row.source.standNo)"
                  >微调</el-button>
                </template>
              </el-table-column>
            </el-table>
          </div>
        </div>

        <div class="correction-line">
          <span>适配建议</span>
          <span>F1力缩放：<b>{{ formatNumber(correction.forceScaleF1, 4) }}</b></span>
          <span>F2力缩放：<b>{{ formatNumber(correction.forceScaleF2, 4) }}</b></span>
          <span>厚度偏置：<b>{{ formatSigned(correction.avgThicknessBias, 4) }}</b> mm</span>
          <span>样本数：<b>{{ correction.sampleCount }}</b></span>
        </div>
      </div>

      <div class="detail-card">
        <div class="section-header">
          <span>深度模型诊断</span>
        </div>
        <el-table
          :data="resultRows"
          border
          size="mini"
          height="262"
          class="detail-table"
        >
          <el-table-column
            label="机架"
            prop="standName"
            width="58"
            align="center"
          />
          <el-table-column
            label="压下率"
            width="74"
            align="right"
          >
            <template slot-scope="{ row }">{{ formatNumber(row.reductionRate, 2) }}%</template>
          </el-table-column>
          <el-table-column
            label="功率"
            width="82"
            align="right"
          >
            <template slot-scope="{ row }">{{ formatNumber(row.power, 1) }}</template>
          </el-table-column>
          <el-table-column
            label="板形分"
            width="76"
            align="right"
          >
            <template slot-scope="{ row }">{{ formatNumber(row.flatnessScore, 1) }}</template>
          </el-table-column>
          <el-table-column
            label="越界惩罚"
            width="82"
            align="right"
          >
            <template slot-scope="{ row }">{{ formatNumber(row.extrapolationPenalty, 2) }}</template>
          </el-table-column>
          <el-table-column
            label="主要特征"
            min-width="170"
          >
            <template slot-scope="{ row }">
              <span class="feature-text">{{ row.topFeatures }}</span>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </div>

    <div class="equipment-section">
      <div class="section-header">
        <span>设备管理与模型反哺</span>
        <div class="header-actions">
          <el-button
            size="mini"
            icon="el-icon-plus"
            @click="openEquipmentDialog()"
          >新增设备</el-button>
          <el-button
            size="mini"
            icon="el-icon-edit"
            :disabled="!selectedEquipment"
            @click="openEquipmentDialog(selectedEquipment)"
          >维护</el-button>
          <el-button
            size="mini"
            type="primary"
            icon="el-icon-connection"
            :disabled="!selectedEquipment"
            @click="applyEquipmentToModel"
          >反哺模型</el-button>
        </div>
      </div>
      <el-table
        :data="equipmentList"
        border
        size="mini"
        height="286"
        highlight-current-row
        class="equipment-table"
        @current-change="handleEquipmentSelect"
      >
        <el-table-column
          label="设备位号"
          prop="equipNo"
          width="110"
          fixed
        />
        <el-table-column
          label="设备名称"
          prop="equipName"
          width="130"
        />
        <el-table-column
          label="区域"
          prop="area"
          width="86"
          align="center"
        />
        <el-table-column
          label="类型"
          prop="type"
          width="96"
          align="center"
        />
        <el-table-column
          label="状态"
          prop="status"
          width="82"
          align="center"
        >
          <template slot-scope="{ row }">
            <el-tag
              size="mini"
              :type="equipmentStatusType(row.status)"
            >{{ row.status }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column
          label="健康分"
          prop="healthScore"
          width="78"
          align="right"
        />
        <el-table-column
          label="采集值"
          prop="processValue"
          width="86"
          align="right"
        />
        <el-table-column
          label="单位"
          prop="unit"
          width="62"
          align="center"
        />
        <el-table-column
          label="下限"
          prop="lowerLimit"
          width="76"
          align="right"
        />
        <el-table-column
          label="上限"
          prop="upperLimit"
          width="76"
          align="right"
        />
        <el-table-column
          label="模型特征"
          prop="modelFeature"
          width="120"
        />
        <el-table-column
          label="影响系数"
          prop="impactWeight"
          width="86"
          align="right"
        />
        <el-table-column
          label="最近维护"
          prop="lastMaintainTime"
          width="116"
          align="center"
        />
        <el-table-column
          label="负责人"
          prop="owner"
          width="82"
          align="center"
        />
        <el-table-column
          label="备注"
          prop="remark"
          min-width="180"
        />
      </el-table>
    </div>

    <el-dialog
      :title="equipmentDialogTitle"
      :visible.sync="equipmentDialogVisible"
      width="760px"
      append-to-body
    >
      <el-form
        ref="equipmentFormRef"
        :model="equipmentForm"
        :rules="equipmentRules"
        size="mini"
        label-width="92px"
      >
        <el-row :gutter="12">
          <el-col :span="8">
            <el-form-item
              label="设备位号"
              prop="equipNo"
            >
              <el-input v-model="equipmentForm.equipNo" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item
              label="设备名称"
              prop="equipName"
            >
              <el-input v-model="equipmentForm.equipName" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="区域">
              <el-select
                v-model="equipmentForm.area"
                style="width: 100%"
              >
                <el-option
                  v-for="item in areaOptions"
                  :key="item"
                  :label="item"
                  :value="item"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="设备类型">
              <el-select
                v-model="equipmentForm.type"
                style="width: 100%"
              >
                <el-option label="轧机主机" value="轧机主机" />
                <el-option label="张力系统" value="张力系统" />
                <el-option label="传动系统" value="传动系统" />
                <el-option label="液压系统" value="液压系统" />
                <el-option label="测厚仪" value="测厚仪" />
                <el-option label="板形仪" value="板形仪" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="状态">
              <el-select
                v-model="equipmentForm.status"
                style="width: 100%"
              >
                <el-option label="运行" value="运行" />
                <el-option label="关注" value="关注" />
                <el-option label="检修" value="检修" />
                <el-option label="停用" value="停用" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="健康分">
              <el-input-number
                v-model="equipmentForm.healthScore"
                :min="0"
                :max="100"
                :precision="0"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="采集值">
              <el-input-number
                v-model="equipmentForm.processValue"
                :precision="3"
                :controls="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="单位">
              <el-input v-model="equipmentForm.unit" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="模型特征">
              <el-select
                v-model="equipmentForm.modelFeature"
                style="width: 100%"
              >
                <el-option label="F1辊径" value="f1RollDiameter" />
                <el-option label="F2辊径" value="f2RollDiameter" />
                <el-option label="F1张力" value="f1Tension" />
                <el-option label="F2张力" value="f2Tension" />
                <el-option label="速度" value="speed" />
                <el-option label="板形偏置" value="flatnessBias" />
                <el-option label="功率缩放" value="powerScale" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="下限">
              <el-input-number
                v-model="equipmentForm.lowerLimit"
                :precision="3"
                :controls="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="上限">
              <el-input-number
                v-model="equipmentForm.upperLimit"
                :precision="3"
                :controls="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="影响系数">
              <el-input-number
                v-model="equipmentForm.impactWeight"
                :precision="3"
                :step="0.01"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="维护日期">
              <el-date-picker
                v-model="equipmentForm.lastMaintainTime"
                type="date"
                value-format="yyyy-MM-dd"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="负责人">
              <el-input v-model="equipmentForm.owner" />
            </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="备注">
              <el-input
                v-model="equipmentForm.remark"
                type="textarea"
                :rows="2"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <span slot="footer">
        <el-button
          size="mini"
          @click="equipmentDialogVisible = false"
        >取消</el-button>
        <el-button
          size="mini"
          type="primary"
          @click="submitEquipmentForm"
        >保存</el-button>
      </span>
    </el-dialog>

    <el-dialog
      title="模型微调样本录入"
      :visible.sync="tuningDialogVisible"
      width="720px"
      append-to-body
    >
      <el-form
        ref="tuningFormRef"
        :model="tuningForm"
        :rules="tuningRules"
        size="mini"
        label-width="112px"
      >
        <el-row :gutter="12">
          <el-col :span="8">
            <el-form-item label="样本来源">
              <el-select
                v-model="tuningForm.source"
                style="width: 100%"
              >
                <el-option label="人工复核" value="人工复核" />
                <el-option label="质检反馈" value="质检反馈" />
                <el-option label="班组经验" value="班组经验" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item
              label="机架"
              prop="standNo"
            >
              <el-select
                v-model="tuningForm.standNo"
                style="width: 100%"
              >
                <el-option label="F1" :value="1" />
                <el-option label="F2" :value="2" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="可信权重">
              <el-input-number
                v-model="tuningForm.weight"
                :min="0.1"
                :max="1"
                :precision="2"
                :step="0.05"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item
              label="实绩轧制力"
              prop="actualForce"
            >
              <el-input-number
                v-model="tuningForm.actualForce"
                :min="0"
                :precision="1"
                :controls="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item
              label="实绩出口厚"
              prop="actualExitThickness"
            >
              <el-input-number
                v-model="tuningForm.actualExitThickness"
                :min="0"
                :precision="4"
                :controls="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="实绩板形分">
              <el-input-number
                v-model="tuningForm.actualFlatnessScore"
                :min="0"
                :max="100"
                :precision="1"
                :controls="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="备注">
              <el-input
                v-model="tuningForm.remark"
                type="textarea"
                :rows="2"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div class="tuning-preview">
        <span>当前预测力：<b>{{ formatNumber(activeTuningRow && activeTuningRow.force, 1) }}</b> kN</span>
        <span>当前预测厚：<b>{{ formatNumber(activeTuningRow && activeTuningRow.predictedExit, 4) }}</b> mm</span>
        <span>应用学习率：<b>{{ formatNumber(model.learningRate * tuningForm.weight, 2) }}</b></span>
      </div>
      <span slot="footer">
        <el-button
          size="mini"
          @click="tuningDialogVisible = false"
        >取消</el-button>
        <el-button
          size="mini"
          type="primary"
          @click="submitTuningForm"
        >写入微调</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import { listActual } from '@/api/mill/actual'
import { listPlan } from '@/api/mill/plan'
import { getRecipeDetail } from '@/api/mill/recipe'

const FEATURE_NAMES = [
  '机架号',
  '入口厚度',
  '目标厚度',
  '带钢宽度',
  '压下率',
  '辊径',
  '摩擦系数',
  '入口张力',
  '出口张力',
  '张力差',
  '速度',
  '设定力',
  '轧机刚度',
  '弯辊力',
  '合金编码',
  '来料重量',
  '前机架出口',
  '宽厚比'
]

const FEATURE_MEAN = [
  1.5, 1.2, 0.75, 1250, 32, 350, 0.065, 28, 35,
  0, 650, 780, 4400, 0, 2.5, 6, 0.9, 1300
]

const FEATURE_SCALE = [
  0.5, 0.8, 0.55, 420, 18, 55, 0.025, 26, 28,
  42, 420, 520, 900, 160, 2.5, 5, 0.7, 900
]

const OUTPUT_MEAN = {
  1: [820, 0, 520, 42, 0.76],
  2: [640, 0, 460, 38, 0.78]
}

const OUTPUT_SCALE = {
  1: [360, 0.065, 340, 25, 0.18],
  2: [300, 0.045, 290, 23, 0.16]
}

const emptyCoil = () => ({
  planId: null,
  planNo: '',
  coilNo: '',
  alloyNo: '',
  alloyCode: null,
  entryThickness: null,
  targetThickness: null,
  width: null,
  weight: null,
  length: null
})

const emptyModel = () => ({
  forceScaleF1: 1,
  forceScaleF2: 1,
  thicknessBiasF1: 0,
  thicknessBiasF2: 0,
  powerScale: 1,
  flatnessBias: 0,
  learningRate: 0.35,
  confidenceFloor: 0.55,
  forceLimit: 1800,
  powerLimit: 1600,
  reductionLimit: 62,
  flatnessLimit: 72
})

const emptyStand = (standNo) => ({
  standNo,
  entryThickness: null,
  targetThickness: null,
  width: null,
  rollDiameter: standNo === 1 ? 360 : 340,
  friction: standNo === 1 ? 0.07 : 0.06,
  entryTension: null,
  exitTension: null,
  speed: null,
  setForce: null,
  millModulus: standNo === 1 ? 4200 : 4600,
  bendForce: null,
  actualForce: null,
  actualExitThickness: null
})

const emptyEquipment = () => ({
  equipNo: '',
  equipName: '',
  area: 'F1',
  type: '轧机主机',
  status: '运行',
  healthScore: 95,
  processValue: null,
  unit: '',
  lowerLimit: null,
  upperLimit: null,
  modelFeature: '',
  impactWeight: 1,
  lastMaintainTime: '',
  owner: '',
  remark: ''
})

const defaultEquipmentList = () => ([
  {
    equipNo: 'F1-MILL-001',
    equipName: 'F1轧机主传动',
    area: 'F1',
    type: '轧机主机',
    status: '运行',
    healthScore: 94,
    processValue: 360,
    unit: 'mm',
    lowerLimit: 320,
    upperLimit: 390,
    modelFeature: 'f1RollDiameter',
    impactWeight: 1,
    lastMaintainTime: '2026-05-18',
    owner: '设备班',
    remark: '工作辊径参与F1模型特征'
  },
  {
    equipNo: 'F2-MILL-001',
    equipName: 'F2轧机主传动',
    area: 'F2',
    type: '轧机主机',
    status: '运行',
    healthScore: 92,
    processValue: 340,
    unit: 'mm',
    lowerLimit: 320,
    upperLimit: 390,
    modelFeature: 'f2RollDiameter',
    impactWeight: 1,
    lastMaintainTime: '2026-05-18',
    owner: '设备班',
    remark: '工作辊径参与F2模型特征'
  },
  {
    equipNo: 'TENS-ENTRY',
    equipName: '入口张力计',
    area: '入口段',
    type: '张力系统',
    status: '运行',
    healthScore: 90,
    processValue: 28,
    unit: 'kN',
    lowerLimit: 5,
    upperLimit: 80,
    modelFeature: 'f1Tension',
    impactWeight: 0.85,
    lastMaintainTime: '2026-05-20',
    owner: '仪表班',
    remark: '影响F1入口张力'
  },
  {
    equipNo: 'TENS-INTER',
    equipName: '机架间张力计',
    area: '机架间',
    type: '张力系统',
    status: '关注',
    healthScore: 78,
    processValue: 35,
    unit: 'kN',
    lowerLimit: 5,
    upperLimit: 90,
    modelFeature: 'f2Tension',
    impactWeight: 0.75,
    lastMaintainTime: '2026-05-12',
    owner: '仪表班',
    remark: '健康分低时会降低模型置信度'
  },
  {
    equipNo: 'SPD-MAIN',
    equipName: '主线速度反馈',
    area: '主线',
    type: '传动系统',
    status: '运行',
    healthScore: 96,
    processValue: 650,
    unit: 'm/min',
    lowerLimit: 0,
    upperLimit: 1200,
    modelFeature: 'speed',
    impactWeight: 1,
    lastMaintainTime: '2026-05-21',
    owner: '电气班',
    remark: '同步F1/F2速度输入'
  },
  {
    equipNo: 'SHAPE-EXIT',
    equipName: '出口板形仪',
    area: '出口段',
    type: '板形仪',
    status: '运行',
    healthScore: 88,
    processValue: 0,
    unit: 'I-Unit',
    lowerLimit: -80,
    upperLimit: 80,
    modelFeature: 'flatnessBias',
    impactWeight: 0.2,
    lastMaintainTime: '2026-05-15',
    owner: '仪表班',
    remark: '用于板形风险偏置'
  },
  {
    equipNo: 'POWER-MAIN',
    equipName: '主电机功率采集',
    area: '主线',
    type: '传动系统',
    status: '运行',
    healthScore: 91,
    processValue: 1,
    unit: 'ratio',
    lowerLimit: 0.7,
    upperLimit: 1.3,
    modelFeature: 'powerScale',
    impactWeight: 0.15,
    lastMaintainTime: '2026-05-17',
    owner: '电气班',
    remark: '用于功率输出缩放'
  }
])

const emptyTuningForm = () => ({
  source: '人工复核',
  standNo: 1,
  weight: 0.8,
  actualForce: null,
  actualExitThickness: null,
  actualFlatnessScore: null,
  remark: ''
})

const clamp = (value, min, max) => Math.min(Math.max(value, min), max)

const makeDense = (inputSize, outputSize, seed, gain) => {
  const weights = []
  for (let row = 0; row < outputSize; row++) {
    const line = []
    for (let col = 0; col < inputSize; col++) {
      const raw = Math.sin((row + 1) * 12.9898 + (col + 1) * 78.233 + seed) *
        Math.cos((row + 1) * 4.1414 + seed)
      line.push(Number((raw * gain).toFixed(6)))
    }
    weights.push(line)
  }
  const bias = []
  for (let index = 0; index < outputSize; index++) {
    bias.push(Number((Math.sin(seed + index * 0.71) * gain * 0.15).toFixed(6)))
  }
  return { weights, bias }
}

const DEEP_MODEL = {
  version: 'DCR-MLP-2Stand-v1',
  inputSize: FEATURE_NAMES.length,
  featureNames: FEATURE_NAMES,
  layers: [
    { ...makeDense(FEATURE_NAMES.length, 28, 1.7, 0.34), activation: 'gelu' },
    { ...makeDense(28, 18, 2.9, 0.28), activation: 'gelu' },
    { ...makeDense(18, 10, 4.2, 0.22), activation: 'tanh' },
    { ...makeDense(10, 5, 5.6, 0.18), activation: 'linear' }
  ]
}

export default {
  name: 'MillModelPrediction',
  data() {
    return {
      deepModel: DEEP_MODEL,
      selectedPlanId: null,
      planOptions: [],
      actualList: [],
      coil: emptyCoil(),
      model: emptyModel(),
      stands: [emptyStand(1), emptyStand(2)],
      lineRunning: true,
      activeLineNodeKey: 'F1',
      selectedEquipment: null,
      equipmentList: defaultEquipmentList(),
      equipmentDialogVisible: false,
      equipmentEditIndex: -1,
      equipmentForm: emptyEquipment(),
      areaOptions: ['入口段', 'F1', '机架间', 'F2', '出口段', '主线'],
      tuningDialogVisible: false,
      tuningForm: emptyTuningForm(),
      tuningSamples: [],
      equipmentRules: {
        equipNo: [{ required: true, message: '请输入设备位号', trigger: 'blur' }],
        equipName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }]
      },
      tuningRules: {
        standNo: [{ required: true, message: '请选择机架', trigger: 'change' }],
        actualForce: [{ required: true, message: '请输入实绩轧制力', trigger: 'blur' }],
        actualExitThickness: [{ required: true, message: '请输入实绩出口厚', trigger: 'blur' }]
      },
      theoryItems: [
        {
          title: '输入特征',
          formula: '18维工艺特征向量',
          desc: '入口/目标厚度、宽度、压下率、张力、速度、辊径、摩擦、弯辊力、合金编码等全部参与推理。'
        },
        {
          title: '网络结构',
          formula: 'MLP: 18 -> 28 -> 18 -> 10 -> 5',
          desc: '每个机架独立执行多层前向推理，隐藏层使用 GELU/Tanh 激活，输出多任务结果。'
        },
        {
          title: '输出头',
          formula: 'Force / Thickness / Power / Flatness / Confidence',
          desc: '一次推理同时给出轧制力、出口厚度偏差、功率、板形风险分和模型置信度。'
        },
        {
          title: '在线适配',
          formula: 'Adapter = Adapter + lr * Error',
          desc: '生产实绩只更新适配层，不改基础网络权重，适合按钢种或班次逐步校正。'
        }
      ]
    }
  },
  computed: {
    resultRows() {
      const rows = []
      let previousExit = null
      this.stands.forEach((stand, index) => {
        const prediction = this.predictStand(stand, previousExit)
        rows.push({
          ...prediction,
          standName: `F${index + 1}`,
          source: stand
        })
        previousExit = prediction.predictedExit || prediction.targetThickness
      })
      return rows
    },
    summary() {
      const validRows = this.resultRows.filter(row => row.valid)
      const last = validRows[validRows.length - 1]
      const riskOrder = ['正常', '关注', '预警', '高风险']
      const maxRisk = validRows.reduce((risk, row) => {
        return riskOrder.indexOf(row.riskLevel) > riskOrder.indexOf(risk) ? row.riskLevel : risk
      }, validRows.length ? '正常' : '')
      const avgConfidence = validRows.length
        ? validRows.reduce((sum, row) => sum + row.confidence, 0) / validRows.length * 100
        : null
      return {
        finalThickness: last ? last.predictedExit : null,
        avgConfidence,
        totalForce: validRows.reduce((sum, row) => sum + (row.force || 0), 0),
        totalPower: validRows.reduce((sum, row) => sum + (row.power || 0), 0),
        riskLevel: maxRisk,
        riskText: this.buildSummaryRiskText(validRows, maxRisk)
      }
    },
    correction() {
      const forceScale = { 1: [], 2: [] }
      const thicknessBias = []
      this.resultRows.forEach(row => {
        const actualForce = this.toNumber(row.source.actualForce)
        const actualThickness = this.toNumber(row.source.actualExitThickness)
        if (row.force > 0 && actualForce > 0) {
          forceScale[row.source.standNo].push(actualForce / row.force)
        }
        if (row.predictedExit > 0 && actualThickness > 0) {
          thicknessBias.push(actualThickness - row.predictedExit)
        }
      })
      const forceScaleF1 = this.average(forceScale[1])
      const forceScaleF2 = this.average(forceScale[2])
      return {
        forceScaleF1,
        forceScaleF2,
        avgThicknessBias: this.average(thicknessBias),
        sampleCount: forceScale[1].length + forceScale[2].length + thicknessBias.length,
        canApply: forceScale[1].length > 0 || forceScale[2].length > 0 || thicknessBias.length > 0
      }
    },
    lineNodes() {
      const f1 = this.resultRows[0] || {}
      const f2 = this.resultRows[1] || {}
      return [
        {
          key: 'ENTRY',
          name: '入口开卷',
          icon: 'IN',
          left: '6%',
          status: this.getAreaStatus('入口段'),
          metric: `${this.formatNumber(this.coil.entryThickness, 3)} mm`
        },
        {
          key: 'F1',
          name: 'F1机架',
          icon: 'F1',
          left: '28%',
          status: this.getAreaStatus('F1'),
          metric: `${this.formatNumber(f1.force, 0)} kN`
        },
        {
          key: 'INTER',
          name: '机架间',
          icon: 'T',
          left: '50%',
          status: this.getAreaStatus('机架间'),
          metric: `${this.formatNumber(this.stands[1].entryThickness, 3)} mm`
        },
        {
          key: 'F2',
          name: 'F2机架',
          icon: 'F2',
          left: '70%',
          status: this.getAreaStatus('F2'),
          metric: `${this.formatNumber(f2.force, 0)} kN`
        },
        {
          key: 'EXIT',
          name: '出口卷取',
          icon: 'OUT',
          left: '90%',
          status: this.getAreaStatus('出口段'),
          metric: `${this.formatNumber(this.summary.finalThickness, 4)} mm`
        }
      ]
    },
    activeLineNodeLabel() {
      const node = this.lineNodes.find(item => item.key === this.activeLineNodeKey)
      return node ? node.name : '--'
    },
    lineSpeed() {
      const f1Speed = this.toNumber(this.stands[0].speed)
      const f2Speed = this.toNumber(this.stands[1].speed)
      return f2Speed || f1Speed || 0
    },
    equipmentDialogTitle() {
      return this.equipmentEditIndex >= 0 ? '设备维护' : '新增设备'
    },
    activeTuningRow() {
      return this.resultRows.find(row => row.source.standNo === this.tuningForm.standNo) || null
    }
  },
  mounted() {
    this.loadPlanList()
  },
  methods: {
    loadPlanList() {
      listPlan({ pageNum: 1, pageSize: 100 }).then(res => {
        this.planOptions = res.rows || res.data || []
      }).catch(() => {
        this.$message.warning('生产计划加载失败')
      })
    },
    handlePlanChange(planId) {
      if (!planId) return
      const plan = this.planOptions.find(item => item.planId === planId)
      if (!plan) return
      this.applyPlan(plan)
      if (plan.recipeId) {
        getRecipeDetail(plan.recipeId).then(res => {
          this.applyRecipe(res.data || {})
        }).catch(() => {
          this.distributeReduction()
          this.$message.warning('工艺方案加载失败，已按厚度自动分配压下')
        })
      } else {
        this.distributeReduction()
      }
    },
    applyPlan(plan) {
      this.coil = {
        planId: plan.planId,
        planNo: plan.planNo || '',
        coilNo: plan.inMatNo || '',
        alloyNo: plan.alloyNo || '',
        alloyCode: this.inferAlloyCode(plan.alloyNo),
        entryThickness: this.toNumber(plan.inMatThick),
        targetThickness: this.toNumber(plan.outThick),
        width: this.toNumber(plan.inMatWidth),
        weight: this.toNumber(plan.inMatWeight),
        length: this.toNumber(plan.inMatLength)
      }
      this.stands.forEach(stand => {
        stand.width = this.coil.width
      })
    },
    applyRecipe(recipe) {
      const passList = Array.isArray(recipe.passList) ? recipe.passList : []
      if (passList.length >= 2) {
        this.fillStandFromPass(this.stands[0], passList[0])
        this.fillStandFromPass(this.stands[1], passList[1])
        return
      }
      if (passList.length === 1) {
        this.fillStandFromPass(this.stands[0], passList[0])
        this.stands[1].entryThickness = this.toNumber(passList[0].outThick)
        this.stands[1].targetThickness = this.toNumber(this.coil.targetThickness)
        this.stands[1].width = this.toNumber(passList[0].width) || this.coil.width
        return
      }
      this.distributeReduction(false)
    },
    fillStandFromPass(stand, pass) {
      stand.entryThickness = this.toNumber(pass.inThick)
      stand.targetThickness = this.toNumber(pass.outThick)
      stand.width = this.toNumber(pass.width) || this.coil.width
      stand.setForce = this.toNumber(pass.rollForce)
      stand.entryTension = this.toNumber(pass.inTension)
      stand.exitTension = this.toNumber(pass.outTension)
      stand.speed = this.toNumber(pass.maxSpeed)
    },
    distributeReduction(showWarning = true) {
      const entry = this.toNumber(this.coil.entryThickness)
      const target = this.toNumber(this.coil.targetThickness)
      const width = this.toNumber(this.coil.width)
      if (!entry || !target || entry <= target) {
        if (showWarning) this.$message.warning('请先输入有效的入口厚度和目标厚度')
        return
      }
      const middle = this.round(entry - (entry - target) * 0.56, 4)
      this.stands[0].entryThickness = entry
      this.stands[0].targetThickness = middle
      this.stands[0].width = width
      this.stands[1].entryThickness = middle
      this.stands[1].targetThickness = target
      this.stands[1].width = width
    },
    loadActualList() {
      const query = { pageNum: 1, pageSize: 30 }
      if (this.coil.planNo) {
        query.planNo = this.coil.planNo
      } else if (this.coil.coilNo) {
        query.entryMatId = this.coil.coilNo
      }
      listActual(query).then(res => {
        this.actualList = res.rows || []
        if (!this.actualList.length) {
          this.$message.info('未查询到匹配的生产实绩')
        }
      }).catch(() => {
        this.$message.warning('生产实绩加载失败')
      })
    },
    handleActualSelect(row) {
      if (!row) return
      const exitThickness = this.normalizeActualThickness(row.exitThickness)
      if (exitThickness) {
        this.stands[1].actualExitThickness = exitThickness
      }
    },
    selectLineNode(node) {
      this.activeLineNodeKey = node.key
      const areaMap = {
        ENTRY: '入口段',
        F1: 'F1',
        INTER: '机架间',
        F2: 'F2',
        EXIT: '出口段'
      }
      const area = areaMap[node.key]
      const equipment = this.equipmentList.find(item => item.area === area)
      if (equipment) this.selectedEquipment = equipment
    },
    lineNodeClass(node) {
      return [
        'line-node',
        `line-node--${node.status}`,
        { active: node.key === this.activeLineNodeKey }
      ]
    },
    getAreaStatus(area) {
      const list = this.equipmentList.filter(item => item.area === area)
      if (!list.length) return 'normal'
      if (list.some(item => item.status === '检修' || item.status === '停用' || item.healthScore < 60)) return 'danger'
      if (list.some(item => item.status === '关注' || item.healthScore < 82)) return 'warning'
      return 'normal'
    },
    handleEquipmentSelect(row) {
      this.selectedEquipment = row
      if (!row) return
      const keyMap = {
        '入口段': 'ENTRY',
        F1: 'F1',
        '机架间': 'INTER',
        F2: 'F2',
        '出口段': 'EXIT'
      }
      this.activeLineNodeKey = keyMap[row.area] || this.activeLineNodeKey
    },
    openEquipmentDialog(row) {
      if (row) {
        this.equipmentEditIndex = this.equipmentList.indexOf(row)
        this.equipmentForm = { ...row }
      } else {
        this.equipmentEditIndex = -1
        this.equipmentForm = emptyEquipment()
      }
      this.equipmentDialogVisible = true
    },
    submitEquipmentForm() {
      this.$refs.equipmentFormRef.validate(valid => {
        if (!valid) return
        const payload = { ...this.equipmentForm }
        if (this.equipmentEditIndex >= 0) {
          this.$set(this.equipmentList, this.equipmentEditIndex, payload)
          this.selectedEquipment = payload
        } else {
          this.equipmentList.push(payload)
          this.selectedEquipment = payload
        }
        this.equipmentDialogVisible = false
        this.$message.success('设备信息已保存')
      })
    },
    syncLineFromEquipment() {
      this.equipmentList.forEach(item => {
        this.applyEquipmentValue(item, false)
      })
      this.$message.success('设备采集值已同步到模型输入')
    },
    applyEquipmentToModel() {
      if (!this.selectedEquipment) return
      this.applyEquipmentValue(this.selectedEquipment, true)
    },
    applyEquipmentValue(equipment, notify) {
      const value = this.toNumber(equipment.processValue)
      const impact = this.toNumber(equipment.impactWeight) || 1
      if (value === null) return
      if (equipment.modelFeature === 'f1RollDiameter') this.stands[0].rollDiameter = value
      if (equipment.modelFeature === 'f2RollDiameter') this.stands[1].rollDiameter = value
      if (equipment.modelFeature === 'f1Tension') {
        this.stands[0].entryTension = value
        this.stands[0].exitTension = this.stands[0].exitTension || value
      }
      if (equipment.modelFeature === 'f2Tension') {
        this.stands[1].entryTension = value
        this.stands[1].exitTension = this.stands[1].exitTension || value
      }
      if (equipment.modelFeature === 'speed') {
        this.stands[0].speed = value
        this.stands[1].speed = value
      }
      if (equipment.modelFeature === 'flatnessBias') {
        this.model.flatnessBias = this.round(value * impact, 3)
      }
      if (equipment.modelFeature === 'powerScale') {
        this.model.powerScale = this.round(value * impact + (1 - impact), 4)
      }
      if (equipment.healthScore < 82) {
        this.model.confidenceFloor = clamp(this.model.confidenceFloor + 0.03, 0, 0.9)
      }
      if (notify) this.$message.success('设备参数已反哺模型')
    },
    openTuningDialog(standNo) {
      this.tuningForm = {
        ...emptyTuningForm(),
        standNo: standNo || 1
      }
      this.tuningDialogVisible = true
    },
    submitTuningForm() {
      this.$refs.tuningFormRef.validate(valid => {
        if (!valid) return
        const row = this.activeTuningRow
        if (!row || !row.valid) {
          this.$message.warning('当前机架没有可用预测结果')
          return
        }
        const sample = {
          ...this.tuningForm,
          predictedForce: row.force,
          predictedExit: row.predictedExit,
          createTime: new Date().toISOString()
        }
        this.tuningSamples.push(sample)
        this.applyTuningSample(sample)
        this.tuningDialogVisible = false
        this.$message.success('微调样本已写入适配层')
      })
    },
    applyTuningSample(sample) {
      const rate = clamp(this.model.learningRate * sample.weight, 0, 1)
      const forceRatio = sample.predictedForce > 0 ? sample.actualForce / sample.predictedForce : null
      const thicknessError = sample.actualExitThickness - sample.predictedExit
      if (sample.standNo === 1) {
        if (forceRatio) this.model.forceScaleF1 = this.round(this.model.forceScaleF1 * (1 + rate * (forceRatio - 1)), 4)
        this.model.thicknessBiasF1 = this.round(this.model.thicknessBiasF1 + rate * thicknessError, 4)
      } else {
        if (forceRatio) this.model.forceScaleF2 = this.round(this.model.forceScaleF2 * (1 + rate * (forceRatio - 1)), 4)
        this.model.thicknessBiasF2 = this.round(this.model.thicknessBiasF2 + rate * thicknessError, 4)
      }
      if (sample.actualFlatnessScore !== null && sample.actualFlatnessScore !== undefined) {
        const flatnessError = sample.actualFlatnessScore - (this.activeTuningRow ? this.activeTuningRow.flatnessScore : 0)
        this.model.flatnessBias = this.round(this.model.flatnessBias + rate * flatnessError * 0.1, 3)
      }
    },
    applyActualCorrection() {
      const rate = clamp(this.model.learningRate, 0, 1)
      if (this.correction.forceScaleF1) {
        this.model.forceScaleF1 = this.round(
          this.model.forceScaleF1 * (1 + rate * (this.correction.forceScaleF1 - 1)),
          4
        )
      }
      if (this.correction.forceScaleF2) {
        this.model.forceScaleF2 = this.round(
          this.model.forceScaleF2 * (1 + rate * (this.correction.forceScaleF2 - 1)),
          4
        )
      }
      if (this.correction.avgThicknessBias != null) {
        this.model.thicknessBiasF1 = this.round(this.model.thicknessBiasF1 + rate * this.correction.avgThicknessBias, 4)
        this.model.thicknessBiasF2 = this.round(this.model.thicknessBiasF2 + rate * this.correction.avgThicknessBias, 4)
      }
      this.$message.success('深度模型适配层已更新')
    },
    runPrediction() {
      if (!this.resultRows.some(row => row.valid)) {
        this.$message.warning('请先输入双机架厚度、宽度和目标参数')
        return
      }
      this.$message.success('深度模型推理已刷新')
    },
    resetAll() {
      this.selectedPlanId = null
      this.actualList = []
      this.coil = emptyCoil()
      this.model = emptyModel()
      this.stands = [emptyStand(1), emptyStand(2)]
    },
    predictStand(stand, previousExit) {
      const featureState = this.buildFeatureVector(stand, previousExit)
      const base = {
        valid: false,
        entryThickness: featureState.entryThickness,
        targetThickness: featureState.targetThickness,
        force: null,
        predictedExit: null,
        power: null,
        flatnessScore: null,
        confidence: null,
        riskLevel: '未计算',
        riskText: '参数不足',
        topFeatures: '--'
      }
      if (!featureState.valid) return base

      const normalized = this.normalizeVector(featureState.features)
      const rawOutput = this.forwardNetwork(normalized)
      const decoded = this.decodeOutput(rawOutput, stand, featureState)
      const risk = this.evaluateRisk(decoded, featureState)
      const topFeatures = this.getTopFeatures(normalized, decoded, stand, featureState)
      const actualForce = this.toNumber(stand.actualForce)
      const actualExitThickness = this.toNumber(stand.actualExitThickness)
      return {
        valid: true,
        entryThickness: featureState.entryThickness,
        targetThickness: featureState.targetThickness,
        reductionRate: featureState.reductionRate,
        force: decoded.force,
        predictedExit: decoded.predictedExit,
        power: decoded.power,
        flatnessScore: decoded.flatnessScore,
        confidence: decoded.confidence,
        extrapolationPenalty: featureState.extrapolationPenalty,
        riskLevel: risk.level,
        riskText: risk.text,
        topFeatures,
        forceError: actualForce ? actualForce - decoded.force : null,
        thicknessError: actualExitThickness ? actualExitThickness - decoded.predictedExit : null,
        forceScaleSuggested: actualForce && decoded.force > 0 ? actualForce / decoded.force : null
      }
    },
    buildFeatureVector(stand, previousExit) {
      const entryThickness = this.toNumber(stand.entryThickness) || previousExit
      const targetThickness = this.toNumber(stand.targetThickness)
      const width = this.toNumber(stand.width) || this.toNumber(this.coil.width)
      const rollDiameter = this.toNumber(stand.rollDiameter)
      if (!entryThickness || !targetThickness || !width || !rollDiameter || entryThickness <= targetThickness) {
        return { valid: false, entryThickness, targetThickness, features: [] }
      }
      const reduction = entryThickness - targetThickness
      const reductionRate = reduction / entryThickness * 100
      const entryTension = this.toNumber(stand.entryTension) || 0
      const exitTension = this.toNumber(stand.exitTension) || 0
      const speed = this.toNumber(stand.speed) || 0
      const setForce = this.toNumber(stand.setForce) || 0
      const millModulus = this.toNumber(stand.millModulus) || (stand.standNo === 1 ? 4200 : 4600)
      const bendForce = this.toNumber(stand.bendForce) || 0
      const friction = this.toNumber(stand.friction) || 0.065
      const alloyCode = this.toNumber(this.coil.alloyCode) || this.inferAlloyCode(this.coil.alloyNo)
      const weight = this.toNumber(this.coil.weight) || 0
      const prevExit = previousExit || entryThickness
      const widthThicknessRatio = width / Math.max(targetThickness, 0.001)
      const features = [
        stand.standNo,
        entryThickness,
        targetThickness,
        width,
        reductionRate,
        rollDiameter,
        friction,
        entryTension,
        exitTension,
        exitTension - entryTension,
        speed,
        setForce,
        millModulus,
        bendForce,
        alloyCode,
        weight,
        prevExit,
        widthThicknessRatio
      ]
      return {
        valid: true,
        features,
        entryThickness,
        targetThickness,
        width,
        reductionRate,
        setForce,
        speed,
        bendForce,
        extrapolationPenalty: this.calcExtrapolationPenalty(features)
      }
    },
    normalizeVector(features) {
      return features.map((value, index) => {
        return clamp((value - FEATURE_MEAN[index]) / FEATURE_SCALE[index], -4, 4)
      })
    },
    forwardNetwork(input) {
      let vector = input.slice()
      this.deepModel.layers.forEach(layer => {
        const next = layer.weights.map((weights, rowIndex) => {
          const sum = weights.reduce((total, weight, colIndex) => total + weight * vector[colIndex], layer.bias[rowIndex])
          return this.activate(sum, layer.activation)
        })
        vector = next
      })
      return vector
    },
    activate(value, activation) {
      if (activation === 'gelu') {
        return 0.5 * value * (1 + Math.tanh(Math.sqrt(2 / Math.PI) * (value + 0.044715 * Math.pow(value, 3))))
      }
      if (activation === 'tanh') return Math.tanh(value)
      return value
    },
    decodeOutput(rawOutput, stand, state) {
      const standNo = stand.standNo
      const mean = OUTPUT_MEAN[standNo]
      const scale = OUTPUT_SCALE[standNo]
      const forceScale = standNo === 1 ? this.model.forceScaleF1 : this.model.forceScaleF2
      const thicknessBias = standNo === 1 ? this.model.thicknessBiasF1 : this.model.thicknessBiasF2
      const networkForce = mean[0] + rawOutput[0] * scale[0]
      const setForce = state.setForce || networkForce
      const force = clamp((networkForce * 0.78 + setForce * 0.22) * forceScale, 80, 3200)
      const thicknessDelta = clamp(rawOutput[1] * scale[1] + thicknessBias, -0.28, 0.28)
      const predictedExit = clamp(
        state.targetThickness + thicknessDelta,
        Math.max(0.01, state.targetThickness * 0.7),
        state.entryThickness * 1.02
      )
      const power = clamp((mean[2] + rawOutput[2] * scale[2]) * this.model.powerScale, 0, 3600)
      const flatnessScore = clamp(mean[3] + rawOutput[3] * scale[3] + this.model.flatnessBias, 0, 100)
      const confidenceBase = clamp(mean[4] + rawOutput[4] * scale[4], 0.05, 0.98)
      const confidence = clamp(confidenceBase - state.extrapolationPenalty, 0.05, 0.98)
      return { force, predictedExit, power, flatnessScore, confidence }
    },
    getTopFeatures(input, decoded, stand, state) {
      const baseline = this.featureObjective(decoded)
      const impacts = input.map((value, index) => {
        const changed = input.slice()
        changed[index] = clamp(value + 0.15, -4, 4)
        const raw = this.forwardNetwork(changed)
        const next = this.decodeOutput(raw, stand, state)
        return {
          name: FEATURE_NAMES[index],
          impact: Math.abs(this.featureObjective(next) - baseline)
        }
      })
      return impacts
        .sort((a, b) => b.impact - a.impact)
        .slice(0, 3)
        .map(item => item.name)
        .join('、')
    },
    featureObjective(output) {
      return output.force / 1200 + output.predictedExit * 3 + output.flatnessScore / 80
    },
    calcExtrapolationPenalty(features) {
      const penalty = features.reduce((sum, value, index) => {
        const normalized = Math.abs((value - FEATURE_MEAN[index]) / FEATURE_SCALE[index])
        return sum + Math.max(0, normalized - 2.5) * 0.035
      }, 0)
      return clamp(penalty, 0, 0.45)
    },
    evaluateRisk(decoded, state) {
      const risks = []
      if (decoded.confidence < this.model.confidenceFloor) risks.push('模型置信度偏低')
      if (decoded.force > this.model.forceLimit) risks.push('轧制力接近上限')
      if (decoded.power > this.model.powerLimit) risks.push('功率负载偏高')
      if (state.reductionRate > this.model.reductionLimit) risks.push('压下率超限')
      if (decoded.flatnessScore > this.model.flatnessLimit) risks.push('板形风险偏高')
      if (risks.length >= 3) return { level: '高风险', text: risks.join('、') }
      if (risks.length === 2) return { level: '预警', text: risks.join('、') }
      if (risks.length === 1) return { level: '关注', text: risks[0] }
      return { level: '正常', text: '深度模型未发现明显风险' }
    },
    buildSummaryRiskText(rows, level) {
      if (!rows.length) return '等待输入计划、工艺或手工参数'
      if (level === '正常') return '双机架深度推理结果处于当前阈值范围'
      return rows
        .filter(row => row.riskLevel !== '正常')
        .map(row => `${row.standName}${row.riskText}`)
        .join('；')
    },
    inferAlloyCode(alloyNo) {
      const text = String(alloyNo || '').toUpperCase()
      if (!text) return null
      if (text.indexOf('3') === 0) return 3
      if (text.indexOf('5') === 0) return 5
      if (text.indexOf('6') === 0) return 6
      const digit = text.match(/\d/)
      return digit ? Number(digit[0]) : 2
    },
    normalizeActualThickness(value) {
      const thickness = this.toNumber(value)
      const entry = this.toNumber(this.coil.entryThickness)
      if (!thickness) return null
      if (entry && entry < 20 && thickness > 20) {
        return this.round(thickness / 1000, 4)
      }
      return thickness
    },
    average(values) {
      if (!values.length) return null
      return values.reduce((sum, item) => sum + item, 0) / values.length
    },
    toNumber(value) {
      if (value === null || value === undefined || value === '') return null
      const numberValue = Number(value)
      return Number.isFinite(numberValue) ? numberValue : null
    },
    round(value, precision) {
      if (value === null || value === undefined || !Number.isFinite(Number(value))) return null
      return Number(Number(value).toFixed(precision))
    },
    formatNumber(value, precision) {
      const numberValue = this.toNumber(value)
      if (numberValue === null) return '--'
      return numberValue.toFixed(precision)
    },
    formatPercent(value) {
      const numberValue = this.toNumber(value)
      if (numberValue === null) return '--'
      return `${(numberValue * 100).toFixed(1)}%`
    },
    formatSigned(value, precision) {
      const numberValue = this.toNumber(value)
      if (numberValue === null) return '--'
      const prefix = numberValue > 0 ? '+' : ''
      return `${prefix}${numberValue.toFixed(precision)}`
    },
    riskTagType(level) {
      if (level === '高风险') return 'danger'
      if (level === '预警') return 'warning'
      if (level === '关注') return 'info'
      if (level === '正常') return 'success'
      return ''
    },
    confidenceClass(value) {
      const numberValue = this.toNumber(value)
      if (numberValue === null) return 'muted-val'
      if (numberValue < this.model.confidenceFloor) return 'bad-val'
      if (numberValue < 0.7) return 'warn-val'
      return 'good-val'
    },
    errorClass(value) {
      const numberValue = this.toNumber(value)
      if (numberValue === null) return 'muted-val'
      return Math.abs(numberValue) <= 0.01 ? 'good-val' : 'bad-val'
    },
    planOptionLabel(plan) {
      const coilNo = plan.inMatNo || '未填卷号'
      const size = `${plan.inMatThick || '-'} -> ${plan.outThick || '-'} mm`
      return `${coilNo} / ${plan.planNo || '-'} / ${size}`
    },
    standRowClass({ rowIndex }) {
      return rowIndex % 2 === 0 ? '' : 'alt-row'
    }
  }
}
</script>

<style scoped lang="scss">
.model-page {
  min-height: calc(100vh - 84px);
  padding: 10px 12px;
  box-sizing: border-box;
  background: #f0f2f5;
  color: #1f2d3d;
}

.top-section,
.middle-section,
.bottom-section {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: 10px;
  margin-bottom: 10px;
}

.middle-section {
  grid-template-columns: 360px minmax(0, 1fr);
}

.bottom-section {
  grid-template-columns: minmax(0, 1fr) 420px;
  margin-bottom: 0;
}

.line-section,
.equipment-section {
  margin-bottom: 10px;
  background: #fff;
  border: 1px solid #dde1e6;
  border-radius: 3px;
  overflow: hidden;
}

.model-card,
.summary-card,
.input-card,
.stand-card,
.actual-card,
.detail-card {
  background: #fff;
  border: 1px solid #dde1e6;
  border-radius: 3px;
  overflow: hidden;
}

.section-header {
  min-height: 34px;
  padding: 7px 10px;
  box-sizing: border-box;
  background: #1c2b3a;
  color: #ecf0f1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  font-weight: 700;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 6px;
}

.line-canvas {
  position: relative;
  height: 154px;
  margin: 10px;
  border: 1px solid #e4e7ed;
  border-radius: 3px;
  background:
    linear-gradient(180deg, #fbfcfe 0%, #f6f8fb 100%);
  overflow: hidden;
}

.line-strip {
  position: absolute;
  left: 5%;
  right: 5%;
  top: 72px;
  height: 10px;
  border-radius: 10px;
  background: #c8d2df;
  overflow: hidden;
}

.strip-core {
  display: block;
  width: 35%;
  height: 100%;
  border-radius: 10px;
  background: linear-gradient(90deg, #5f7f9f, #1d4e89);
}

.line-running .strip-core {
  animation: stripMove 1.8s linear infinite;
}

@keyframes stripMove {
  0% { transform: translateX(-120%); }
  100% { transform: translateX(360%); }
}

.line-node {
  position: absolute;
  top: 34px;
  width: 106px;
  height: 88px;
  transform: translateX(-50%);
  border: 1px solid #cfd8e3;
  border-radius: 4px;
  background: #fff;
  color: #1f2d3d;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(18, 35, 54, 0.08);
  transition: border-color .16s, box-shadow .16s, transform .16s;

  &:hover,
  &.active {
    border-color: #1d4e89;
    box-shadow: 0 4px 12px rgba(29, 78, 137, 0.18);
    transform: translateX(-50%) translateY(-2px);
  }

  b,
  em {
    display: block;
    font-style: normal;
    text-align: center;
  }

  b {
    margin-top: 5px;
    font-size: 12px;
  }

  em {
    margin-top: 4px;
    font-family: Consolas, Monaco, monospace;
    font-size: 11px;
    color: #606266;
  }
}

.node-icon {
  display: block;
  width: 42px;
  height: 26px;
  line-height: 26px;
  margin: 9px auto 0;
  border-radius: 3px;
  background: #1c2b3a;
  color: #fff;
  text-align: center;
  font-size: 11px;
  font-weight: 700;
}

.line-node--warning .node-icon {
  background: #d99000;
}

.line-node--danger .node-icon {
  background: #d9534f;
}

.line-info {
  display: flex;
  align-items: center;
  gap: 18px;
  min-height: 34px;
  padding: 0 12px 8px;
  font-size: 12px;
  color: #606266;

  b {
    color: #1c2b3a;
    font-family: Consolas, Monaco, monospace;
  }
}

.theory-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  padding: 10px;
}

.theory-item {
  min-height: 100px;
  padding: 9px 10px;
  border: 1px solid #e4e7ed;
  border-radius: 3px;
  background: #fbfcfe;
  box-sizing: border-box;
}

.theory-title {
  font-size: 12px;
  font-weight: 700;
  color: #1c2b3a;
}

.theory-formula {
  margin-top: 6px;
  padding: 5px 6px;
  border-left: 3px solid #1d4e89;
  background: #eef4fb;
  font-family: Consolas, Monaco, monospace;
  font-size: 12px;
  color: #1d4e89;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.theory-desc {
  margin-top: 6px;
  line-height: 1.45;
  font-size: 12px;
  color: #606266;
}

.summary-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  padding: 10px 10px 0;
}

.summary-cell {
  min-height: 58px;
  padding: 8px;
  border: 1px solid #e4e7ed;
  border-radius: 3px;
  background: #fbfcfe;
  box-sizing: border-box;

  span {
    display: block;
    font-size: 12px;
    color: #7f8c8d;
  }

  b {
    display: inline-block;
    margin-top: 4px;
    font-size: 20px;
    color: #1c2b3a;
    font-family: Consolas, Monaco, monospace;
  }

  em {
    margin-left: 4px;
    font-style: normal;
    font-size: 12px;
    color: #909399;
  }
}

.risk-line,
.correction-line {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 36px;
  padding: 0 10px;
  font-size: 12px;
  color: #606266;
}

.risk-line {
  border-top: 1px solid #edf0f5;
  margin-top: 10px;
}

.risk-text,
.feature-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.compact-form {
  padding: 10px 10px 0;

  ::v-deep .el-form-item {
    margin-bottom: 8px;
  }
}

.section-subtitle {
  padding: 7px 10px;
  border-top: 1px solid #edf0f5;
  border-bottom: 1px solid #edf0f5;
  background: #f7f9fc;
  font-size: 12px;
  font-weight: 700;
  color: #1c2b3a;
}

.stand-table,
.actual-table,
.detail-table,
.equipment-table {
  ::v-deep .el-input-number {
    width: 100%;
  }

  ::v-deep .el-input__inner {
    text-align: right;
    padding: 0 4px;
  }

  ::v-deep .el-table__row.alt-row td {
    background: #f7f9fc !important;
  }
}

.equipment-section {
  margin-top: 10px;
}

.actual-body {
  display: grid;
  grid-template-columns: 43% minmax(0, 1fr);
  gap: 8px;
  padding: 8px;
}

.actual-list,
.actual-edit {
  min-width: 0;
}

.correction-line {
  border-top: 1px solid #edf0f5;
  background: #fbfcfe;

  b {
    color: #1c2b3a;
    font-family: Consolas, Monaco, monospace;
  }
}

.calc-val {
  font-family: Consolas, Monaco, monospace;
  font-weight: 700;
  color: #1d4e89;
}

.risk-cell {
  margin-left: 6px;
  color: #606266;
}

.good-val {
  color: #13a463;
  font-family: Consolas, Monaco, monospace;
  font-weight: 700;
}

.warn-val {
  color: #d99000;
  font-family: Consolas, Monaco, monospace;
  font-weight: 700;
}

.bad-val {
  color: #d9534f;
  font-family: Consolas, Monaco, monospace;
  font-weight: 700;
}

.muted-val {
  color: #909399;
}

.tuning-preview {
  display: flex;
  gap: 16px;
  padding: 8px 10px;
  border: 1px solid #e4e7ed;
  border-radius: 3px;
  background: #fbfcfe;
  font-size: 12px;
  color: #606266;

  b {
    color: #1c2b3a;
    font-family: Consolas, Monaco, monospace;
  }
}

@media (max-width: 1280px) {
  .top-section,
  .middle-section,
  .bottom-section {
    grid-template-columns: 1fr;
  }

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

  .line-info {
    flex-wrap: wrap;
    gap: 8px;
  }
}
</style>
