<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="76px">
     
      <el-form-item label="生产令号" prop="hbytSclh">
        <el-input
          v-model="queryParams.hbytSclh"
          placeholder="请输入生产令号"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="工作中心" prop="workCenterName">
        <el-select
          v-model="queryParams.workCenterName"
          placeholder="请选择工作中心"
          clearable
          style="width: 215px"
          @change="handleQuery"
        >
          <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-form-item label="产品编码" prop="productCode">
        <el-input
          v-model="queryParams.productCode"
          placeholder="请输入产品编码"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="产品名称" prop="productName">
        <el-input
          v-model="queryParams.productName"
          placeholder="请输入产品名称"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>

      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

 

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

    <div v-if="selectedRows.length" class="selection-bar">
      <span class="selection-bar__text">已选{{ selectedRows.length }}项</span>
      <span class="selection-bar__clear" @click="clearSelection">取消选择</span>
    </div>

    <el-table
      ref="mainTable"
      v-loading="loading"
      :data="operationPlanningMainList"
      :row-key="rowKey"
      :row-class-name="rowClassName"
      :header-cell-style="headerCellStyle"
      @selection-change="handleSelectionChange"
    >
      <el-table-column type="selection" width="46" align="center" />
      <el-table-column type="index" label="序号" width="60" align="center" />
      <el-table-column label="生产令号" min-width="160">
        <template slot-scope="scope">
          <div class="cell-main">
            <div class="cell-title">{{ scope.row.batchNo || scope.row.hbytSclh || '-' }}</div>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="类型" width="90" align="center">
        <template slot-scope="scope">
          <el-tag :type="typeTagType(scope.row)" size="mini" effect="plain">{{ typeText(scope.row) }}</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="产品主数据" min-width="320">
        <template slot-scope="scope">
          <div class="cell-main">
            <div class="cell-title cell-title--wrap">{{ scope.row.productCode }}<span v-if="scope.row.productName"> | {{ scope.row.productName }}</span></div>
            <div class="cell-sub">{{ scope.row.proSpecification || '-' }}</div>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="产线" width="120">
        <template slot-scope="scope">
          <div class="cell-main">
            <div class="cell-title">{{ scope.row.lineText || '-' }}</div>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="开始时间" width="120" align="center">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.planStartTime, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="结束时间" width="120" align="center">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.planFinishTime, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column
        label="状态"
        width="100"
        align="center"
        :filters="statusFilters"
        :filter-multiple="false"
        :filter-method="filterOrderStatus"
      >
        <template slot-scope="scope">
          <el-tag size="mini" :class="statusTagClass(scope.row)">{{ statusText(scope.row) }}</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="工序进度" min-width="360">
        <template slot-scope="scope">
          <div class="proc">
            <template v-if="!(scope.row.processSteps || []).length">
              <span class="cell-sub">-</span>
            </template>
            <template v-else>
              <div class="proc-steps">
                <div v-for="(p, idx) in scope.row.processSteps" :key="idx" class="proc-step">
                  <div class="proc-step-top">
                    <span class="proc-circle" :class="{ clickable: !!p.raw, ellipsis: !p.raw && p.processName === '…' }" @click="openProcessDetail(scope.row, p)">
                      <el-progress
                        type="circle"
                        :percentage="p.done ? 100 : p.percent"
                        :width="32"
                        :stroke-width="4"
                        :show-text="false"
                        :color="p.done ? '#16c784' : '#00b4ad'"
                      />
                      <span class="proc-circle__text" :class="{ done: p.done }">
                        <i v-if="p.done" class="el-icon-check"></i>
                        <span v-else-if="p.raw">{{ Math.max(0, Math.min(99, Number(p.percent) || 0)) }}%</span>
                        <span v-else>…</span>
                      </span>
                    </span>
                    <span v-if="idx < scope.row.processSteps.length - 1" class="proc-line" :class="{ done: p.done }"></span>
                  </div>
                  <div class="proc-step-name">{{ p.processName || '-' }}</div>
                </div>
              </div>
            </template>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="数量" width="110" align="center">
        <template slot-scope="scope">
          <span>{{ scope.row.overallPlanQty }}</span>
        </template>
      </el-table-column>
      <el-table-column label="已完成数量" width="110" align="center">
        <template slot-scope="scope">
          <span>{{ scope.row.overallOkQty }}</span>
        </template>
      </el-table-column>
      <el-table-column label="进度" width="140" align="center">
        <template slot-scope="scope">
          <div class="progress-cell">
            <el-progress :percentage="scope.row.overallProgress" :stroke-width="8" :show-text="false" :color="'#16c784'" />
            <span class="progress-text">{{ scope.row.overallProgress }}%</span>
          </div>
        </template>
      </el-table-column>
     
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />

    <el-drawer
      class="process-drawer"
      :title="processDialogTitle"
      :visible.sync="processDialogOpen"
      direction="btt"
      size="70%"
      append-to-body
    >
      <template slot="title">
        <div class="process-drawer__title-line">
          <span class="process-drawer__title-main">工序详情</span>
          <span v-if="processDialogBillNo" class="process-drawer__title-kv">单据编码：{{ processDialogBillNo }}</span>
          <span v-if="processDialogBatchNo" class="process-drawer__title-kv">生产执行单号：{{ processDialogBatchNo }}</span>
          <span v-if="processDialogProduct" class="process-drawer__title-kv process-drawer__title-product">物料编码：{{ processDialogProduct }}</span>
        </div>
      </template>
      <div class="process-drawer__toolbar">
        <div class="process-drawer__toolbar-left">
          <span v-if="processDialogDirtyCount" class="process-drawer__dirty">已修改{{ processDialogDirtyCount }}项</span>
        </div>
        <div class="process-drawer__toolbar-right">
          <el-button
            type="primary"
            size="mini"
            :loading="processDialogSaving"
            :disabled="!processDialogDirtyCount"
            @click="submitOperatorUpdates"
            v-hasPermi="['system:operationPlanningMain:updateOperators']"
          >保存</el-button>
        </div>
      </div>
      <div class="process-drawer__table">
        <el-table
          ref="processTable"
          :data="processDialogList"
          size="mini"
          border
          highlight-current-row
          height="100%"
          :row-key="p => p.entityEntryId || p.operNo"
        >
        <el-table-column label="工序号" prop="operNo" width="80" align="center" />
        <el-table-column label="工序名称" prop="processName" min-width="140" />
        <el-table-column label="工作中心" prop="workCenter" min-width="120" />
        <el-table-column label="计划数量" prop="planQty" width="90" align="center" />
        <el-table-column label="转入数量" prop="inQty" width="90" align="center" />
        <el-table-column label="合格数量" prop="okQty" width="90" align="center" />
        <el-table-column label="废品数量" prop="ngQty" width="90" align="center" />
        <el-table-column label="状态" width="100" align="center">
          <template slot-scope="scope">
          <el-tag size="mini" effect="plain" :class="processStatusClass(scope.row)">{{ processStatusText(scope.row) }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="转出数量" prop="transOutQty" width="90" align="center" />
        <el-table-column label="损耗数量" prop="wastageQty" width="90" align="center" />
        <el-table-column label="加工车间" prop="department" min-width="120" />
        <el-table-column label="作业人员" min-width="220">
          <template slot-scope="scope">
            <div class="operator-cell">
              <el-select
                v-model="scope.row.__operatorDraft"
                size="mini"
                clearable
                filterable
                placeholder="搜索选择人员"
                @change="markOperatorDirty(scope.row)"
                style="width: 100%"
              >
                <el-option
                  v-for="item in getOperatorOptions(scope.row.workCenter)"
                  :key="item"
                  :label="item"
                  :value="item"
                />
              </el-select>
              <el-button
                v-if="scope.row.__operatorDirty"
                type="text"
                size="mini"
                :disabled="scope.row.__operatorSaving"
                @click="resetOperatorDraft(scope.row)"
              >取消</el-button>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="设备" min-width="220">
          <template slot-scope="scope">
            <div class="operator-cell">
              <el-select
                v-model="scope.row.__equipmentDraft"
                size="mini"
                clearable
                filterable
                placeholder="搜索选择设备"
                @change="(val) => handleEquipmentChange(val, scope.row)"
                style="width: 100%"
              >
                <el-option
                  v-for="item in getEquipmentOptions(scope.row.workCenter)"
                  :key="item"
                  :label="item"
                  :value="item"
                />
              </el-select>
              <el-button
                v-if="scope.row.__equipmentDirty"
                type="text"
                size="mini"
                :disabled="scope.row.__operatorSaving"
                @click="resetEquipmentDraft(scope.row)"
              >取消</el-button>
            </div>
          </template>
        </el-table-column>
        <el-table-column label="计划开始" width="160">
          <template slot-scope="scope">
            <span>{{ getProcessPlanStartTime(scope.row) || '-' }}</span>
          </template>
        </el-table-column>
        <el-table-column label="计划完成" width="160">
          <template slot-scope="scope">
            <span>{{ getProcessPlanFinishTime(scope.row) || '-' }}</span>
          </template>
        </el-table-column>
        <el-table-column label="控制码" prop="optCtrlCode" min-width="120" />
        <el-table-column label="关键工序" prop="keyOper" width="90" align="center" />
        <el-table-column label="活动一" prop="activity1Name" min-width="120" />
        <el-table-column label="工序说明" prop="operDescription" min-width="140" />
        <el-table-column label="单据体内码" prop="entityEntryId" width="120" align="center" />
        </el-table>
      </div>
    </el-drawer>

    <!-- 添加或修改工序计划主对话框 -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-form-item label="FID(K3单据内码)" prop="fid">
          <el-input v-model="form.fid" placeholder="请输入FID(K3单据内码)" />
        </el-form-item>
        <el-form-item label="单据编号" prop="billNo">
          <el-input v-model="form.billNo" placeholder="请输入单据编号" />
        </el-form-item>
        <el-form-item label="生产订单编号" prop="moNumber">
          <el-input v-model="form.moNumber" placeholder="请输入生产订单编号" />
        </el-form-item>
        <el-form-item label="生产令号" prop="hbytSclh">
          <el-input v-model="form.hbytSclh" placeholder="请输入生产令号" />
        </el-form-item>
        <el-form-item label="产品编码" prop="productCode">
          <el-input v-model="form.productCode" placeholder="请输入产品编码" />
        </el-form-item>
        <el-form-item label="产品名称" prop="productName">
          <el-input v-model="form.productName" placeholder="请输入产品名称" />
        </el-form-item>
        <el-form-item label="规格型号" prop="proSpecification">
          <el-input v-model="form.proSpecification" placeholder="请输入规格型号" />
        </el-form-item>
        <el-form-item label="计划开始时间" prop="planStartTime">
          <el-date-picker clearable
            v-model="form.planStartTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="请选择计划开始时间">
          </el-date-picker>
        </el-form-item>
        <el-form-item label="计划结束时间" prop="planFinishTime">
          <el-date-picker clearable
            v-model="form.planFinishTime"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            placeholder="请选择计划结束时间">
          </el-date-picker>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { listOperationPlanningMain, cascadeListOperationPlanningMain, getOperationPlanningMain, delOperationPlanningMain, addOperationPlanningMain, updateOperationPlanningMain, updateOperationPlanningMainOperators, syncOperationPlanningMain, queryFromK3OperationPlanningMain, getEquipmentOptions } from "@/api/system/operationPlanningMain";

export default {
  name: "OperationPlanningMain",
  data() {
    return {
      // 按钮loading
      buttonLoading: false,
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      selectedRows: [],
      processDialogOpen: false,
      processDialogTitle: "工序详情",
      processDialogMeta: "",
      processDialogList: [],
      processDialogOrder: null,
      processDialogSaving: false,
      k3SyncLoading: false,
      k3QueryLoading: false,
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      statusFilters: [
        { text: '未开始', value: '未开始' },
        { text: '进行中', value: '进行中' },
        { text: '预警', value: '预警' },
        { text: '延期', value: '延期' },
        { text: '已完成', value: '已完成' }
      ],
      // 工序计划主表格数据
      operationPlanningMainList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        moNumber: undefined,
        hbytSclh: undefined,
        workCenterName: undefined,
        productCode: undefined,
        productName: undefined,
        planStartTime: undefined,
        planFinishTime: undefined,
      },
      // 表单参数
      form: {},
      staticEquipmentData: {
        '机一工段': [
          '车铣复合', '双主轴走心机', '数车', '数控外圆磨', '线切割', '电火花', 
          '立铣', '龙门铣', '摇臂钻', '普车', '锯床', '转序员'
        ],
        '机二工段': [
          '立加', '立加 1370', '立加 1177', '数插 5035', '平磨', '立磨', '钳修', 
          '攻丝', '数控龙门铣', '卧加3519', '龙门2013', '龙门3018', '龙门'
        ],
        '铆焊工段': [
          '数控激光下料', '数控火焰下料', '数控切管下料', '铆工', '焊工', 
          '激光焊工', '机器人焊接', '打磨', '抛丸', '校形'
        ],
        '委外中心': [
          '勤润','泰美','南浦','德宝','圣田','盛懋','智创'
        ],
        '电钳工段': [
          '组装线（电气组装配线配盘）','伺服（三相异步）电气测试调试'
        ],
        '装一工段': [
          '产品二班 | 一单元', '产品二班 | 二单元', '产品二班 | 三单元', '产品二班 | 四单元',
          '产品二班 | 五单元', '产品二班 | 六单元', '产品二班 | 七单元', '产品二班 | 八单元',
          '产品一班 | 一单元', '产品一班 | 二单元', '产品一班 | 三单元', '产品一班 | 四单元',
          '链条班 | 一单元', '链条班 | 二单元', '链条班 | 三单元', '链条班 | 四单元', '链条班 | 五单元',
          '测试班 | 一单元', '测试班 | 二单元', '测试班 | 三单元'
        ],
        '装二工段': [
          '集成产品组 | 一单元', '集成产品组 | 二单元', '集成产品组 | 三单元', '集成产品组 | 四单元',
          '减速机班 | 一单元', '减速机班 | 二单元',
          '装配一组 | 一单元', '装配一组 | 二单元', '装配一组 | 三单元', '装配一组 | 四单元'
        ]
      },
      staticOperatorData: {
        '机一工段': [
          '刘彩茹', '王玉', '王瑞康', '王立兴', '张泽旭', '何亚平', '董柯朝', '张晓峰', 
          '李风存', '贾中恒', '丁世强', '吴崎', '孙小健', '池彦朋', '焦伟涛', '柴鑫豪', 
          '孙朋园', '仇建龙', '张召飞', '楚世光', '许会强', '王永旺', '王颜强', '秦孟青', 
          '王海芳', '宋娇娇'
        ],
        '机二工段': [
          '柴旭璞', '谷丽博', '裴旭卓', '刘少杰', '常书芳', '田由甲', '曹兴丽', '李子轩', 
          '张旭泽', '王子过', '李蒲', '杜晓龙', '刘命鑫', '张梧祎', '赵伟', '安松浩', 
          '刘思雨', '肖泽润', '魏双全', '李兴酉', '王锦翔', '张利明', '李少聪', '李强', 
          '崔金铎', '于亚乐', '鲁彦芹', '路静茹', '刘翠青', '杨冲冲', '李晓玉', '宋佳佳', 
          '秦孝宇', '赵铭豪', '李凯峰', '高子娟', '常志涛', '宁若奇', '周建坤', '张泽远', 
          '王晨光', '张佳祺', '王浩义', '王龙', '陈阳', '崔炫宇', '尚宇清', '金国林', 
          '李子洋', '康玉朝'
        ],
        '铆焊工段': [
          '马明广', '周佳乐', '李晓亮', '董献召', '王春炜', '付佳兴', '柳晓龙', '张雨轩', 
          '石泽明', '王晓凡', '郝亮', '耿利刚', '魏辰强', '李永文', '吴海庆', '范会雨', 
          '贾会涛', '吴凯亮', '赵兰争', '董亚军', '王晓军', '代程翼', '魏佳蕊', '齐乐芳', 
          '康坤兰', '赵锦泽', '游梦皓', '牛立革', '宋金娜', '赵科飞'
        ],
        '电钳工段': [
          '乔克俭', '乔鑫磊', '贾江涛', '魏兴宁', '韩巍', '温玉博',
          '王宁', '李士荷', '李卓航', '王烁', '孙涵', '赵泽龙'
        ],
        '装一工段': [
          '位会然', '李飞飞', '孙红霞', '王恩静', '宋瑞彦', '胡建泛', '常静肖', '宋丽花',
          '刘世普', '马宇航', '王欢欢', '韦隆香', '高领琪', '梁红霞', '张晓帅',
          '姚少鹏', '王晓田', '任泽钊', '牛泽鹏', '胡韶倩', '史傲磊', '于金赐', '孙畅',
          '宋少然', '李丽哲', '刘翠娟', '李丽彦', '董静柳', '高肖欣', '康文娟', '高丽月',
          '徐燕', '茹彩肖',
          '何许娜', '侯乐辉', '李少杰', '李江花', '梅彦恒', '何旭冉'
        ],
        '装二工段': [
          '尹哲辉', '李博博', '李建霞', '丁小丽', '周晓冲', '曹海勃', '董永军', '周文茹',
          '王硕', '王玉博', '任泽寒', '宁素丽',
          '董士杰', '张佳迅', '边敬辉', '谷飞博', '王东坡', '王玉霞', '刘倚辰', '孙玉凯'
        ],
        '委外中心': [
          '刘冉', '吕松林', '王旭得', '张浩宇', '李欣'
        ]
      },
      // 表单校验
      rules: {
        id: [
          { required: true, message: "主键ID不能为空", trigger: "blur" }
        ],
        fid: [
          { required: true, message: "FID(K3单据内码)不能为空", trigger: "blur" }
        ],
        billNo: [
          { required: true, message: "单据编号不能为空", trigger: "blur" }
        ],
        moNumber: [
          { required: true, message: "生产订单编号不能为空", trigger: "blur" }
        ],
        hbytSclh: [
          { required: true, message: "生产令号不能为空", trigger: "blur" }
        ],
        productCode: [
          { required: true, message: "产品编码不能为空", trigger: "blur" }
        ],
        productName: [
          { required: true, message: "产品名称不能为空", trigger: "blur" }
        ],
        proSpecification: [
          { required: true, message: "规格型号不能为空", trigger: "blur" }
        ],
        planStartTime: [
          { required: true, message: "计划开始时间不能为空", trigger: "blur" }
        ],
        planFinishTime: [
          { required: true, message: "计划结束时间不能为空", trigger: "blur" }
        ],
      }
    };
  },
  created() {
    this.initFromRoute();
  },
  computed: {
    processDialogBillNo() {
      const o = this.processDialogOrder || {};
      return o.billNo || o.moNumber || '';
    },
    processDialogBatchNo() {
      const o = this.processDialogOrder || {};
      return o.batchNo || o.hbytSclh || '';
    },
    processDialogProduct() {
      const o = this.processDialogOrder || {};
      return [o.productCode, o.productName].filter(Boolean).join(' | ');
    },
    processDialogDirtyCount() {
      return (this.processDialogList || []).filter(p => p && (p.__operatorDirty || p.__equipmentDirty)).length;
    }
  },
  methods: {
    getEquipmentOptions(workCenter) {
      if (!workCenter) return [];
      const opts = this.staticEquipmentData[workCenter] || [];
      return opts;
    },
    getOperatorOptions(workCenter) {
      if (!workCenter) return [];
      const opts = this.staticOperatorData[workCenter] || [];
      return opts;
    },
    buildK3Query() {
      const q = this.queryParams || {};
      const { pageNum, pageSize, ...filters } = q;
      return { ...filters };
    },
    handleSyncK3() {
      if (this.k3SyncLoading) return;
      const payload = this.buildK3Query();
      this.$modal.confirm('确认同步K3数据？').then(() => {
        this.k3SyncLoading = true;
        return syncOperationPlanningMain(payload);
      }).then(() => {
        this.$modal.msgSuccess('同步成功');
        this.getList();
      }).catch(() => {
      }).finally(() => {
        this.k3SyncLoading = false;
      });
    },
    handleQueryFromK3() {
      if (this.k3QueryLoading) return;
      const query = this.buildK3Query();
      this.k3QueryLoading = true;
      queryFromK3OperationPlanningMain(query)
        .then(response => {
          const { rows, total, hasTotal } = this.normalizeListResponse(response);
          this.operationPlanningMainList = (rows || []).map(row => this.decorateRow(row));
          this.total = hasTotal ? total : this.inferTotalFromPage(rows);
          this.$modal.msgSuccess('已从K3查询');
        })
        .finally(() => {
          this.k3QueryLoading = false;
        });
    },
    initFromRoute() {
      const q = (this.$route && this.$route.query) || {};
      const batchNo = q.batchNo || q.hbytSclh || q.productionOrderNo;
      if (batchNo) {
        this.queryParams.hbytSclh = batchNo;
        this.queryParams.pageNum = 1;
      }
      this.getList();
    },
    /** 查询工序计划主列表 */
    getList() {
      this.loading = true;
      cascadeListOperationPlanningMain(this.queryParams)
        .catch(() => listOperationPlanningMain(this.queryParams))
        .then(response => {
          const { rows, total, hasTotal } = this.normalizeListResponse(response);
          this.operationPlanningMainList = (rows || []).map(row => this.decorateRow(row));
          this.total = hasTotal ? total : this.inferTotalFromPage(rows);
        })
        .finally(() => {
          this.loading = false;
        });
    },
    normalizeListResponse(response) {
      if (!response) return { rows: [], total: 0, hasTotal: false };

      if (Array.isArray(response.rows)) {
        return { rows: response.rows, total: Number(response.total) || response.rows.length, hasTotal: response.total != null };
      }

      if (response.data && Array.isArray(response.data.records)) {
        return { rows: response.data.records, total: Number(response.data.total) || response.data.records.length, hasTotal: response.data.total != null };
      }

      if (response.data && Array.isArray(response.data.list)) {
        return { rows: response.data.list, total: Number(response.data.total) || response.data.list.length, hasTotal: response.data.total != null };
      }

      if (Array.isArray(response.data)) {
        return { rows: response.data, total: Number(response.total) || response.data.length, hasTotal: response.total != null };
      }

      if (response.data && Array.isArray(response.data.rows)) {
        return { rows: response.data.rows, total: Number(response.data.total) || response.data.rows.length, hasTotal: response.data.total != null };
      }

      if (response.data && response.data.data && Array.isArray(response.data.data.records)) {
        return { rows: response.data.data.records, total: Number(response.data.data.total) || response.data.data.records.length, hasTotal: response.data.data.total != null };
      }

      if (response.data && response.data.data && Array.isArray(response.data.data.rows)) {
        return { rows: response.data.data.rows, total: Number(response.data.data.total) || response.data.data.rows.length, hasTotal: response.data.data.total != null };
      }

      return { rows: [], total: 0, hasTotal: false };
    },
    inferTotalFromPage(rows) {
      const pageNum = Number(this.queryParams.pageNum) || 1;
      const pageSize = Number(this.queryParams.pageSize) || 10;
      const len = Array.isArray(rows) ? rows.length : 0;

      if (len >= pageSize) return pageNum * pageSize + 1;
      return (pageNum - 1) * pageSize + len;
    },
    decorateRow(row) {
      const processList = Array.isArray(row.processList)
        ? [...row.processList].sort((a, b) => (Number(a.operNo) || 0) - (Number(b.operNo) || 0))
        : [];

      const finalProcess = processList.length ? processList[processList.length - 1] : undefined;
      const overallPlanQty = this.normalizeNumber(
        (finalProcess && finalProcess.planQty) ?? (processList.find(p => p.planQty != null) || {}).planQty
      );
      const overallOkQty = this.normalizeNumber((finalProcess && finalProcess.okQty) ?? 0);
      const overallProgress = overallPlanQty > 0 ? Math.min(100, Math.round((overallOkQty / overallPlanQty) * 100)) : 0;

      const processSteps = processList.map(p => {
        const planQty = this.normalizeNumber(p.planQty || overallPlanQty);
        const okQty = this.normalizeNumber(p.okQty);
        const inQty = this.normalizeNumber(p.inQty);
        const backendProgress = this.normalizePercent(p.progress);
        const baseQty = p.inQty != null ? inQty : okQty;
        const fallbackProgress = planQty > 0 ? (baseQty / planQty) * 100 : 0;
        const percent = backendProgress != null ? backendProgress : this.normalizePercent(fallbackProgress) || 0;
        const done = this.isProcessDone(p, planQty, okQty, inQty, percent);

        return {
          operNo: p.operNo,
          entityEntryId: p.entityEntryId,
          processName: p.processName,
          percent,
          done,
          raw: p
        };
      });

      const displaySteps = processSteps.length > 6
        ? [...processSteps.slice(0, 6), { operNo: null, processName: '…', percent: 0, done: false, raw: null }]
        : processSteps;

      const lineText = this.buildLineText(processList);

      return {
        ...row,
        processList,
        processSteps: displaySteps,
        overallPlanQty,
        overallOkQty,
        overallProgress,
        lineText
      };
    },
    isProcessDone(process, planQty, okQty, inQty, percent) {
      const derived = this.calcProcessStatus(process);
      if (derived === '工序完成' || derived === '已关闭') return true;

      const status = (process && process.status) || '';
      if (String(status).includes('完工')) return true;
      if (percent != null && Number(percent) >= 100) return true;
      if (planQty > 0 && okQty >= planQty) return true;
      if (planQty > 0 && inQty >= planQty) return true;
      return false;
    },
    normalizePercent(val) {
      if (val == null || val === '') return null;
      const n = Number(val);
      if (!Number.isFinite(n)) return null;
      if (n < 0) return 0;
      if (n > 100) return 100;
      return n;
    },
    buildLineText(processList) {
      const p0 = Array.isArray(processList) && processList.length ? processList[0] : undefined;
      if (p0 && p0.workCenter) return p0.workCenter;
      return '';
    },
    normalizeNumber(val) {
      const n = Number(val);
      return Number.isFinite(n) ? n : 0;
    },
    rowKey(row) {
      return row && (row.id || row.fid || row.billNo || row.moNumber);
    },
    rowClassName({ row }) {
      const key = this.rowKey(row);
      const selectedKeys = new Set(this.selectedRows.map(r => this.rowKey(r)));
      return selectedKeys.has(key) ? 'is-row-checked' : '';
    },
    headerCellStyle() {
      return {
        background: '#e8faf8',
        color: '#303133',
        fontWeight: 600
      };
    },
    clearSelection() {
      if (this.$refs.mainTable) this.$refs.mainTable.clearSelection();
      this.selectedRows = [];
      this.ids = [];
      this.single = true;
      this.multiple = true;
    },
    statusText(row) {
      if (!row) return '-';
      return this.calcOrderStatus(row) || '-';
    },
    filterOrderStatus(value, row) {
      return this.statusText(row) === value;
    },
    statusTagClass(row) {
      const text = this.statusText(row);
      if (text === '未开始') return 'tag-gray';
      if (text === '进行中') return 'tag-green';
      if (text === '预警') return 'tag-orange';
      if (text === '延期') return 'tag-red';
      if (text === '已完成') return 'tag-blue';
      return 'tag-gray';
    },
    calcOrderStatus(row) {
      if (!row) return '';
      const overallProgress = Number(row.overallProgress) || 0;
      if (overallProgress >= 100) return '已完成';

      const risk = this.calcOrderRisk(row);
      if (risk) return risk;

      if (overallProgress > 0) return '进行中';
      return '未开始';
    },
    calcOrderRisk(row) {
      if (!row) return '';
      if (Number(row.overallProgress) >= 100) return '';

      const warnDays = 1;
      const now = Date.now();
      const orderDue = this.toTimestamp(row.planFinishTime);
      const orderRisk = this.calcRiskByDue(orderDue, now, warnDays);

      let processRisk = '';
      const list = Array.isArray(row.processList) ? row.processList : [];
      for (const p of list) {
        const base = this.calcProcessStatus(p);
        if (base === '工序完成' || base === '已转出' || base === '已关闭') continue;
        const due = this.toTimestamp(this.getProcessPlanFinishTime(p));
        const r = this.calcRiskByDue(due, now, warnDays);
        if (r === '延期') {
          processRisk = '延期';
          break;
        }
        if (r === '预警') processRisk = '预警';
      }

      if (orderRisk === '延期' || processRisk === '延期') return '延期';
      if (orderRisk === '预警' || processRisk === '预警') return '预警';
      return '';
    },
    calcRiskByDue(dueTs, nowTs, warnDays) {
      if (!dueTs) return '';
      const diffDays = (dueTs - nowTs) / 86400000;
      if (diffDays < 0) return '延期';
      if (diffDays <= warnDays) return '预警';
      return '';
    },
    toTimestamp(val) {
      if (val == null || val === '') return null;
      if (typeof val === 'number' && Number.isFinite(val)) return val;
      const s = String(val);
      const ts = new Date(s.replace(/-/g, '/')).getTime();
      return Number.isFinite(ts) ? ts : null;
    },
    getProcessPlanStartTime(process) {
      if (!process) return '';
      return process.operPlanStartTime || process.fOperPlanStartTime || process.seqPlanStartTime || process.fSeqPlanStartTime || '';
    },
    getProcessPlanFinishTime(process) {
      if (!process) return '';
      return process.operPlanFinishTime || process.fOperPlanFinishTime || process.seqPlanFinishTime || process.fSeqPlanFinishTime || '';
    },
    typeText(row) {
      return (row && row.typeText) || '正常';
    },
    typeTagType(row) {
      const t = this.typeText(row);
      if (t.includes('自建')) return 'warning';
      return 'success';
    },
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        id: undefined,
        fid: undefined,
        billNo: undefined,
        moNumber: undefined,
        hbytSclh: undefined,
        productCode: undefined,
        productName: undefined,
        proSpecification: undefined,
        planStartTime: undefined,
        planFinishTime: undefined,
        createTime: undefined,
        updateTime: undefined,
        createBy: undefined,
        updateBy: undefined
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.selectedRows = selection;
      this.ids = selection.map(item => item.id).filter(v => v != null);
      this.single = selection.length !== 1;
      this.multiple = !selection.length;
    },
    handleDispatch(row) {
      const code = (row && (row.billNo || row.moNumber)) || '';
      this.$modal.msgSuccess(code ? `已下发：${code}` : '已下发');
    },
    handleMoreCommand(command, row) {
      if (command === 'delete') {
        this.handleDelete(row);
        return;
      }
      if (command === 'viewProcess') {
        this.openProcessDetail(row);
      }
    },
    openProcessDetail(orderRow, step) {
      const list = orderRow && Array.isArray(orderRow.processList) ? orderRow.processList : [];
      const code = (orderRow && (orderRow.billNo || orderRow.moNumber)) || '';
      const batchNo = (orderRow && (orderRow.batchNo || orderRow.hbytSclh)) || '';

      this.processDialogTitle = code ? `工序详情 - ${code}` : '工序详情';
      this.processDialogMeta = batchNo ? `生产令号：${batchNo}` : '';
      this.processDialogOrder = orderRow || null;
      this.processDialogList = this.prepareProcessDialogList(list);
      this.processDialogOpen = true;

      this.$nextTick(() => {
        if (!this.$refs.processTable || !step || !step.raw) return;
        const entryId = step.raw.entityEntryId != null ? String(step.raw.entityEntryId) : '';
        const operNo = step.operNo != null ? String(step.operNo) : '';
        const target = this.processDialogList.find(p => p.entityEntryId != null && String(p.entityEntryId) === entryId)
          || this.processDialogList.find(p => p.operNo != null && String(p.operNo) === operNo);
        if (target) this.$refs.processTable.setCurrentRow(target);
      });
    },
    prepareProcessDialogList(list) {
      const safeList = Array.isArray(list) ? list : [];
      return safeList.map(p => {
        const operator = p && p.operator != null ? String(p.operator) : '';
        const equipment = p && p.equipment != null ? String(p.equipment) : '';
        return {
          ...p,
          __operatorDraft: operator,
          __operatorDirty: false,
          __equipmentDraft: equipment,
          __equipmentDirty: false,
          __equipmentOptions: [],
          __equipmentLoading: false,
          __operatorSaving: false
        };
      });
    },
    processStatusText(process) {
      return this.calcProcessDisplayStatus(process) || '-';
    },
    processStatusClass(process) {
      const text = this.calcProcessDisplayStatus(process);
      if (text === '未开工') return 'tag-gray';
      if (text === '生产中') return 'tag-green';
      if (text === '质检中') return 'tag-blue';
      if (text === '预警') return 'tag-orange';
      if (text === '延期') return 'tag-red';
      if (text === '工序完成' || text === '已转出') return 'tag-blue';
      if (text === '已关闭') return 'tag-gray';
      return 'tag-gray';
    },
    calcProcessDisplayStatus(process) {
      const base = this.calcProcessStatus(process);
      if (base === '工序完成' || base === '已转出' || base === '已关闭') return base;

      const warnDays = 1;
      const now = Date.now();
      const due = this.toTimestamp(this.getProcessPlanFinishTime(process));
      const risk = this.calcRiskByDue(due, now, warnDays);
      if (risk) return risk;

      if (base === '未开工' || base === '生产中' || base === '质检中') return base;
      return base || '';
    },
    calcProcessStatus(process) {
      if (!process) return '';

      const rawK3 = process.k3Status ?? process.operStatus ?? process.k3OperStatus;
      const k3 = rawK3 == null || rawK3 === '' ? 0 : Number(rawK3);
      if (k3 === 5 || k3 === 6) return '已关闭';

      const inQty = this.normalizeNumber(process.inQty);
      const okQty = this.normalizeNumber(process.okQty);
      const ngQty = this.normalizeNumber(process.ngQty);
      const transOutQty = this.normalizeNumber(process.transOutQty);

      if (inQty === 0) return '未开工';
      if (transOutQty > 0) return '已转出';
      if (okQty + ngQty >= inQty) return '工序完成';
      if (okQty + ngQty > 0) return '质检中';
      return '生产中';
    },
    markOperatorDirty(row) {
      if (!row) return;
      const origin = row.operator != null ? String(row.operator) : '';
      const draft = row.__operatorDraft != null ? String(row.__operatorDraft) : '';
      row.__operatorDirty = origin !== draft;
    },
    resetOperatorDraft(row) {
      if (!row) return;
      row.__operatorDraft = row.operator != null ? String(row.operator) : '';
      row.__operatorDirty = false;
    },
    markEquipmentDirty(row) {
      if (!row) return;
      const origin = row.equipment != null ? String(row.equipment) : '';
      const draft = row.__equipmentDraft != null ? String(row.__equipmentDraft) : '';
      row.__equipmentDirty = origin !== draft;
    },
    resetEquipmentDraft(row) {
      if (!row) return;
      row.__equipmentDraft = row.equipment != null ? String(row.equipment) : '';
      row.__equipmentDirty = false;
    },
    handleEquipmentChange(val, row) {
      if (!row) return;
      this.markEquipmentDirty(row);
    },
    buildOperatorUpdatePayload(rows) {
      const order = this.processDialogOrder || {};
      const list = Array.isArray(rows) ? rows : [];

      return {
        orderId: order.id ?? null,
        fid: order.fid ?? null,
        billNo: order.billNo ?? null,
        moNumber: order.moNumber ?? null,
        batchNo: (order.batchNo || order.hbytSclh) ?? null,
        updates: list.map(p => ({
          k3DetailId: p.k3DetailId ?? null,
          fDetailId: p.fDetailId ?? p.FDetailId ?? p.k3DetailId ?? null,
          FDetailId: p.FDetailId ?? p.fDetailId ?? p.k3DetailId ?? null,
          entityEntryId: p.entityEntryId ?? null,
          operNo: p.operNo ?? null,
          processName: p.processName ?? null,
          operator: p.__operatorDraft != null ? String(p.__operatorDraft).trim() : '',
          equipment: p.__equipmentDraft != null ? String(p.__equipmentDraft).trim() : ''
        }))
      };
    },
    submitOperatorUpdates(targetRows) {
      if (this.processDialogSaving) return;
      const rows = Array.isArray(targetRows)
        ? targetRows
        : (this.processDialogList || []).filter(p => p && (p.__operatorDirty || p.__equipmentDirty));

      const realRows = rows.filter(r => {
        const originOperator = r && r.operator != null ? String(r.operator) : '';
        const draftOperator = r && r.__operatorDraft != null ? String(r.__operatorDraft).trim() : '';
        const originEquipment = r && r.equipment != null ? String(r.equipment) : '';
        const draftEquipment = r && r.__equipmentDraft != null ? String(r.__equipmentDraft).trim() : '';
        return originOperator !== draftOperator || originEquipment !== draftEquipment;
      });

      if (!realRows.length) return;

      this.processDialogSaving = true;
      realRows.forEach(r => {
        r.__operatorSaving = true;
      });

      const payload = this.buildOperatorUpdatePayload(realRows);
      updateOperationPlanningMainOperators(payload)
        .then(() => {
          realRows.forEach(r => {
            const nextOperator = r.__operatorDraft != null ? String(r.__operatorDraft).trim() : '';
            const nextEquipment = r.__equipmentDraft != null ? String(r.__equipmentDraft).trim() : '';
            r.operator = nextOperator;
            r.equipment = nextEquipment;
            r.__operatorDraft = nextOperator;
            r.__equipmentDraft = nextEquipment;
            r.__operatorDirty = false;
            r.__equipmentDirty = false;
          });
          this.$modal.msgSuccess('保存成功');
        })
        .finally(() => {
          realRows.forEach(r => {
            r.__operatorSaving = false;
          });
          this.processDialogSaving = false;
        });
    },
    saveOperator(row) {
      if (!row || row.__operatorSaving) return;
      this.markOperatorDirty(row);
      if (!row.__operatorDirty) return;
      this.$modal.msgWarning('已暂存，点击上方“保存”统一提交');
    },
    formatProcessText(row) {
      const list = (row && Array.isArray(row.processList) ? row.processList : []).map(p => {
        const operNo = p.operNo != null ? String(p.operNo) : '-';
        const name = p.processName || '-';
        const status = p.status || '-';
        const okQty = p.okQty != null ? String(p.okQty) : '-';
        const planQty = p.planQty != null ? String(p.planQty) : '-';
        return `${operNo} ${name} | ${status} | ${okQty}/${planQty}`;
      });

      return list.length ? list.join('\n') : '暂无工序数据';
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加工序计划主";
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.loading = true;
      this.reset();
      const id = row.id || this.ids
      getOperationPlanningMain(id).then(response => {
        this.loading = false;
        this.form = response.data;
        this.open = true;
        this.title = "修改工序计划主";
      });
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          this.buttonLoading = true;
          if (this.form.id != null) {
            updateOperationPlanningMain(this.form).then(response => {
              this.$modal.msgSuccess("修改成功");
              this.open = false;
              this.getList();
            }).finally(() => {
              this.buttonLoading = false;
            });
          } else {
            addOperationPlanningMain(this.form).then(response => {
              this.$modal.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            }).finally(() => {
              this.buttonLoading = false;
            });
          }
        }
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const ids = row.id || this.ids;
      this.$modal.confirm('是否确认删除工序计划主编号为"' + ids + '"的数据项？').then(() => {
        this.loading = true;
        return delOperationPlanningMain(ids);
      }).then(() => {
        this.loading = false;
        this.getList();
        this.$modal.msgSuccess("删除成功");
      }).catch(() => {
      }).finally(() => {
        this.loading = false;
      });
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download('system/operationPlanningMain/export', {
        ...this.queryParams
      }, `operationPlanningMain_${new Date().getTime()}.xlsx`)
    }
  }
};
</script>

