<template>
  <div class="aps-page">
    <div class="aps-calendar-flat">
      <aps-factory-calendar-board :embedded="true" :height="360" :external-query="calendarQuery" />
    </div>
    <!-- 顶部：计划操作区 -->
    <el-card class="aps-header" shadow="never">
      <div class="aps-header-main">
        <div class="aps-header-left">
          <div class="aps-header-sub">
            一键完成产品直排或库存订单排产，支持按产线进行调度
          </div>
          <div class="aps-header-current" v-if="form.orderCode || form.orderId">
            当前库存订单：{{ form.orderCode || form.orderId }}
          </div>
        </div>
        <div class="aps-header-actions">
          <el-button size="small" type="primary" plain icon="el-icon-date" @click="$router.push('/aps/factory-calendar')">工厂总日历</el-button>
        </div>
      </div>
      <div class="aps-header-filter">
        <div class="dispatch-panel">
          <div class="dispatch-left">
            <div class="dispatch-title">快速排产</div>
            <div class="dispatch-source-row">
              <el-radio-group v-model="quick.sourceType" size="small">
                <el-radio-button label="product">产品直排</el-radio-button>
                <el-radio-button label="wmsOrder">库存订单排产</el-radio-button>
              </el-radio-group>
            </div>
            <div class="dispatch-form-row" v-if="quick.sourceType === 'product'">
              <el-input :value="quick.productName || quick.productId" placeholder="请选择产品" readonly style="width: 220px" />
              <el-button size="mini" @click="openProductSelector('quick')">选择产品</el-button>
              <el-input-number v-model="quick.productQty" :min="1" :step="1" style="width: 130px" />
              <el-button size="mini" type="primary" plain @click="addQuickProductItem">加入清单</el-button>
            </div>

            <div class="dispatch-form-row" v-else>
              <el-input :value="quick.orderCode || quick.orderId" placeholder="请在右侧库存订单池点击选择" readonly style="width: 260px" />
              <el-tag v-if="quick.orderId" type="success" size="mini">已选库存订单</el-tag>
            </div>
            <div class="dispatch-form-row">
              <el-select
                v-model="quick.lineId"
                placeholder="选择目标产线（必选）"
                clearable
                filterable
                :loading="lineLoading"
                style="width: 220px"
                @visible-change="onQuickLineVisible"
                @change="onLineSelected"
              >
                <el-option v-for="line in lineOptions" :key="line.lineId" :label="line.lineName || line.lineCode || ('编号' + line.lineId)" :value="line.lineId" />
              </el-select>
              <el-date-picker
                v-model="quick.range"
                type="datetimerange"
                start-placeholder="开始时间"
                end-placeholder="结束时间"
                range-separator="至"
                value-format="yyyy-MM-dd HH:mm:ss"
                :default-time="['00:00:00', '23:59:59']"
                style="width: 360px"
              />
              <el-button type="primary" icon="el-icon-magic-stick" :loading="loading.oneKey" @click="handleQuickSchedule">一键排产并查看</el-button>
            </div>
          </div>
          <div class="dispatch-right">
            <template v-if="quick.sourceType === 'wmsOrder'">
              <div class="pending-order-head">
                <span class="pending-order-title">库存订单池（未完成）</span>
                <el-input v-model="pendingOrderKeyword" size="mini" clearable placeholder="订单号/客户" style="width: 180px" @keyup.enter.native="loadPendingOrders" />
                <el-button size="mini" @click="loadPendingOrders">刷新</el-button>
              </div>
              <div class="pending-order-list" v-loading="pendingOrdersLoading">
                <el-empty v-if="!pendingOrderList.length" :image-size="40" description="暂无未完成库存订单" />
                <el-tag
                  v-for="item in pendingOrderList"
                  :key="item.orderId"
                  size="small"
                  :type="String(quick.orderId) === String(item.orderId) ? 'primary' : 'info'"
                  effect="plain"
                  class="pending-order-tag"
                  @click="pickPendingOrder(item)"
                >
                  {{ item.orderCode || item.orderId }}
                </el-tag>
              </div>
            </template>
            <template v-else>
              <div class="pending-order-head">
                <span class="pending-order-title">已选产品清单</span>
              </div>
              <div class="product-pool-list product-pool-inline">
                <el-empty v-if="!quick.productItems.length" :image-size="40" description="暂无已选产品" />
                <div class="product-card product-card-inline" v-for="(p, idx) in quick.productItems" :key="p.productId + '_picked_' + idx">
                  <div class="p-inline-main">
                    <span class="p-title">{{ p.productName || p.productId }}</span>
                    <span class="p-sub">规格：{{ p.specification || '-' }}</span>
                    <span class="p-sub">材质：{{ p.material || '-' }}</span>
                  </div>
                  <div class="p-inline-op">
                    <el-input-number v-model="p.quantity" :min="1" :step="1" size="mini" style="width: 94px" />
                    <el-button type="text" size="mini" :disabled="idx===0" @click="moveQuickProductItem(idx,-1)">↑</el-button>
                    <el-button type="text" size="mini" :disabled="idx===quick.productItems.length-1" @click="moveQuickProductItem(idx,1)">↓</el-button>
                    <el-button type="text" size="mini" @click="removeQuickProductItem(idx)">移除</el-button>
                  </div>
                </div>
              </div>
            </template>
          </div>
        </div>

        <el-form :inline="true" size="small" :model="form">
          <el-form-item label="时间范围">
            <el-date-picker
              v-model="form.range"
              type="datetimerange"
              start-placeholder="开始时间"
              end-placeholder="结束时间"
              range-separator="至"
              value-format="yyyy-MM-dd HH:mm:ss"
              :default-time="['00:00:00', '23:59:59']"
              style="width: 380px"
              @change="handleQueryGantt"
            />
          </el-form-item>
          <el-form-item>
            <el-button icon="el-icon-refresh" @click="resetFilter">重置</el-button>
          </el-form-item>
        </el-form>
      </div>

      <template v-if="ganttList && ganttList.length">
        <div class="result-toolbar">
          <el-button size="small" type="primary" icon="el-icon-download" @click="goSheet">下载排产表</el-button>
        </div>
        <div class="result-head">排产甘特图</div>
        <div class="gantt-wrap" v-loading="loading.gantt">
          <div class="gantt-axis">
            <span>{{ ganttChartStartLabel }}</span>
            <span>{{ ganttChartEndLabel }}</span>
          </div>
          <div class="gantt-line" v-for="line in ganttLineRows" :key="line.lineId || line.lineName">
            <div class="gantt-line-name">{{ line.lineName }}</div>
            <div class="gantt-track">
              <div
                class="gantt-bar"
                v-for="task in line.tasks"
                :key="task.operationId || (task.startTime + '_' + task.endTime + '_' + task.productName)"
                :style="ganttBarStyle(task)"
                :title="ganttTaskLabel(task) + '  ' + (task.startTime || '') + ' ~ ' + (task.endTime || '')"
                @click="openReschedule(task)"
              >
                {{ ganttTaskLabel(task) }}
              </div>
            </div>
          </div>
        </div>
      </template>
      <template v-else>
        <div class="aps-empty">
          <el-empty>
            <template slot="description">
              <p>当前条件下暂无排产记录。</p>
              <p>如需为加急订单排产，可先执行自动排产或在生成结果后通过“重排”进行手动调整。</p>
            </template>
            <el-button
              v-if="form.orderId"
              type="primary"
              size="mini"
              :loading="loading.oneKey"
              @click="handleOneKeySchedule"
            >
              为当前订单生成排产
            </el-button>
          </el-empty>
        </div>
      </template>
    </el-card>

    <!-- 创建计划弹窗 -->
    <el-dialog
      title="创建排产计划"
      :visible.sync="createDialog.visible"
      width="520px"
    >
      <el-form
        ref="createFormRef"
        :model="createDialog.form"
        :rules="createRules"
        label-width="90px"
        size="small"
      >
        <el-form-item label="排产来源">
          <el-radio-group v-model="createDialog.form.sourceType">
            <el-radio label="product">产品直排</el-radio>
            <el-radio label="wmsOrder">库存订单排产</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item v-if="createDialog.form.sourceType === 'product'" label="产品" prop="productId">
          <el-input :value="createDialog.form.productName || createDialog.form.productId" placeholder="请选择产品" readonly style="width: calc(100% - 88px); margin-right: 8px" />
          <el-button @click="openProductSelector('create')">选择</el-button>
        </el-form-item>
        <el-form-item v-if="createDialog.form.sourceType === 'product'" label="排产数量" prop="productQty">
          <el-input-number v-model="createDialog.form.productQty" :min="1" :step="1" style="width: 100%" />
        </el-form-item>
        <el-form-item v-if="createDialog.form.sourceType === 'wmsOrder'" label="库存订单" prop="orderId">
          <el-input :value="createDialog.form.orderCode || createDialog.form.orderId" placeholder="请选择库存订单" readonly style="width: calc(100% - 88px); margin-right: 8px" />
          <el-button @click="openOrderSelector('create')">选择</el-button>
        </el-form-item>
        <el-form-item label="目标产线" prop="lineId">
          <el-select v-model="createDialog.form.lineId" placeholder="请选择产线" clearable filterable style="width: 100%">
            <el-option v-for="line in lineOptions" :key="line.lineId" :label="line.lineName || line.lineCode || ('产线' + line.lineId)" :value="line.lineId" />
          </el-select>
        </el-form-item>
        <el-form-item label="优先级">
          <el-select v-model="createDialog.form.priority" placeholder="默认：中">
            <el-option :value="0" label="低" />
            <el-option :value="1" label="中" />
            <el-option :value="2" label="高" />
            <el-option :value="3" label="VIP" />
          </el-select>
        </el-form-item>
        <el-form-item label="计划时间">
          <el-date-picker
            v-model="createDialog.form.range"
            type="datetimerange"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            range-separator="至"
            value-format="yyyy-MM-dd HH:mm:ss"
            :default-time="['00:00:00', '23:59:59']"
            style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="备注">
          <el-input
            v-model="createDialog.form.remark"
            type="textarea"
            :rows="3"
            placeholder="可选"
          />
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button size="small" @click="createDialog.visible = false">取 消</el-button>
        <el-button size="small" type="primary" @click="submitCreatePlan">确 定</el-button>
      </span>
    </el-dialog>

    <el-dialog title="选择库存订单" :visible.sync="orderSelector.visible" width="980px" append-to-body>
      <el-form :inline="true" size="small">
        <el-form-item label="订单关键字">
          <el-input v-model="orderSelector.keyword" placeholder="输入库存订单号/客户名称" @keyup.enter.native="fetchOrderOptions" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" size="mini" @click="fetchOrderOptions">搜索</el-button>
        </el-form-item>
      </el-form>
      <el-table v-loading="orderSelector.loading" :data="orderSelector.list" size="mini" border height="360">
        <el-table-column prop="orderCode" label="库存订单号" min-width="150" />
        <el-table-column prop="customerName" label="客户" min-width="140" />
        <el-table-column prop="orderStatus" label="状态" width="90">
          <template slot-scope="scope">
            <el-tag v-if="Number(scope.row.orderStatus) === 0" size="mini" type="info">新建</el-tag>
            <el-tag v-else-if="Number(scope.row.orderStatus) === 1" size="mini" type="warning">生产中</el-tag>
            <el-tag v-else-if="Number(scope.row.orderStatus) === 2" size="mini" type="success">已完成</el-tag>
            <el-tag v-else size="mini">{{ scope.row.orderStatus }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="90" fixed="right">
          <template slot-scope="scope">
            <el-button size="mini" type="text" @click="selectOrder(scope.row)">选择</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="orderSelector.total > 0" :total="orderSelector.total" :page.sync="orderSelector.pageNum" :limit.sync="orderSelector.pageSize" @pagination="fetchOrderOptions" />
    </el-dialog>

    <el-dialog title="选择产品" :visible.sync="productSelector.visible" width="980px" append-to-body>
      <el-form :inline="true" size="small">
        <el-form-item label="产品关键字">
          <el-input v-model="productSelector.keyword" placeholder="输入产品名称/编号" @keyup.enter.native="fetchProductOptions" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" size="mini" @click="fetchProductOptions">搜索</el-button>
        </el-form-item>
      </el-form>
      <div class="product-dialog-toolbar">
        <el-button size="mini" type="primary" plain :disabled="!selectedProductRows.length" @click="batchAddSelectedProducts">批量加入清单</el-button>
        <span class="dialog-tip">已选 {{ selectedProductRows.length }} 条</span>
      </div>
      <el-table v-loading="productSelector.loading" :data="productSelector.list" size="mini" border @selection-change="onProductSelectionChange">
        <el-table-column type="selection" width="45" />
        <el-table-column prop="productCode" label="产品编号" min-width="120" />
        <el-table-column prop="productName" label="产品名称" min-width="150" />
        <el-table-column prop="material" label="材质" min-width="100" show-overflow-tooltip>
          <template slot-scope="scope">{{ scope.row.material || '-' }}</template>
        </el-table-column>
        <el-table-column prop="specification" label="规格" min-width="120" show-overflow-tooltip />
        <el-table-column prop="unit" label="单位" width="70">
          <template slot-scope="scope">{{ scope.row.unit || '-' }}</template>
        </el-table-column>
        <el-table-column label="操作" width="90" fixed="right">
          <template slot-scope="scope">
            <el-button size="mini" type="text" @click="selectProduct(scope.row)">选择</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="productSelector.total > 0" :total="productSelector.total" :page.sync="productSelector.pageNum" :limit.sync="productSelector.pageSize" @pagination="fetchProductOptions" />
    </el-dialog>

    <!-- 重排弹窗 -->
    <el-dialog
      title="单工序重排"
      :visible.sync="rescheduleDialog.visible"
      width="520px"
    >
      <div class="dialog-tip">
        手动排产步骤：1）选择目标产线；2）选择目标时间范围；3）填写调整原因并确认。
      </div>
      <el-form
        ref="rescheduleFormRef"
        :model="rescheduleDialog.form"
        :rules="rescheduleRules"
        label-width="110px"
        size="small"
      >
        <el-form-item label="当前产线">
          <span>{{ rescheduleDialog.meta.lineName }}</span>
        </el-form-item>
        <el-form-item label="目标产线" prop="targetLineId">
          <el-select
            v-model="rescheduleDialog.form.targetLineId"
            placeholder="选择产线"
            filterable
            @change="onLineSelected"
          >
            <el-option
              v-for="line in lineOptions"
              :key="line.lineId"
              :label="line.lineName || ('产线' + line.lineId)"
              :value="line.lineId"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="目标时间" prop="range">
          <el-date-picker
            v-model="rescheduleDialog.form.range"
            type="datetimerange"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            range-separator="至"
            value-format="yyyy-MM-dd HH:mm:ss"
            :default-time="['00:00:00', '23:59:59']"
            style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="重排原因" prop="reason">
          <el-input
            v-model="rescheduleDialog.form.reason"
            type="textarea"
            :rows="3"
          />
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button size="small" @click="rescheduleDialog.visible = false">取 消</el-button>
        <el-button size="small" type="primary" @click="submitReschedule">确 定</el-button>
      </span>
    </el-dialog>

    <!-- 锁定/解锁弹窗 -->
    <el-dialog
      title="锁定时间窗 / 工序"
      :visible.sync="lockDialog.visible"
      width="520px"
    >
      <el-form
        ref="lockFormRef"
        :model="lockDialog.form"
        :rules="lockRules"
        label-width="110px"
        size="small"
      >
        <el-form-item label="锁定类型" prop="lockType">
          <el-radio-group v-model="lockDialog.form.lockType">
            <el-radio :label="3">工序</el-radio>
            <el-radio :label="2">产线时间窗</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="计划编号">
          <span>{{ form.planId || '-' }}</span>
        </el-form-item>
        <el-form-item label="产线">
          <span>{{ lockDialog.meta.lineName }}</span>
        </el-form-item>
        <el-form-item
          v-if="lockDialog.form.lockType === 2"
          label="锁定时间"
          prop="range"
        >
          <el-date-picker
            v-model="lockDialog.form.range"
            type="datetimerange"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            range-separator="至"
            value-format="yyyy-MM-dd HH:mm:ss"
            :default-time="['00:00:00', '23:59:59']"
            style="width: 100%"
          />
        </el-form-item>
        <el-form-item label="锁定原因" prop="lockReason">
          <el-input
            v-model="lockDialog.form.lockReason"
            type="textarea"
            :rows="3"
          />
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button size="small" @click="lockDialog.visible = false">取 消</el-button>
        <el-button size="small" type="primary" @click="submitLock">确 定</el-button>
      </span>
    </el-dialog>

    <!-- 解锁确认弹窗 -->
    <el-dialog
      title="解锁提示"
      :visible.sync="unlockDialog.visible"
      width="420px"
    >
      <div>确定要解除该工序/时间窗的锁定吗？</div>
      <span slot="footer" class="dialog-footer">
        <el-button size="small" @click="unlockDialog.visible = false">取 消</el-button>
        <el-button size="small" type="primary" @click="submitUnlock">解 锁</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import { convertFromProducts, createPlan, autoSchedule, publishPlan, fetchGantt, rescheduleOperation, createLock, releaseLock } from '@/api/aps/aps'
import { listProductionLine } from '@/api/wms/productionLine'
import { listOrder as listWmsOrder } from '@/api/wms/order'
import { listProduct } from '@/api/wms/product'
import { parseTime } from '@/utils/klp'
import ApsFactoryCalendarBoard from './calendar/index.vue'

export default {
  name: 'ApsPlanGantt',
  components: {
    ApsFactoryCalendarBoard
  },
  data() {
    return {
      loading: {
        gantt: false,
        creating: false,
        auto: false,
        publish: false,
        oneKey: false
      },
      form: {
        planId: '',
        orderId: '',
        orderCode: '',
        lineId: null,
        range: []
      },
      lineOptions: [],
      lineLoading: false,
      orderSelectorTarget: 'filter',
      orderSelector: {
        visible: false,
        loading: false,
        keyword: '',
        pageNum: 1,
        pageSize: 20,
        total: 0,
        list: []
      },
      lineHasMore: false,
      linePageNum: 1,
      lineKeyword: '',
      pendingOrdersLoading: false,
      pendingOrderKeyword: '',
      pendingOrderList: [],
      recentStorageKey: 'aps_plan_recent',
      ganttList: [],
      tableHeight: 520,
      quick: {
        sourceType: 'product',
        orderId: '',
        orderCode: '',
        productId: null,
        productName: '',
        productSpec: '',
        productMaterial: '',
        productQty: 1,
        productItems: [],
        lineId: null,
        range: []
      },
      createDialog: {
        visible: false,
        form: {
          sourceType: 'product',
          orderId: '',
          orderCode: '',
          productId: null,
          productName: '',
          productQty: 1,
          priority: 1,
          range: [],
          remark: ''
        }
      },
      productSelector: {
        target: 'create',
        visible: false,
        loading: false,
        keyword: '',
        pageNum: 1,
        pageSize:20,
        total: 0,
        list: []
      },
      selectedProductRows: [],
      rescheduleDialog: {
        visible: false,
        meta: {
          operationId: null,
          lineName: ''
        },
        form: {
          targetLineId: null,
          range: [],
          reason: ''
        }
      },
      lockDialog: {
        visible: false,
        meta: {
          operationId: null,
          lineId: null,
          lineName: ''
        },
        form: {
          lockType: 3,
          range: [],
          lockReason: ''
        }
      },
      unlockDialog: {
        visible: false,
        lockId: null,
        operationId: null
      },
      adjustMode: false,
      adjustSnapshot: [],
      createRules: {
        orderId: [{ required: true, message: '库存订单不能为空', trigger: 'blur' }],
        productId: [{ required: true, message: '产品不能为空', trigger: 'change' }],
        productQty: [{ required: true, message: '数量不能为空', trigger: 'blur' }],
        lineId: [{ required: true, message: '请选择产线', trigger: 'change' }]
      },
      rescheduleRules: {
        targetLineId: [{ required: true, message: '请选择目标产线', trigger: 'change' }],
        range: [{ type: 'array', required: true, message: '请选择目标时间范围', trigger: 'change' }],
        reason: [{ required: true, message: '请填写重排原因', trigger: 'blur' }]
      },
      lockRules: {
        lockType: [{ required: true, message: '请选择锁定类型', trigger: 'change' }],
        lockReason: [{ required: true, message: '请填写锁定原因', trigger: 'blur' }]
      }
    }
  },
  computed: {
    calendarQuery() {
      const range = (this.form.range && this.form.range.length === 2)
        ? [this.form.range[0].slice(0, 10), this.form.range[1].slice(0, 10)]
        : []
      return {
        range,
        lineId: this.form.lineId || undefined,
        orderId: this.form.orderId || undefined,
        keyword: this.form.orderCode || ''
      }
    },
    quickReady() {
      if (!this.quick.lineId) return false
      if (this.quick.sourceType === 'product') {
        return (this.quick.productItems || []).length > 0
      }
      return !!this.quick.orderId
    },
    ganttTimes() {
      const times = (this.ganttList || [])
        .flatMap(i => [i.startTime, i.endTime])
        .filter(Boolean)
        .map(t => new Date(t).getTime())
        .filter(v => !Number.isNaN(v))
      if (!times.length) return { start: null, end: null }
      return { start: Math.min(...times), end: Math.max(...times) }
    },
    ganttChartStartLabel() {
      if (!this.ganttTimes.start) return '-'
      return this.formatDateTime(new Date(this.ganttTimes.start))
    },
    ganttChartEndLabel() {
      if (!this.ganttTimes.end) return '-'
      return this.formatDateTime(new Date(this.ganttTimes.end))
    },
    ganttLineRows() {
      const map = {}
      ;(this.ganttList || []).forEach(item => {
        const key = String(item.lineId || item.lineName || '未知产线')
        if (!map[key]) {
          map[key] = {
            lineId: item.lineId,
            lineName: item.lineName || ('产线' + (item.lineId || '-')),
            tasks: []
          }
        }
        map[key].tasks.push(item)
      })
      return Object.values(map).map(i => {
        i.tasks.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime())
        return i
      })
    }
  },
  created() {
    this.tableHeight = document.documentElement.clientHeight - 260
    this.initDefaultRange()
    this.loadLines()
    this.loadPendingOrders()
    this.fetchProductOptions()
    this.applyRouteQuery()
    this.$nextTick(() => {
      if (!this.loading.oneKey) {
        this.handleQueryGantt()
      }
    })
  },
  methods: {
    applyRouteQuery() {
      const q = (this.$route && this.$route.query) || {}
      if (q.orderId) {
        this.form.orderId = q.orderId
        if (q.orderCode) {
          this.form.orderCode = q.orderCode
        }
        this.openCreatePlan()
        this.$nextTick(() => {
          if (this.createDialog && this.createDialog.form) {
            this.createDialog.form.orderId = String(q.orderId)
            this.createDialog.form.orderCode = q.orderCode || ''
          }
          if (q.autoOneKey === '1') {
            this.handleOneKeySchedule()
          }
        })
      }
    },
    openOrderSelector(target = 'filter') {
      this.orderSelectorTarget = target
      this.orderSelector.visible = true
      this.orderSelector.pageNum = 1
      this.fetchOrderOptions()
    },
    openProductSelector(target = 'create') {
      this.productSelector.target = target
      this.productSelector.visible = true
      this.productSelector.pageNum = 1
      this.fetchProductOptions()
    },
    fetchOrderOptions() {
      this.orderSelector.loading = true
      const params = {
        pageNum: this.orderSelector.pageNum,
        pageSize: this.orderSelector.pageSize,
        orderCode: this.orderSelector.keyword || undefined
      }
      listWmsOrder(params).then(res => {
        this.orderSelector.list = (res.rows || []).filter(item => {
          const status = Number(item.orderStatus)
          return status !== 2 && status !== 3
        })
        this.orderSelector.total = res.total || 0
      }).finally(() => {
        this.orderSelector.loading = false
      })
    },
    loadPendingOrders() {
      this.pendingOrdersLoading = true
      listWmsOrder({
        pageNum: 1,
        pageSize: 200,
        orderCode: this.pendingOrderKeyword || undefined
      }).then(res => {
        const rows = res.rows || []
        this.pendingOrderList = rows.filter(item => {
          const status = Number(item.orderStatus)
          return status !== 2 && status !== 3
        })
      }).finally(() => {
        this.pendingOrdersLoading = false
      })
    },
    pickPendingOrder(item) {
      const orderId = item.orderId || item.id
      this.quick.orderId = String(orderId)
      this.quick.orderCode = item.orderCode || ''
      this.quick.sourceType = 'wmsOrder'
      this.$message.success('已选择库存订单：' + (item.orderCode || item.orderId))
    },

    fetchProductOptions() {
      this.productSelector.loading = true
      listProduct({
        pageNum: this.productSelector.pageNum,
        pageSize: this.productSelector.pageSize,
        productName: this.productSelector.keyword || undefined,
        productCode: this.productSelector.keyword || undefined
      }).then(res => {
        this.productSelector.list = res.rows || []
        this.productSelector.total = res.total || 0
      }).finally(() => {
        this.productSelector.loading = false
      })
    },
    selectOrder(row) {
      const orderId = row.orderId || row.id
      if (!orderId) {
        this.$message.warning('订单数据缺少 orderId')
        return
      }
      const orderCode = row.orderCode || ''
      if (this.orderSelectorTarget === 'create') {
        this.createDialog.form.orderId = String(orderId)
        this.createDialog.form.orderCode = orderCode
      } else {
        this.quick.orderId = String(orderId)
        this.quick.orderCode = orderCode
      }
      this.orderSelector.visible = false
    },
    selectProduct(row) {
      if (this.productSelector.target === 'quick') {
        this.quick.productId = row.productId
        this.quick.productName = row.productName || row.productCode || ''
        this.quick.productSpec = row.specification || ''
        this.quick.productMaterial = row.material || ''
      } else {
        this.createDialog.form.productId = row.productId
        this.createDialog.form.productName = row.productName || row.productCode || ''
      }
      this.productSelector.visible = false
    },
    onProductSelectionChange(rows) {
      this.selectedProductRows = rows || []
    },
    batchAddSelectedProducts() {
      if (this.productSelector.target !== 'quick') {
        this.$message.warning('当前模式不支持批量加入')
        return
      }
      if (!this.selectedProductRows.length) {
        this.$message.warning('请先勾选产品')
        return
      }
      const qty = Number(this.quick.productQty || 1)
      this.selectedProductRows.forEach(row => {
        const existed = (this.quick.productItems || []).find(i => String(i.productId) === String(row.productId))
        if (existed) {
          existed.quantity = Number(existed.quantity || 0) + qty
        } else {
          this.quick.productItems.push({
            productId: row.productId,
            productName: row.productName || row.productCode || String(row.productId),
            specification: row.specification || '',
            material: row.material || '',
            quantity: qty
          })
        }
      })
      this.$message.success('已批量加入 ' + this.selectedProductRows.length + ' 个产品')
      this.productSelector.visible = false
      this.selectedProductRows = []
    },
    addQuickProductItem() {
      if (!this.quick.productId) {
        this.$message.warning('请先选择产品')
        return
      }
      const qty = Number(this.quick.productQty || 0)
      if (qty <= 0) {
        this.$message.warning('数量必须大于 0')
        return
      }
      const existed = (this.quick.productItems || []).find(i => String(i.productId) === String(this.quick.productId))
      if (existed) {
        existed.quantity = Number(existed.quantity || 0) + qty
      } else {
        this.quick.productItems.push({
          productId: this.quick.productId,
          productName: this.quick.productName || String(this.quick.productId),
          specification: this.quick.productSpec || '',
          material: this.quick.productMaterial || '',
          quantity: qty
        })
      }
      this.quick.productQty = 1
    },
    removeQuickProductItem(index) {
      this.quick.productItems.splice(index, 1)
    },
    moveQuickProductItem(index, step) {
      const target = index + step
      if (target < 0 || target >= this.quick.productItems.length) return
      const arr = this.quick.productItems
      const tmp = arr[index]
      this.$set(arr, index, arr[target])
      this.$set(arr, target, tmp)
    },
    goSheet() {
      const q = {
        templateKey: 'cold_rolling',
        planId: this.form.planId || undefined,
        orderId: this.form.orderId || undefined,
        lineId: this.form.lineId || undefined
      }
      if (this.form.range && this.form.range.length === 2) {
        q.queryStart = this.form.range[0]
        q.queryEnd = this.form.range[1]
      }
      this.$router.push({ path: '/aps/sheet', query: q }).catch(() => {})
    },
    initDefaultRange() {
      const now = new Date()
      const y = now.getFullYear()
      const m = now.getMonth()
      const d = now.getDate()
      const start = new Date(y, m, d, 0, 0, 0)
      const end = new Date(y, m, d + 7, 23, 59, 59)
      this.form.range = [parseTime(start), parseTime(end)]
      this.quick.range = [parseTime(start), parseTime(end)]
    },
    formatDateTime(d) {
      if (!d) return '-'
      const y = d.getFullYear()
      const m = `${d.getMonth() + 1}`.padStart(2, '0')
      const day = `${d.getDate()}`.padStart(2, '0')
      const hh = `${d.getHours()}`.padStart(2, '0')
      const mm = `${d.getMinutes()}`.padStart(2, '0')
      return `${y}-${m}-${day} ${hh}:${mm}`
    },
    ganttTaskLabel(task) {
      const p = task.productName || task.productCode || '产品'
      const m = task.material || task.productMaterial || '-'
      const s = task.specification || task.productSpec || '-'
      return `${p}-${m}-${s}`
    },
    ganttBarStyle(task) {
      const start = new Date(task.startTime).getTime()
      const end = new Date(task.endTime).getTime()
      const min = this.ganttTimes.start
      const max = this.ganttTimes.end
      if (!min || !max || Number.isNaN(start) || Number.isNaN(end) || max <= min) {
        return { left: '0%', width: '2%' }
      }
      const left = ((start - min) / (max - min)) * 100
      const width = Math.max(((end - start) / (max - min)) * 100, 1.2)
      return { left: `${left}%`, width: `${width}%` }
    },
    loadLines(keyword) {
      this.lineLoading = true
      listProductionLine({ pageNum: 1, pageSize: 500, lineName: keyword || undefined }).then(res => {
        const rows = (res.rows || res.data || []).map(r => ({
          lineId: r.lineId,
          lineName: r.lineName || r.lineCode
        }))
        this.lineOptions = this.sortByRecent(rows, 'lineId')
        this.applyDefaultSelections()
      }).catch(() => {}).finally(() => {
        this.lineLoading = false
      })
    },
    searchLines(keyword) {
      this.loadLines(keyword)
    },
    loadMoreLines() {},
    onLineSelectVisible() {},
    getRecentSelections() {
      try {
        const raw = localStorage.getItem(this.recentStorageKey)
        if (!raw) {
          return { lineIdList: [], defaultLineId: null }
        }
        const parsed = JSON.parse(raw)
        return {
          lineIdList: parsed.lineIdList || [],
          defaultLineId: parsed.defaultLineId || null
        }
      } catch (e) {
        return { lineIdList: [], defaultLineId: null }
      }
    },
    saveRecentSelections(state) {
      localStorage.setItem(this.recentStorageKey, JSON.stringify(state))
    },
    rememberSelection(lineId) {
      if (!lineId) return
      const state = this.getRecentSelections()
      state.lineIdList = [lineId, ...(state.lineIdList || []).filter(id => id !== lineId)].slice(0, 8)
      state.defaultLineId = lineId
      this.saveRecentSelections(state)
    },
    sortByRecent(options, idKey) {
      const state = this.getRecentSelections()
      const recent = state.lineIdList || []
      const recentSet = new Set(recent)
      const unique = []
      const seen = new Set()
      ;(options || []).forEach(item => {
        const key = item && item[idKey]
        if (!seen.has(key)) {
          seen.add(key)
          unique.push(item)
        }
      })
      const recentPart = []
      recent.forEach(id => {
        const matched = unique.find(item => item && item[idKey] === id)
        if (matched) {
          recentPart.push(matched)
        }
      })
      const normalPart = unique.filter(item => !recentSet.has(item && item[idKey]))
      return recentPart.concat(normalPart)
    },
    applyDefaultSelections() {
      const state = this.getRecentSelections()
      if (!this.quick.lineId && state.defaultLineId) {
        this.quick.lineId = state.defaultLineId
      }
    },
    onQuickLineVisible(visible) {
      if (visible && (!this.lineOptions || !this.lineOptions.length)) {
        this.loadLines()
      }
    },
    onLineSelected(value) {
      this.rememberSelection(value)
      this.quick.lineId = value
      this.form.lineId = value
    },
    resetFilter() {
      this.form.planId = ''
      this.form.orderId = ''
      this.form.orderCode = ''
      this.form.lineId = null
      this.initDefaultRange()
      this.applyDefaultSelections()
      this.ganttList = []
    },
    handleQueryGantt() {
      if (!this.form.range || this.form.range.length !== 2) {
        this.$message.warning('请先选择时间范围')
        return
      }
      if (!this.form.orderId && !this.form.planId && !this.form.lineId) {
        // 保守起见，允许仅按时间与产线/订单过滤，此处不强制订单编号
      }
      const params = {
        queryStart: this.form.range[0],
        queryEnd: this.form.range[1],
        lineId: this.form.lineId || undefined,
        planId: this.form.planId || undefined,
        orderId: this.form.orderId || undefined
      }
      this.loading.gantt = true
      fetchGantt(params)
        .then(res => {
          this.ganttList = (res.data || []).map((r, idx) => ({
            ...r,
            _editStartTime: r.startTime,
            _editEndTime: r.endTime,
            _editSequenceNo: r.sequenceNo || idx + 1
          }))
        })
        .finally(() => {
          this.loading.gantt = false
        })
    },
    openCreatePlan() {
      this.createDialog.visible = true
      this.$nextTick(() => {
        this.$refs.createFormRef && this.$refs.createFormRef.resetFields()
        this.createDialog.form.sourceType = 'product'
        this.createDialog.form.priority = 1
        this.createDialog.form.productQty = 1
        this.createDialog.form.lineId = this.form.lineId || null
        this.createDialog.form.range = []
      })
    },
    submitCreatePlan() {
      this.$refs.createFormRef.validate(valid => {
        if (!valid) return
        const sourceType = this.createDialog.form.sourceType || 'product'
        this.loading.creating = true
        const convertReq = sourceType === 'product'
          ? convertFromProducts({
              items: [{
                productId: this.createDialog.form.productId,
                quantity: Number(this.createDialog.form.productQty || 0),
                productName: this.createDialog.form.productName
              }],
              remark: this.createDialog.form.remark
            })
          : Promise.resolve({ data: { wmsOrderId: String(this.createDialog.form.orderId) } })

        convertReq
          .then(conv => {
            const wmsOrderId = conv && conv.data && conv.data.wmsOrderId
            if (!wmsOrderId) throw new Error('未获取到库存订单ID')
            this.form.orderId = String(wmsOrderId)
            this.form.orderCode = this.createDialog.form.orderCode || this.form.orderCode
            this.form.lineId = this.createDialog.form.lineId || this.form.lineId
            const payload = {
              orderId: String(wmsOrderId),
              lineId: this.createDialog.form.lineId,
              priority: this.createDialog.form.priority,
              startDate: this.createDialog.form.range && this.createDialog.form.range[0],
              endDate: this.createDialog.form.range && this.createDialog.form.range[1],
              remark: this.createDialog.form.remark
            }
            return createPlan(payload)
          })
          .then(res => {
            this.form.planId = res.data
            this.$message.success('创建计划成功，planId=' + res.data)
            this.createDialog.visible = false
          })
          .finally(() => {
            this.loading.creating = false
          })
      })
    },
    handleQuickSchedule() {
      if (!this.quick.lineId) {
        this.$message.warning('请选择要排产的目标产线')
        return
      }
      this.createDialog.form.sourceType = this.quick.sourceType
      this.createDialog.form.productId = this.quick.productId
      this.createDialog.form.productName = this.quick.productName
      this.createDialog.form.productQty = this.quick.productQty
      this.createDialog.form.orderId = this.quick.orderId
      this.createDialog.form.orderCode = this.quick.orderCode
      this.createDialog.form.lineId = this.quick.lineId
      this.form.lineId = this.quick.lineId
      this.createDialog.form.range = this.quick.range
      this.handleOneKeySchedule()
    },
    handleOneKeySchedule() {
      const sourceType = (this.createDialog.form && this.createDialog.form.sourceType) || 'product'
      const orderId = this.createDialog.form && this.createDialog.form.orderId
      const hasProductItems = (this.quick.productItems || []).some(i => i && i.productId && Number(i.quantity || 0) > 0)
      if (sourceType === 'product' && !hasProductItems) {
        this.$message.warning('请先添加产品到排产清单')
        return
      }
      if (sourceType === 'wmsOrder' && !orderId) {
        this.$message.warning('请先选择库存订单')
        return
      }
      this.loading.oneKey = true
      const ensurePlan = () => {
        if (this.form.planId) return Promise.resolve(this.form.planId)
        const convertReq = sourceType === 'product'
          ? (() => {
              const items = (this.quick.productItems || []).map(i => ({
                productId: i.productId,
                quantity: Number(i.quantity || 0),
                productName: i.productName
              })).filter(i => i.productId && i.quantity > 0)
              if (!items.length) {
                throw new Error('请先添加至少一个产品到排产清单')
              }
              return convertFromProducts({ items, remark: this.createDialog.form.remark })
            })()
          : Promise.resolve({ data: { wmsOrderId: String(orderId) } })

        return convertReq.then(conv => {
          const wmsOrderId = conv && conv.data && conv.data.wmsOrderId
          this.form.orderId = String(wmsOrderId)
          this.form.orderCode = this.createDialog.form.orderCode || this.form.orderCode
          const payload = {
            orderId: String(wmsOrderId),
            lineId: this.createDialog.form.lineId,
            priority: this.createDialog.form.priority || 1,
            startDate: this.createDialog.form.range && this.createDialog.form.range[0],
            endDate: this.createDialog.form.range && this.createDialog.form.range[1],
            remark: this.createDialog.form.remark
          }
          return createPlan(payload)
        }).then(res => {
          this.form.planId = res.data
          return res.data
        })
      }

      ensurePlan()
        .then(planId => autoSchedule({ planId: String(planId), clearOld: true, remark: 'ONE_KEY_SCHEDULE' }))
        .then(() => {
          this.$message.success('一键排产完成')
          if (this.quick.range && this.quick.range.length === 2) this.form.range = this.quick.range
          if (!this.form.range || this.form.range.length !== 2) this.initDefaultRange()
          this.handleQueryGantt()
        })
        .finally(() => {
          this.loading.oneKey = false
        })
    },
    handleAutoSchedule() {
      if (!this.form.planId) {
        this.$message.warning('当前暂无排产计划，请先通过“创建计划”或“一键排产并查看”生成计划')
        return
      }
      this.$confirm('将对计划 ID=' + this.form.planId + ' 执行自动排程，是否继续？', '提示', {
        type: 'warning'
      }).then(() => {
        this.loading.auto = true
        autoSchedule({
          planId: String(this.form.planId),
          clearOld: true,
          remark: 'FE_TRIGGER'
        }).then(() => {
          this.$message.success('自动排程成功')
          this.handleQueryGantt()
        }).finally(() => {
          this.loading.auto = false
        })
      }).catch(() => {})
    },
    handlePublish() {
      if (!this.form.planId) {
        this.$message.warning('当前暂无排产计划，请先完成排产')
        return
      }
      this.$confirm('发布计划后将进入执行状态，是否继续？', '提示', {
        type: 'warning'
      }).then(() => {
        this.loading.publish = true
        publishPlan(String(this.form.planId))
          .then(() => {
            this.$message.success('发布成功')
          })
          .finally(() => {
            this.loading.publish = false
          })
      }).catch(() => {})
    },
    enterAdjustMode() {
      if (!this.ganttList.length) return
      this.adjustMode = true
      this.adjustSnapshot = JSON.parse(JSON.stringify(this.ganttList))
    },
    cancelAdjustMode() {
      this.adjustMode = false
      this.ganttList = JSON.parse(JSON.stringify(this.adjustSnapshot || []))
    },
    moveGanttRow(index, step) {
      const target = index + step
      if (target < 0 || target >= this.ganttList.length) return
      const arr = this.ganttList
      const tmp = arr[index]
      this.$set(arr, index, arr[target])
      this.$set(arr, target, tmp)
      arr.forEach((r, i) => { r._editSequenceNo = i + 1 })
    },
    saveAdjustments() {
      if (!this.adjustMode) return
      const changed = (this.ganttList || []).filter((r, idx) => {
        const old = (this.adjustSnapshot || [])[idx]
        if (!old) return true
        return String(r._editStartTime || '') !== String(old._editStartTime || '') ||
          String(r._editEndTime || '') !== String(old._editEndTime || '') ||
          Number(r._editSequenceNo || 0) !== Number(old._editSequenceNo || 0)
      })
      if (!changed.length) {
        this.$message.info('没有检测到调整')
        this.adjustMode = false
        return
      }

      const jobs = changed.map(r => {
        const payload = {
          operationId: r.operationId,
          targetLineId: r.lineId,
          targetStartTime: r._editStartTime,
          targetEndTime: r._editEndTime,
          reason: 'MANUAL_ADJUST_SAVE'
        }
        return rescheduleOperation(payload)
      })

      Promise.all(jobs).then(() => {
        this.$message.success('调整已保存')
        this.adjustMode = false
        this.handleQueryGantt()
      })
    },
    openReschedule(row) {
      this.rescheduleDialog.visible = true
      this.rescheduleDialog.meta.operationId = row.operationId
      this.rescheduleDialog.meta.lineName = row.lineName || ('产线' + row.lineId)
      this.$nextTick(() => {
        this.$refs.rescheduleFormRef && this.$refs.rescheduleFormRef.resetFields()
        const state = this.getRecentSelections()
        if (state.defaultLineId) {
          this.rescheduleDialog.form.targetLineId = state.defaultLineId
        }
      })
    },
    submitReschedule() {
      this.$refs.rescheduleFormRef.validate(valid => {
        if (!valid) return
        const [start, end] = this.rescheduleDialog.form.range || []
        const payload = {
          operationId: this.rescheduleDialog.meta.operationId,
          targetLineId: this.rescheduleDialog.form.targetLineId,
          targetStartTime: start,
          targetEndTime: end,
          reason: this.rescheduleDialog.form.reason
        }
        rescheduleOperation(payload).then(() => {
          this.$message.success('重排成功')
          this.rescheduleDialog.visible = false
          this.handleQueryGantt()
        })
      })
    },
    openLock(row) {
      this.lockDialog.visible = true
      this.lockDialog.meta.operationId = row.operationId
      this.lockDialog.meta.lineId = row.lineId
      this.lockDialog.meta.lineName = row.lineName || ('产线' + row.lineId)
      this.$nextTick(() => {
        this.$refs.lockFormRef && this.$refs.lockFormRef.resetFields()
        this.lockDialog.form.lockType = 3
      })
    },
    submitLock() {
      this.$refs.lockFormRef.validate(valid => {
        if (!valid) return
        const [start, end] = this.lockDialog.form.range || []
        const payload = {
          lockType: this.lockDialog.form.lockType,
          planId: this.form.planId ? String(this.form.planId) : null,
          lineId: this.lockDialog.meta.lineId,
          operationId: this.lockDialog.form.lockType === 3 ? this.lockDialog.meta.operationId : null,
          lockStartTime: this.lockDialog.form.lockType === 2 ? start : null,
          lockEndTime: this.lockDialog.form.lockType === 2 ? end : null,
          lockReason: this.lockDialog.form.lockReason
        }
        createLock(payload).then(() => {
          this.$message.success('锁定成功')
          this.lockDialog.visible = false
          this.handleQueryGantt()
        })
      })
    },
    openUnlock(row) {
      this.unlockDialog.lockId = row.lockId || null
      this.unlockDialog.operationId = row.operationId || null
      this.unlockDialog.visible = true
    },
    submitUnlock() {
      if (!this.unlockDialog.lockId) {
        this.$message.warning('当前记录未返回 lockId，暂无法直接解锁，请到锁管理页面处理')
        this.unlockDialog.visible = false
        return
      }
      releaseLock(this.unlockDialog.lockId).then(() => {
        this.$message.success('解锁成功')
        this.unlockDialog.visible = false
        this.handleQueryGantt()
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.aps-page {
  padding: 10px;
  background: #f7f9fc;
}

.aps-calendar-flat {
  margin-bottom: 10px;
}

::v-deep .aps-header.el-card,
::v-deep .aps-body.el-card {
  border: none;
  box-shadow: none;
}

.aps-header,
.aps-body,
.aps-calendar-flat {
  border: 1px solid #eef2f7;
  border-radius: 8px;
  background: #fff;
}

.aps-header {
  margin-bottom: 10px;

  .aps-header-main {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 8px;
  }

  .aps-header-sub {
    margin-top: 4px;
    font-size: 13px;
    color: #606266;
  }

      .aps-header-current {
        margin-top: 6px;
        font-size: 13px;
        color: #409EFF;
      }

  .aps-header-actions > .el-button + .el-button {
    margin-left: 8px;
  }
}

.aps-header-filter {
  margin-top: 4px;
}

.dispatch-panel {
  margin-bottom: 10px;
  padding: 12px;
  border: 1px solid #edf1f7;
  border-radius: 10px;
  background: #ffffff;
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 12px;
}

.dispatch-panel-single {
  grid-template-columns: 1fr;
}

.dispatch-title {
  font-size: 14px;
  font-weight: 600;
  color: #303133;
  margin-bottom: 8px;
}

.dispatch-source-row,
.dispatch-form-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

.dispatch-right {
  border-left: 1px solid #f0f2f6;
  padding-left: 10px;
}

.pending-order-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
}

.pending-order-title {
  font-size: 13px;
  font-weight: 600;
  color: #303133;
}

.pending-order-list {
  min-height: 58px;
  max-height: 126px;
  overflow: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding-right: 4px;
}

.pending-order-list::-webkit-scrollbar,
.product-pool-list::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

.pending-order-list::-webkit-scrollbar-thumb,
.product-pool-list::-webkit-scrollbar-thumb {
  background: #d9e2ef;
  border-radius: 4px;
}

.pending-order-list::-webkit-scrollbar-track,
.product-pool-list::-webkit-scrollbar-track {
  background: transparent;
}

.pending-order-tag {
  cursor: pointer;
  border-radius: 12px;
}

.product-pool-list {
  max-height: 190px;
  overflow: auto;
  display: grid;
  grid-template-columns: 1fr;
  gap: 6px;
}

.product-pool-inline {
  max-height: none;
  overflow: visible;
}

.product-card {
  border: 1px solid #edf1f7;
  border-radius: 8px;
  padding: 8px;
  cursor: pointer;
  background: #fff;
}

.product-card-inline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.p-inline-main {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.p-inline-op {
  display: flex;
  align-items: center;
  gap: 6px;
}

.product-card:hover {
  border-color: #bcd2f7;
  background: #f7fbff;
}

.product-dialog-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}

.p-title {
  font-size: 13px;
  color: #303133;
  margin-bottom: 4px;
}

.p-sub {
  font-size: 12px;
  color: #909399;
}

.aps-body {
  .el-table {
    font-size: 12px;
  }
}

.result-toolbar {
  margin: 0 0 8px;
  display: flex;
  justify-content: flex-end;
}

.result-head {
  font-size: 12px;
  color: #606266;
  margin: 0 0 8px;
}

.gantt-wrap {
  border: 1px solid #eef2f7;
  border-radius: 8px;
  padding: 8px;
  background: #fff;
}

.gantt-axis {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #909399;
  margin-bottom: 8px;
}

.gantt-line {
  display: grid;
  grid-template-columns: 120px 1fr;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px;
}

.gantt-line-name {
  font-size: 12px;
  color: #303133;
  font-weight: 600;
}

.gantt-track {
  position: relative;
  height: 28px;
  border: 1px solid #f0f2f6;
  border-radius: 4px;
  background: repeating-linear-gradient(to right, #fafbfd 0, #fafbfd 24px, #fff 24px, #fff 48px);
}

.gantt-bar {
  position: absolute;
  top: 3px;
  height: 22px;
  border-radius: 4px;
  background: #409eff;
  color: #fff;
  font-size: 12px;
  line-height: 22px;
  padding: 0 6px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

.aps-empty {
  padding: 40px 0;
}

.dialog-tip {
  font-size: 12px;
  color: #909399;
  margin: 0 0 8px;
}
</style>

