<template>
  <div class="app-container op-plan-page">
    <div v-show="showSearch" class="search-panel">
      <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" 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-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>
    </div>

    <div class="toolbar-row">
      <el-button
        type="warning"
        plain
        icon="el-icon-download"
        size="mini"
        @click="handleExportProgress"
        v-hasPermi="['system:operationPlanningMain:export']"
      >导出进度</el-button>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
    </div>

    <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"
      class="main-table"
      :data="operationPlanningMainList"
      :row-key="rowKey"
      :row-class-name="rowClassName"
      :header-cell-style="headerCellStyle"
      border
      size="small"
      @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="140">
        <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="产品主数据" min-width="280">
        <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="80">
        <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="92" align="center" column-key="overallStatus" :filters="statusFilters" :filter-method="filterOrderStatus">
        <template slot-scope="scope">
          <el-tag size="mini" effect="plain" :class="overallStatusTagClass(scope.row)">{{ overallStatusText(scope.row) }}</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="进度" width="140" align="center">
        <template slot-scope="scope">
          <div class="progress-cell">
            <el-progress
              class="progress-cell__bar"
              :percentage="scope.row.overallProgress"
              :stroke-width="8"
              :show-text="false"
              :color="'#409eff'"
            />
            <span class="progress-text">{{ scope.row.overallProgress }}%</span>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="工序进度" min-width="440">
        <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 === '…',
                        'proc-circle--idle': p.phase === 'idle',
                        'proc-circle--active': p.phase === 'active',
                        'proc-circle--done': p.phase === 'done'
                      }"
                      @click="openProcessDetail(scope.row, p)"
                    >
                      <el-progress
                        type="circle"
                        :percentage="p.displayPercent"
                        :width="32"
                        :stroke-width="4"
                        :show-text="false"
                        :color="procCircleColor(p)"
                      />
                      <span class="proc-circle__text" :class="'proc-circle__text--' + p.phase">
                        <i v-if="p.phase === 'done'" class="el-icon-check"></i>
                        <span v-else-if="p.phase === 'active'">{{ p.activeLabel }}</span>
                        <span v-else-if="p.raw">{{ p.displayPercent }}%</span>
                        <span v-else>…</span>
                      </span>
                    </span>
                    <span v-if="idx < scope.row.processSteps.length - 1" class="proc-line" :class="{ done: p.phase === 'done' }"></span>
                  </div>
                  <div class="proc-step-name">{{ p.processName || '-' }}</div>
                </div>
              </div>
            </template>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="计划开始 / 结束" width="168" align="center">
        <template slot-scope="scope">
          <div class="plan-time-cell">
            <span>{{ formatPlanDateTime(scope.row.planStartTime) }}</span>
            <span class="plan-time-cell__sep">~</span>
            <span>{{ formatPlanDateTime(scope.row.planFinishTime) }}</span>
          </div>
        </template>
      </el-table-column>
      <el-table-column label="数量" width="90" align="center">
        <template slot-scope="scope">
          <span class="num-text">{{ scope.row.overallPlanQty }}</span>
        </template>
      </el-table-column>
      <el-table-column label="已完成" width="90" align="center">
        <template slot-scope="scope">
          <span class="num-text">{{ scope.row.overallOkQty }}</span>
        </template>
      </el-table-column>

      <template slot="empty">
        <el-empty description="暂无数据" :image-size="64" />
      </template>
    </el-table>

    <pagination
      v-show="total>0"
      :total="total"
      :page.sync="queryParams.pageNum"
      :limit.sync="queryParams.pageSize"
      :page-sizes="[10, 20, 50, 100, 200, 500, 1000]"
      @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
                multiple
                collapse-tags
                collapse-tags-tooltip
                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>{{ formatDateOnly(getProcessPlanStartTime(scope.row)) || '-' }}</span>
          </template>
        </el-table-column>
        <el-table-column label="计划完成" width="160">
          <template slot-scope="scope">
            <span>{{ formatDateOnly(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: '已完工' }
      ],
      // 工序计划主表格数据
      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();
  },
  watch: {
    '$route.query': {
      handler() {
        this.initFromRoute();
      },
      deep: true
    }
  },
  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;
    },
    formatPlanDateTime(val) {
      return this.parseTime(val, '{y}-{m}-{d} {h}:{i}:{s}') || '-';
    },
    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;
      this.queryParams.hbytSclh = batchNo || undefined;
      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 rawOverallPlanQty = this.normalizeNumber(
        (finalProcess && finalProcess.planQty) ?? (processList.find(p => p.planQty != null) || {}).planQty
      );

      for (let i = 0; i < processList.length; i++) {
        const p = processList[i];
        const prev = i > 0 ? processList[i - 1] : null;
        const effPlan = this.getProcessEffectivePlanQty(p, prev, rawOverallPlanQty);
        if (effPlan != null) p.__effectivePlanQty = effPlan;
        const effIn = this.getProcessEffectiveInQty(p, prev, rawOverallPlanQty);
        if (effIn != null) p.__effectiveInQty = effIn;
      }

      const overallPlanQty = row.overallPlanQty != null && row.overallPlanQty !== ''
        ? this.normalizeNumber(row.overallPlanQty)
        : this.normalizeNumber(
            (finalProcess && finalProcess.__effectivePlanQty != null) ? finalProcess.__effectivePlanQty : rawOverallPlanQty
          );
      const overallOkQty = row.overallOkQty != null && row.overallOkQty !== ''
        ? this.normalizeNumber(row.overallOkQty)
        : this.normalizeNumber((finalProcess && finalProcess.okQty) ?? 0);
      const backendOverallProgress = this.normalizePercent(row.overallProgress);
      const overallProgress = backendOverallProgress != null
        ? Math.round(backendOverallProgress)
        : (overallPlanQty > 0 ? Math.min(100, Math.round((overallOkQty / overallPlanQty) * 100)) : 0);

      const processSteps = processList.map(p => {
        const planQty = this.normalizeNumber(p.__effectivePlanQty != null ? p.__effectivePlanQty : (p.planQty || overallPlanQty));
        const okQty = this.normalizeNumber(p.okQty);
        const inQty = this.normalizeNumber(p.__effectiveInQty != null ? p.__effectiveInQty : p.inQty);
        const ngQty = this.normalizeNumber(p.ngQty);
        const transOutQty = this.normalizeNumber(p.transOutQty);
        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 phase = this.calcProcessStepPhase(p, planQty, okQty, inQty, ngQty, transOutQty);
        const displayPercent = this.calcStepDisplayPercent(phase, planQty, okQty, inQty, ngQty, percent);
        const activeLabel = this.calcStepActiveLabel(planQty, okQty, inQty, ngQty, displayPercent);

        return {
          operNo: p.operNo,
          entityEntryId: p.entityEntryId,
          processName: p.processName,
          percent,
          phase,
          displayPercent,
          activeLabel,
          raw: p
        };
      });

      const lineText = this.buildLineText(processList);

      return {
        ...row,
        processList,
        processSteps,
        overallPlanQty,
        overallOkQty,
        overallProgress,
        lineText
      };
    },
    calcProcessStepPhase(process, planQty, okQty, inQty, ngQty, transOutQty) {
      const status = this.calcProcessStatus(process);
      if (status === '已转出' || status === '工序完成' || status === '已关闭') return 'done';
      if (transOutQty > 0) return 'done';
      if (planQty > 0 && okQty >= planQty) return 'done';
      if (inQty === 0) return 'idle';
      return 'active';
    },
    calcStepDisplayPercent(phase, planQty, okQty, inQty, ngQty, percent) {
      if (phase === 'done') return 100;
      if (phase === 'idle') return 0;
      if (planQty > 0) {
        const processed = okQty + ngQty;
        if (processed > 0) return Math.min(99, Math.round((processed / planQty) * 100));
        return Math.min(40, Math.max(12, Math.round((inQty / planQty) * 100)));
      }
      return Math.min(99, Math.max(12, Number(percent) || 12));
    },
    calcStepActiveLabel(planQty, okQty, inQty, ngQty, displayPercent) {
      const processed = okQty + ngQty;
      if (processed > 0 && planQty > 0) {
        return `${Math.min(99, Math.round((processed / planQty) * 100))}%`;
      }
      return '入';
    },
    procCircleColor(step) {
      if (!step) return '#c0c4cc';
      if (step.phase === 'done') return '#67c23a';
      if (step.phase === 'active') return '#e6a23c';
      return '#c0c4cc';
    },
    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: '#fafafa',
        color: '#606266',
        fontWeight: 500
      };
    },
    clearSelection() {
      if (this.$refs.mainTable) this.$refs.mainTable.clearSelection();
      this.selectedRows = [];
      this.ids = [];
      this.single = true;
      this.multiple = true;
    },
    overallStatusText(row) {
      if (!row) return '-';
      return row.overallStatus || '-';
    },
    filterOrderStatus(value, row) {
      return this.overallStatusText(row) === value;
    },
    overallStatusTagClass(row) {
      const text = this.overallStatusText(row);
      if (text === '未开工') return 'tag-gray';
      if (text === '进行中') return 'tag-green';
      if (text === '已延期') return 'tag-red';
      if (text === '已完工') return 'tag-blue';
      return 'tag-gray';
    },
    statusText(row) {
      return this.overallStatusText(row);
    },
    statusTagClass(row) {
      return this.overallStatusTagClass(row);
    },
    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;
    },
    getProcessEffectivePlanQty(process, prevProcess, overallPlanQty) {
      const planQty = this.normalizeNumber(process && (process.planQty || overallPlanQty));
      const prevOut = this.normalizeNumber(prevProcess && prevProcess.transOutQty);
      const prevOk = this.normalizeNumber(prevProcess && prevProcess.okQty);
      const cap = prevOut > 0 ? prevOut : (prevOk > 0 ? prevOk : 0);
      if (cap <= 0) return null;
      if (planQty > 0) return Math.min(planQty, cap);
      return cap;
    },
    getProcessEffectiveInQty(process, prevProcess, overallPlanQty) {
      const inQty = this.normalizeNumber(process && process.inQty);
      const planQty = this.normalizeNumber(process && ((process.__effectivePlanQty != null ? process.__effectivePlanQty : process.planQty) || overallPlanQty));
      const prevOut = this.normalizeNumber(prevProcess && prevProcess.transOutQty);
      const prevOk = this.normalizeNumber(prevProcess && prevProcess.okQty);
      const cap = prevOut > 0 ? prevOut : (prevOk > 0 ? prevOk : 0);
      if (cap <= 0) return null;
      if (inQty > 0) return Math.min(inQty, cap);
      if (planQty > 0) return Math.min(planQty, cap);
      return cap;
    },
    formatDateOnly(val) {
      if (val == null || val === '') return '';
      if (typeof val === 'string') {
        const m = val.match(/\d{4}-\d{2}-\d{2}/);
        if (m) return m[0];
      }

      const ts = this.toTimestamp(val);
      if (!ts) return '';
      const d = new Date(ts);
      const y = d.getFullYear();
      const mm = String(d.getMonth() + 1).padStart(2, '0');
      const dd = String(d.getDate()).padStart(2, '0');
      return `${y}-${mm}-${dd}`;
    },
    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);
      });
    },
    openCapacityAnalysisByOrder(row) {
      // 从主表列表直接打开独立的产能分析页面，通过路由 query 传参
      const productCode = row.productCode || '';
      const productName = row.productName || '';
      const productionOrderNo = row.hbytSclh || row.batchNo || row.moNumber || '';
      
      this.$router.push({
        path: '/system/capacity-analysis/index',
        query: { productionOrderNo, productCode, productName }
      });
    },
    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: this.parseOperatorDraft(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.__effectiveInQty != null ? process.__effectiveInQty : 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 = this.normalizeOperatorText(row.operator);
      const draft = this.normalizeOperatorText(this.formatOperatorDraft(row.__operatorDraft));
      row.__operatorDirty = origin !== draft;
    },
    resetOperatorDraft(row) {
      if (!row) return;
      row.__operatorDraft = this.parseOperatorDraft(row.operator != null ? String(row.operator) : '');
      row.__operatorDirty = false;
    },
    parseOperatorDraft(text) {
      const raw = this.normalizeOperatorText(text);
      if (!raw) return [];
      return raw
        .split(/[、,，;；\s]+/)
        .map(s => (s || '').trim())
        .filter(Boolean);
    },
    formatOperatorDraft(draft) {
      if (Array.isArray(draft)) {
        return draft.map(s => (s || '').trim()).filter(Boolean).join('、');
      }
      return draft != null ? String(draft).trim() : '';
    },
    normalizeOperatorText(text) {
      return (text != null ? String(text) : '')
        .trim()
        .replace(/\s+/g, '')
        .replace(/[、,，;；]+/g, '、')
        .replace(/^、+|、+$/g, '');
    },
    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: this.formatOperatorDraft(p.__operatorDraft),
          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 = this.normalizeOperatorText(r && r.operator != null ? String(r.operator) : '');
        const draftOperator = this.normalizeOperatorText(this.formatOperatorDraft(r && r.__operatorDraft));
        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 = this.formatOperatorDraft(r.__operatorDraft);
            const nextEquipment = r.__equipmentDraft != null ? String(r.__equipmentDraft).trim() : '';
            r.operator = nextOperator;
            r.equipment = nextEquipment;
            r.__operatorDraft = this.parseOperatorDraft(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`)
    },
    /** 按生产令号分 Sheet 导出未完工工序进度 */
    handleExportProgress() {
      this.download('system/operationPlanningMain/exportCascadeProgress', {
        ...this.queryParams
      }, `工序进度_${new Date().getTime()}.xlsx`)
    }
  }
};
</script>

<style scoped>
.op-plan-page {
  --text: #303133;
  --text-secondary: #909399;
  --border: #ebeef5;
  --bg-muted: #f5f7fa;
  --accent: #409eff;
}

.search-panel {
  margin-bottom: 12px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
}

.search-panel ::v-deep .el-form-item {
  margin-bottom: 12px;
}

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

.selection-bar {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
  padding: 8px 12px;
  font-size: 13px;
  background: var(--bg-muted);
  border-radius: 4px;
}

.selection-bar__text {
  color: var(--text);
}

.selection-bar__clear {
  margin-left: 12px;
  color: var(--accent);
  cursor: pointer;
}

.selection-bar__clear:hover {
  opacity: 0.8;
}

.main-table {
  width: 100%;
}

.tag-green {
  background: #67c23a;
  border-color: #67c23a;
  color: #fff;
}

.tag-purple {
  background: #909399;
  border-color: #909399;
  color: #fff;
}

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

.tag-orange {
  background: #e6a23c;
  border-color: #e6a23c;
  color: #fff;
}

.tag-red {
  background: #f56c6c;
  border-color: #f56c6c;
  color: #fff;
}

.tag-blue {
  background: #409eff;
  border-color: #409eff;
  color: #fff;
}

.cell-main {
  line-height: 1.5;
}

.cell-title {
  color: var(--text);
  font-size: 13px;
}

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

.cell-sub {
  margin-top: 2px;
  color: var(--text-secondary);
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.plan-time-cell {
  font-size: 12px;
  color: var(--text);
  line-height: 1.6;
}

.plan-time-cell__sep {
  margin: 0 4px;
  color: #c0c4cc;
}

.num-text {
  font-variant-numeric: tabular-nums;
}

.proc {
  display: flex;
  align-items: center;
  min-height: 44px;
  overflow-x: auto;
}

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

.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.clickable:hover {
  opacity: 0.85;
}

.proc-circle__text {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 500;
  user-select: none;
}

.proc-circle__text--idle {
  color: #c0c4cc;
}

.proc-circle__text--active {
  color: #e6a23c;
  font-size: 11px;
  font-weight: 600;
}

.proc-circle__text--done {
  color: #67c23a;
  font-size: 14px;
}

.proc-circle.ellipsis .proc-circle__text {
  color: #c0c4cc;
  font-size: 16px;
}

.proc-line {
  width: 24px;
  height: 1px;
  margin: 0 4px;
  background: var(--border);
}

.proc-line.done {
  background: #67c23a;
}

.proc-step-name {
  margin-top: 4px;
  max-width: 72px;
  font-size: 11px;
  color: var(--text-secondary);
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

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

.progress-cell__bar {
  width: 64px;
  flex-shrink: 0;
}

.progress-cell ::v-deep .el-progress-bar {
  padding-right: 0;
  margin-right: 0;
}

.progress-text {
  margin-left: 6px;
  font-size: 12px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

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

.process-drawer ::v-deep .el-drawer__header {
  margin-bottom: 0;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}

.process-drawer ::v-deep .el-drawer__body {
  padding: 12px 16px;
  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: 16px;
  font-weight: 500;
  color: var(--text);
}

.process-drawer__title-line {
  display: flex;
  align-items: center;
  gap: 12px;
  overflow: hidden;
  white-space: nowrap;
}

.process-drawer__title-kv {
  font-size: 13px;
  color: var(--text-secondary);
}

.process-drawer__title-kv + .process-drawer__title-kv::before {
  content: '·';
  margin-right: 12px;
  color: #dcdfe6;
}

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

.process-drawer__dirty {
  font-size: 12px;
  color: var(--text-secondary);
}

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

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