<style scoped>
.selection-bar {
  display: flex;
  align-items: center;
  height: 36px;
  margin-bottom: 10px;
  padding: 0 12px;
  background: #e8faf8;
  border: 1px solid #d6f5f0;
}

.selection-bar__text {
  color: #303133;
  font-size: 14px;
}

.selection-bar__clear {
  margin-left: 12px;
  color: #00b4ad;
  cursor: pointer;
  font-size: 14px;
}

.tag-green {
  background: #16c784;
  border-color: #16c784;
  color: #ffffff;
}

.tag-purple {
  background: #7c3aed;
  border-color: #7c3aed;
  color: #ffffff;
}

.tag-gray {
  background: #909399;
  border-color: #909399;
  color: #ffffff;
}

.tag-orange {
  background: #f59e0b;
  border-color: #f59e0b;
  color: #ffffff;
}

.tag-red {
  background: #ef4444;
  border-color: #ef4444;
  color: #ffffff;
}

.tag-blue {
  background: #3b82f6;
  border-color: #3b82f6;
  color: #ffffff;
}

.cell-main {
  line-height: 18px;
}

.cell-title {
  color: #303133;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cell-title--wrap {
  white-space: normal;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.cell-sub {
  color: #909399;
  font-size: 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.proc {
  display: flex;
  align-items: center;
  min-height: 46px;
}

.proc-steps {
  display: flex;
  align-items: flex-start;
}

.proc-step {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.proc-step-top {
  display: flex;
  align-items: center;
}


.proc-circle {
  position: relative;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.proc-circle.clickable {
  cursor: pointer;
}

.proc-circle__text {
  position: absolute;
  left: 0;
  top: 0;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #00b4ad;
  font-size: 10px;
  font-weight: 600;
  user-select: none;
}

.proc-circle__text.done {
  color: #16c784;
  font-size: 16px;
}

.proc-circle.ellipsis .proc-circle__text {
  font-size: 18px;
  line-height: 1;
  color: #c0c4cc;
}

.proc-line {
  width: 26px;
  height: 2px;
  background: #dcdfe6;
  margin: 0 6px;
}

.proc-line.done {
  background: #16c784;
}

.proc-step-name {
  margin-top: 6px;
  max-width: 84px;
  font-size: 12px;
  color: #303133;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: center;
}

.progress-cell {
  display: flex;
  align-items: center;
}

.progress-text {
  margin-left: 8px;
  color: #606266;
  font-size: 12px;
}

.more-trigger {
  cursor: pointer;
  color: #00b4ad;
  font-size: 12px;
}

.is-row-checked td {
  background: #e8faf8 !important;
}

.process-drawer ::v-deep .el-drawer__body {
  padding: 12px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.process-drawer__toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}

.process-drawer__title-main {
  font-size: 20px;
  font-weight: 800;
  color: #303133;
}

.process-drawer__title-line {
  display: flex;
  align-items: baseline;
  gap: 14px;
  width: 100%;
  overflow: hidden;
  white-space: nowrap;
}

.process-drawer__title-kv {
  font-size: 14px;
  font-weight: 600;
  color: #606266;
}

.process-drawer__title-product {
  min-width: 0;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
}

.process-drawer__dirty {
  color: #606266;
  font-size: 12px;
}

.process-drawer__table {
  flex: 1;
  min-height: 0;
}

.operator-cell {
  display: flex;
  align-items: center;
  gap: 6px;
}
</style>
