<template>
  <el-dialog
    :title="dialogTitle"
    :visible.sync="visible"
    width="1050px"
    :close-on-click-modal="false"
    @close="reset"
  >
    <template v-if="mode === 'records'">
      <ele-pro-table
        ref="recordTable"
        :columns="recordColumns"
        :datasource="recordSource"
        height="450px"
        :pageSize="10"
      >
        <template v-slot:status="{ row }">
          <el-tag :type="statusType(row.verifyStatus)">
            {{ statusText(row.verifyStatus) }}
          </el-tag>
        </template>
        <template v-slot:action="{ row }">
          <el-button type="text" @click="showDetail(row)">详情</el-button>
          <el-button
            v-if="row.verifyStatus === 0"
            type="text"
            :loading="recordActionId === row.id"
            @click="submitRecord(row)"
            >提交</el-button
          >
          <el-button
            v-if="row.verifyStatus === 1"
            type="text"
            :loading="recordActionId === row.id"
            @click="withdrawRecord(row)"
            >撤回</el-button
          >
        </template>
      </ele-pro-table>
    </template>

    <div v-else v-loading="sourceLoading">
      <el-alert
        v-if="mode === 'merge'"
        title="仅允许同仓库、批次号、物品属性、供应商和价格完全一致的库存合批，审批通过后生成新系统批次。"
        type="info"
        :closable="false"
      />
      <el-alert
        v-else
        title="包装明细必须整行分配；锁定明细按锁定单分配，关联包装时自动跟随包装目标。"
        type="info"
        :closable="false"
      />
      <el-alert
        v-if="pendingApproveId"
        title="申请已保存，当前仅续提审批，不会重复生成审核代码。"
        type="warning"
        :closable="false"
        class="saved-alert"
      />

      <div class="section-title">来源库存</div>
      <el-table :data="sources" size="mini" border>
        <el-table-column
          prop="systemBatchNo"
          label="来源系统批次"
          min-width="150"
        />
        <el-table-column prop="batchNo" label="业务批次" />
        <el-table-column prop="warehouseName" label="仓库" />
        <el-table-column prop="categoryCode" label="物品编码" />
        <el-table-column prop="stockCountBase" label="计量数量" />
        <el-table-column prop="packingQuantity" label="包装数量" />
        <el-table-column prop="weight" label="重量" />
      </el-table>

      <template v-if="mode === 'split'">
        <div class="section-title">目标子批次</div>
        <el-table :data="targetRows" size="mini" border>
          <el-table-column label="目标" width="90">
            <template slot-scope="{ row }"
              >子批次 {{ row.targetDetailId }}</template
            >
          </el-table-column>
          <el-table-column label="系统批次号" width="130">
            <template>审批后生成</template>
          </el-table-column>
          <el-table-column label="计量数量">
            <template slot-scope="{ row }">
              <el-input
                v-model="row.target.measureQuantity"
                maxlength="19"
                :disabled="Boolean(pendingApproveId)"
                size="mini"
                placeholder="请输入"
              />
            </template>
          </el-table-column>
          <el-table-column label="包装数量">
            <template slot-scope="{ row }">
              <span v-if="hasPacking">{{ row.packageQuantity }}</span>
              <el-input
                v-else
                v-model="row.target.packageQuantity"
                maxlength="19"
                :disabled="Boolean(pendingApproveId)"
                size="mini"
                placeholder="请输入"
              />
            </template>
          </el-table-column>
          <el-table-column label="重量">
            <template slot-scope="{ row }">
              <el-input
                v-model="row.target.weight"
                maxlength="19"
                :disabled="Boolean(pendingApproveId)"
                size="mini"
                placeholder="请输入"
              />
            </template>
          </el-table-column>
          <el-table-column prop="lockQuantity" label="锁库数量" />
          <el-table-column width="70">
            <template slot-scope="{ row }">
              <el-button
                type="text"
                :disabled="Boolean(pendingApproveId)"
                @click="removeTarget(row.targetDetailId)"
                >删除</el-button
              >
            </template>
          </el-table-column>
        </el-table>
        <el-button
          type="text"
          icon="el-icon-plus"
          :disabled="Boolean(pendingApproveId)"
          @click="addTarget"
          >新增子批次</el-button
        >
      </template>
      <div v-else class="merge-summary">
        合并后计量数量：{{ total('stockCountBase') }}，包装数量：{{
          total('packingQuantity')
        }}，重量：{{ total('weight') }}；存货周期取来源最长值。
      </div>

      <template v-if="packingRows.length">
        <div class="section-title">整包装分配</div>
        <el-table :data="packingRows" size="mini" border max-height="260">
          <el-table-column
            prop="systemBatchNo"
            label="来源系统批次"
            min-width="140"
          />
          <el-table-column prop="packageNo" label="包装编码" min-width="130" />
          <el-table-column prop="measureQuantity" label="计量数量" />
          <el-table-column prop="packageQuantity" label="包装数量" />
          <el-table-column prop="weight" label="重量" />
          <el-table-column prop="lockCountBase" label="锁库数量" />
          <el-table-column label="目标子批次" min-width="150">
            <template slot-scope="{ row }">
              <el-select
                v-model="row.targetDetailId"
                size="mini"
                :disabled="mode === 'merge' || Boolean(pendingApproveId)"
                placeholder="请选择"
                @change="assignPackingTarget(row, $event)"
              >
                <el-option
                  v-for="target in targets"
                  :key="target.targetDetailId"
                  :label="targetLabel(target.targetDetailId)"
                  :value="target.targetDetailId"
                />
              </el-select>
            </template>
          </el-table-column>
        </el-table>
      </template>

      <template v-if="lockRows.length">
        <div class="section-title">锁定单分配</div>
        <el-table :data="lockRows" size="mini" border max-height="260">
          <el-table-column prop="orderNo" label="锁定单号" min-width="140" />
          <el-table-column
            prop="lockOrderDetailId"
            label="锁定明细ID"
            min-width="110"
          />
          <el-table-column prop="quantity" label="锁定数量" />
          <el-table-column label="关联包装" min-width="180">
            <template slot-scope="{ row }">{{ packingNames(row) }}</template>
          </el-table-column>
          <el-table-column label="目标子批次" min-width="150">
            <template slot-scope="{ row }">
              <el-select
                v-model="row.targetDetailId"
                size="mini"
                :disabled="mode === 'merge' || Boolean(pendingApproveId)"
                placeholder="请选择"
                @change="assignLockOrderTarget(row, $event)"
              >
                <el-option
                  v-for="target in targets"
                  :key="target.targetDetailId"
                  :label="targetLabel(target.targetDetailId)"
                  :value="target.targetDetailId"
                />
              </el-select>
              <div v-if="row.targetConflict" class="error-text"
                >关联包装不能跨目标</div
              >
            </template>
          </el-table-column>
        </el-table>
      </template>

      <el-input
        v-model="remark"
        type="textarea"
        :rows="2"
        :disabled="Boolean(pendingApproveId)"
        placeholder="申请说明（可选）"
        class="remark"
      />
    </div>

    <span slot="footer">
      <el-button @click="visible = false">取 消</el-button>
      <el-button
        v-if="mode !== 'records'"
        type="primary"
        :loading="saving"
        :disabled="sourceLoading"
        @click="submitApply"
        >{{ pendingApproveId ? '继续提交审批' : '提交审批' }}</el-button
      >
    </span>

    <el-dialog
      title="批次变更申请详情"
      :visible.sync="detailVisible"
      width="850px"
      append-to-body
    >
      <el-descriptions
        v-loading="detailLoading"
        :column="3"
        border
        size="small"
      >
        <el-descriptions-item label="审核代码">{{
          detailData.changeNo || '-'
        }}</el-descriptions-item>
        <el-descriptions-item label="变更类型">{{
          detailData.changeType === 1 ? '拆批' : '合批'
        }}</el-descriptions-item>
        <el-descriptions-item label="审批状态">{{
          statusText(detailData.verifyStatus)
        }}</el-descriptions-item>
        <el-descriptions-item label="仓库">{{
          detailData.warehouseName || '-'
        }}</el-descriptions-item>
        <el-descriptions-item label="申请时间">{{
          detailData.createTime || '-'
        }}</el-descriptions-item>
        <el-descriptions-item label="说明">{{
          detailData.remark || '-'
        }}</el-descriptions-item>
      </el-descriptions>
      <el-table
        :data="detailData.details || []"
        size="mini"
        border
        class="detail-table"
      >
        <el-table-column prop="sourceStockBatchId" label="来源批次ID" />
        <el-table-column
          prop="systemBatchNo"
          label="来源系统批次"
          min-width="140"
        />
        <el-table-column
          prop="targetSystemBatchNo"
          label="目标系统批次"
          min-width="140"
        />
        <el-table-column prop="measureQuantity" label="计量数量" />
        <el-table-column prop="packageQuantity" label="包装数量" />
        <el-table-column prop="weight" label="重量" />
        <el-table-column prop="lockQuantity" label="锁库数量" />
        <el-table-column
          prop="packingStockIds"
          label="包装库存分配"
          min-width="180"
          show-overflow-tooltip
        />
        <el-table-column
          prop="lockAllocations"
          label="锁定单分配"
          min-width="220"
          show-overflow-tooltip
        />
      </el-table>
    </el-dialog>
  </el-dialog>
</template>

<script>
  import BigNumber from 'bignumber.js';
  import stockBatchChange from '@/api/warehouseManagement/stockBatchChange';

  function decimal(value) {
    const result = new BigNumber(value == null || value === '' ? 0 : value);
    return result.isFinite() ? result : new BigNumber(0);
  }

  function decimalValue(value) {
    return decimal(value).decimalPlaces(6).toFixed(6);
  }

  function isEmptyDecimal(value) {
    return value == null || String(value).trim() === '';
  }

  function decimalEquals(left, right) {
    const leftEmpty = isEmptyDecimal(left);
    const rightEmpty = isEmptyDecimal(right);
    if (leftEmpty || rightEmpty) return leftEmpty && rightEmpty;
    const leftDecimal = new BigNumber(left);
    const rightDecimal = new BigNumber(right);
    if (!leftDecimal.isFinite() || !rightDecimal.isFinite()) {
      return String(left) === String(right);
    }
    return leftDecimal.eq(rightDecimal);
  }

  const DECIMAL_INPUT_PATTERN = /^\d{1,12}(?:\.\d{1,6})?$/;

  export default {
    data() {
      return {
        visible: false,
        mode: 'split',
        sources: [],
        targets: [],
        remark: '',
        saving: false,
        sourceLoading: false,
        detailVisible: false,
        detailLoading: false,
        detailData: {},
        pendingApproveId: null,
        recordActionId: null
      };
    },
    computed: {
      dialogTitle() {
        if (this.mode === 'records') return '批次变更申请记录';
        return this.mode === 'split' ? '库存拆批申请' : '库存合批申请';
      },
      packingRows() {
        return this.sources.reduce(
          (rows, source) => rows.concat(source.packingDetails || []),
          []
        );
      },
      lockRows() {
        return this.sources.reduce(
          (rows, source) => rows.concat(source.lockDetails || []),
          []
        );
      },
      hasPacking() {
        return this.packingRows.length > 0;
      },
      targetRows() {
        return this.targets.map((target) => {
          const packages = this.packingRows.filter(
            (row) => row.targetDetailId === target.targetDetailId
          );
          const locks = this.lockRows.filter(
            (row) => row.targetDetailId === target.targetDetailId
          );
          const packingMeasureQuantity = this.sum(packages, 'measureQuantity');
          const packingWeight = this.sum(packages, 'weight');
          return {
            target,
            targetDetailId: target.targetDetailId,
            packingMeasureQuantity,
            packingWeight,
            measureQuantity: target.measureQuantity,
            packageQuantity: this.hasPacking
              ? this.sum(packages, 'packageQuantity')
              : target.packageQuantity,
            weight: target.weight,
            lockQuantity: this.sum(locks, 'quantity')
          };
        });
      },
      recordColumns() {
        return [
          { prop: 'changeNo', label: '审核代码' },
          {
            prop: 'changeType',
            label: '类型',
            formatter: (row) => (row.changeType === 1 ? '拆批' : '合批')
          },
          { prop: 'verifyStatus', slot: 'status', label: '审批状态' },
          { prop: 'createTime', label: '申请时间' },
          {
            columnKey: 'action',
            slot: 'action',
            label: '操作',
            width: 140,
            fixed: 'right'
          }
        ];
      }
    },
    methods: {
      async open(mode, rows) {
        this.mode = mode;
        this.pendingApproveId = null;
        this.visible = true;
        if (mode === 'records') return;
        this.sourceLoading = true;
        try {
          const selectedRows = rows || [];
          const ids = selectedRows.map((row) => row.stockBatchId || row.id);
          const result = await stockBatchChange.sourceDetails(ids);
          const sourceList = Array.isArray(result)
            ? result
            : result.sources || [];
          this.sources = sourceList.map((source) => {
            const selected =
              selectedRows.find(
                (row) =>
                  String(row.stockBatchId || row.id) ===
                  String(source.stockBatchId)
              ) || {};
            return this.normalizeSource({ ...selected, ...source });
          });
          this.targets =
            mode === 'merge'
              ? [this.createTarget(1)]
              : [this.createTarget(1), this.createTarget(2)];
          if (mode === 'merge') this.assignMergeTarget();
        } catch (error) {
          this.visible = false;
          this.$message.error(error.message || '批次来源详情加载失败');
        } finally {
          this.sourceLoading = false;
        }
      },
      normalizeSource(source) {
        const packingDetails = (source.packingDetails || []).map((row) => ({
          ...row,
          sourceStockBatchId: source.stockBatchId,
          systemBatchNo: source.systemBatchNo,
          targetDetailId: null
        }));
        const lockDetails = (source.lockDetails || []).map((row) => ({
          ...row,
          sourceStockBatchId: source.stockBatchId,
          lockedPackingStockIds: this.parseIdList(row.lockedPackingStockIds),
          targetDetailId: null,
          targetConflict: false
        }));
        return {
          ...source,
          stockCountBase:
            source.stockCountBase == null
              ? source.measureQuantity
              : source.stockCountBase,
          packingQuantity:
            source.packingQuantity == null
              ? source.packageQuantity
              : source.packingQuantity,
          packingDetails,
          lockDetails
        };
      },
      parseIdList(value) {
        if (Array.isArray(value)) return value.map(String);
        if (!value) return [];
        try {
          const parsed = JSON.parse(value);
          return Array.isArray(parsed) ? parsed.map(String) : [];
        } catch (error) {
          return [];
        }
      },
      createTarget(targetDetailId) {
        return {
          targetDetailId,
          measureQuantity: '0',
          packageQuantity: '0',
          weight: '0'
        };
      },
      reset() {
        this.sources = [];
        this.targets = [];
        this.remark = '';
        this.detailVisible = false;
        this.detailData = {};
        this.pendingApproveId = null;
      },
      addTarget() {
        const maxId = this.targets.reduce(
          (max, target) => Math.max(max, target.targetDetailId),
          0
        );
        this.targets.push(this.createTarget(maxId + 1));
      },
      removeTarget(targetDetailId) {
        if (this.targets.length <= 2)
          return this.$message.warning('拆批至少保留两个目标子批次');
        if (
          this.packingRows.some(
            (row) => row.targetDetailId === targetDetailId
          ) ||
          this.lockRows.some((row) => row.targetDetailId === targetDetailId)
        ) {
          return this.$message.warning('该目标已有包装或锁定单分配，不能删除');
        }
        this.targets = this.targets.filter(
          (target) => target.targetDetailId !== targetDetailId
        );
      },
      assignMergeTarget() {
        this.packingRows.forEach((row) => {
          row.targetDetailId = 1;
        });
        this.lockRows.forEach((row) => {
          row.targetDetailId = 1;
        });
        const target = this.targets[0];
        target.measureQuantity = this.sum(this.sources, 'stockCountBase');
        target.packageQuantity = this.sum(this.sources, 'packingQuantity');
        target.weight = this.sum(this.sources, 'weight');
        this.syncTargetPackingMinimums();
      },
      /** 包装分配变化时，同步其关联锁定单的全部明细和包装。 */
      assignPackingTarget(packing, targetDetailId) {
        const relatedLocks = this.lockRows.filter((lock) =>
          lock.lockedPackingStockIds.some(
            (id) => String(id) === String(packing.packingStockId)
          )
        );
        if (relatedLocks.length) {
          this.assignConnectedLockRows(relatedLocks, targetDetailId);
        }
        this.syncTargetPackingMinimums();
      },
      /** 锁定单必须整单分配，且所有关联包装必须与锁定单落到同一目标。 */
      assignLockOrderTarget(lock, targetDetailId) {
        this.assignConnectedLockRows([lock], targetDetailId);
        this.syncTargetPackingMinimums();
      },
      /** 传播同锁定单及共享包装关系，确保整个关联组只有一个目标。 */
      assignConnectedLockRows(seedLocks, targetDetailId) {
        const lockOrderIds = new Set(
          seedLocks.map((lock) => String(lock.lockOrderId))
        );
        const packingIds = new Set();
        let changed = true;
        while (changed) {
          changed = false;
          this.lockRows.forEach((lock) => {
            const linkedToOrder = lockOrderIds.has(String(lock.lockOrderId));
            const linkedToPacking = lock.lockedPackingStockIds.some((id) =>
              packingIds.has(String(id))
            );
            if (!linkedToOrder && !linkedToPacking) return;
            if (!lockOrderIds.has(String(lock.lockOrderId))) {
              lockOrderIds.add(String(lock.lockOrderId));
              changed = true;
            }
            lock.lockedPackingStockIds.forEach((id) => {
              if (!packingIds.has(String(id))) {
                packingIds.add(String(id));
                changed = true;
              }
            });
          });
        }
        this.lockRows.forEach((lock) => {
          if (!lockOrderIds.has(String(lock.lockOrderId))) return;
          lock.targetDetailId = targetDetailId;
          lock.targetConflict = false;
        });
        this.packingRows.forEach((packing) => {
          if (packingIds.has(String(packing.packingStockId))) {
            packing.targetDetailId = targetDetailId;
          }
        });
      },
      /** 包装变更后同步目标下限，超出包装汇总的部分作为散量。 */
      syncTargetPackingMinimums() {
        this.targets.forEach((target) => {
          const packages = this.packingRows.filter(
            (row) => row.targetDetailId === target.targetDetailId
          );
          const measureMinimum = decimal(this.sum(packages, 'measureQuantity'));
          const weightMinimum = decimal(this.sum(packages, 'weight'));
          if (decimal(target.measureQuantity).lt(measureMinimum)) {
            target.measureQuantity = measureMinimum.toFixed(6);
          }
          if (decimal(target.weight).lt(weightMinimum)) {
            target.weight = weightMinimum.toFixed(6);
          }
        });
      },
      targetLabel(id) {
        return this.mode === 'merge' ? '合并目标' : `子批次 ${id}`;
      },
      packingNames(lock) {
        if (!lock.lockedPackingStockIds.length) return '-';
        return lock.lockedPackingStockIds
          .map((id) => {
            const packing = this.packingRows.find(
              (row) => String(row.packingStockId) === String(id)
            );
            return packing ? packing.packageNo : id;
          })
          .join('、');
      },
      sum(rows, key) {
        return rows
          .reduce((sum, row) => sum.plus(decimal(row[key])), new BigNumber(0))
          .toFixed(6);
      },
      total(key) {
        return this.sum(this.sources, key);
      },
      statusText(value) {
        return (
          { 0: '未提交', 1: '审批中', 2: '已通过', 3: '已驳回', 4: '已撤回' }[
            value
          ] || '-'
        );
      },
      statusType(value) {
        return value === 2 ? 'success' : value === 3 ? 'danger' : 'info';
      },
      recordSource({ page, limit, where }) {
        return stockBatchChange.page({ ...where, pageNum: page, size: limit });
      },
      async showDetail(row) {
        this.detailVisible = true;
        this.detailLoading = true;
        this.detailData = {};
        try {
          this.detailData = (await stockBatchChange.detail(row.id)) || {};
        } catch (error) {
          this.detailVisible = false;
          this.$message.error(error.message || '申请详情加载失败');
        } finally {
          this.detailLoading = false;
        }
      },
      async submitRecord(row) {
        this.recordActionId = row.id;
        try {
          await stockBatchChange.submit({ id: row.id });
          this.$message.success('申请已提交审批');
          this.reloadRecords();
        } finally {
          this.recordActionId = null;
        }
      },
      async withdrawRecord(row) {
        try {
          await this.$confirm('确认撤回该批次变更申请？', '提示', {
            type: 'warning'
          });
        } catch (error) {
          return;
        }
        this.recordActionId = row.id;
        try {
          await stockBatchChange.withdraw({ id: row.id });
          this.$message.success('申请已撤回');
          this.reloadRecords();
        } finally {
          this.recordActionId = null;
        }
      },
      reloadRecords() {
        this.$nextTick(() => {
          if (this.$refs.recordTable) this.$refs.recordTable.reload();
        });
      },
      getBatchAttributeDifferences() {
        const attributes = [
          { key: 'stockId', label: '库存台账ID' },
          { key: 'warehouseId', label: '仓库', displayKey: 'warehouseName' },
          { key: 'batchNo', label: '业务批次号' },
          { key: 'categoryId', label: '物品ID' },
          { key: 'categoryCode', label: '物品编码' },
          { key: 'categoryName', label: '物品名称' },
          { key: 'specification', label: '规格' },
          { key: 'categoryModel', label: '型号' },
          { key: 'brandNum', label: '牌号' },
          { key: 'colorKey', label: '颜色' },
          { key: 'unitPrice', label: '单价', decimal: true },
          { key: 'supplierId', label: '供应商ID' },
          { key: 'supplierName', label: '供应商' },
          { key: 'supplierCode', label: '供应商代号' }
        ];
        return attributes.reduce((differences, attribute) => {
          const values = this.sources.map((row) => row[attribute.key]);
          const firstValue = values[0];
          const hasDifference = attribute.decimal
            ? values.some((value) => !decimalEquals(firstValue, value))
            : new Set(values.map((value) => String(value == null ? '' : value)))
                .size > 1;
          if (!hasDifference) return differences;
          const displayValues = Array.from(
            new Set(
              this.sources.map((row) => {
                const displayValue = attribute.displayKey
                  ? row[attribute.displayKey]
                  : null;
                const value =
                  displayValue == null || displayValue === ''
                    ? row[attribute.key]
                    : displayValue;
                return value == null || value === '' ? '未填写' : value;
              })
            )
          );
          differences.push(`${attribute.label}（${displayValues.join('、')}）`);
          return differences;
        }, []);
      },
      validateAllocation() {
        if (this.mode === 'split' && this.targets.length < 2)
          return '拆批至少需要两个目标子批次';
        if (
          this.mode === 'split' &&
          this.targets.some((target) =>
            ['measureQuantity', 'packageQuantity', 'weight'].some(
              (key) => !DECIMAL_INPUT_PATTERN.test(String(target[key]).trim())
            )
          )
        )
          return '计量数量、包装数量和重量最多支持12位整数、6位小数';
        if (this.packingRows.some((row) => !row.targetDetailId))
          return '请为每条包装明细选择目标子批次';
        if (
          this.lockRows.some((row) => row.targetConflict || !row.targetDetailId)
        )
          return '请完成锁定单分配，关联包装必须属于同一目标';
        const lockOrderTargets = new Map();
        for (const lock of this.lockRows) {
          const lockOrderId = String(lock.lockOrderId);
          const assignedTarget = lockOrderTargets.get(lockOrderId);
          if (
            lockOrderTargets.has(lockOrderId) &&
            assignedTarget !== lock.targetDetailId
          ) {
            return '同一锁定单的全部明细必须分配到同一目标子批次';
          }
          lockOrderTargets.set(lockOrderId, lock.targetDetailId);
          const packingTargetMismatch = lock.lockedPackingStockIds.some(
            (id) => {
              const packing = this.packingRows.find(
                (row) => String(row.packingStockId) === String(id)
              );
              return !packing || packing.targetDetailId !== lock.targetDetailId;
            }
          );
          if (packingTargetMismatch)
            return '锁定单关联包装必须分配到同一目标子批次';
        }
        if (this.targetRows.some((row) => decimal(row.measureQuantity).lte(0)))
          return '各目标子批次计量数量必须大于零';
        if (
          this.targetRows.some((row) =>
            decimal(row.lockQuantity).gt(decimal(row.measureQuantity))
          )
        )
          return '目标子批次锁库数量不能大于计量数量';
        if (
          this.hasPacking &&
          this.targetRows.some(
            (row) =>
              decimal(row.measureQuantity).lt(
                decimal(row.packingMeasureQuantity)
              ) || decimal(row.weight).lt(decimal(row.packingWeight))
          )
        )
          return '目标计量数量和重量不能小于所选整包装汇总';
        const source = this.sources[0];
        if (this.mode === 'split') {
          if (
            !decimal(this.sum(this.targetRows, 'measureQuantity')).eq(
              decimal(source.stockCountBase)
            ) ||
            !decimal(this.sum(this.targetRows, 'packageQuantity')).eq(
              decimal(source.packingQuantity)
            ) ||
            !decimal(this.sum(this.targetRows, 'weight')).eq(
              decimal(source.weight)
            )
          ) {
            return '目标计量数量、包装数量和重量合计必须等于来源库存';
          }
        }
        return '';
      },
      buildLockAllocations(sourceStockBatchId, targetDetailId) {
        return JSON.stringify(
          this.lockRows
            .filter(
              (row) =>
                row.sourceStockBatchId === sourceStockBatchId &&
                row.targetDetailId === targetDetailId
            )
            .map((row) => ({
              lockOrderDetailId: row.lockOrderDetailId,
              quantity: decimalValue(row.quantity)
            }))
        );
      },
      buildPackingIds(sourceStockBatchId, targetDetailId) {
        return JSON.stringify(
          this.packingRows
            .filter(
              (row) =>
                row.sourceStockBatchId === sourceStockBatchId &&
                row.targetDetailId === targetDetailId
            )
            .map((row) => row.packingStockId)
        );
      },
      buildSplitDetails() {
        const source = this.sources[0];
        return this.targetRows.map((row) => ({
          detailType: 2,
          sourceStockBatchId: source.stockBatchId,
          targetDetailId: row.targetDetailId,
          batchNo: source.batchNo,
          systemBatchNo: source.systemBatchNo,
          measureQuantity: decimalValue(row.measureQuantity),
          packageQuantity: decimalValue(row.packageQuantity),
          weight: decimalValue(row.weight),
          lockQuantity: decimalValue(row.lockQuantity),
          packingStockIds: this.buildPackingIds(
            source.stockBatchId,
            row.targetDetailId
          ),
          lockAllocations: this.buildLockAllocations(
            source.stockBatchId,
            row.targetDetailId
          )
        }));
      },
      buildMergeDetails() {
        return this.sources.map((source) => ({
          detailType: 1,
          sourceStockBatchId: source.stockBatchId,
          targetDetailId: 1,
          batchNo: source.batchNo,
          systemBatchNo: source.systemBatchNo,
          measureQuantity: decimalValue(source.stockCountBase),
          packageQuantity: decimalValue(source.packingQuantity),
          weight: decimalValue(source.weight),
          lockQuantity: this.sum(source.lockDetails || [], 'quantity'),
          packingStockIds: this.buildPackingIds(source.stockBatchId, 1),
          lockAllocations: this.buildLockAllocations(source.stockBatchId, 1)
        }));
      },
      async submitApply() {
        if (this.pendingApproveId) return this.continueSubmit();
        if (!this.sources.length)
          return this.$message.warning('请选择库存批次');
        if (this.mode === 'merge' && this.sources.length < 2)
          return this.$message.warning('合批至少选择两条库存');
        const attributeDifferences = this.getBatchAttributeDifferences();
        if (this.mode === 'merge' && attributeDifferences.length)
          return this.$message.warning(
            `所选库存无法合批，以下信息不一致：${attributeDifferences.join(
              '；'
            )}。`
          );
        const allocationError = this.validateAllocation();
        if (allocationError) return this.$message.warning(allocationError);
        this.saving = true;
        try {
          const source = this.sources[0];
          const saved = await stockBatchChange.save({
            changeType: this.mode === 'split' ? 1 : 2,
            warehouseId: source.warehouseId,
            warehouseName: source.warehouseName,
            remark: this.remark,
            details:
              this.mode === 'split'
                ? this.buildSplitDetails()
                : this.buildMergeDetails()
          });
          this.pendingApproveId = saved.id || saved;
          await stockBatchChange.submit({ id: this.pendingApproveId });
          this.pendingApproveId = null;
          this.$message.success('申请已提交，审批通过后生效');
          this.visible = false;
          this.$emit('success');
        } finally {
          this.saving = false;
        }
      },
      async continueSubmit() {
        this.saving = true;
        try {
          await stockBatchChange.submit({ id: this.pendingApproveId });
          this.pendingApproveId = null;
          this.$message.success('申请已提交，审批通过后生效');
          this.visible = false;
          this.$emit('success');
        } finally {
          this.saving = false;
        }
      }
    }
  };
</script>

<style scoped>
  .section-title {
    margin: 14px 0 8px;
    font-weight: 600;
  }
  .merge-summary {
    padding: 14px 0 0;
    color: #606266;
  }
  .remark {
    margin-top: 14px;
  }
  .saved-alert {
    margin-top: 10px;
  }
  .error-text {
    margin-top: 4px;
    color: #f56c6c;
    font-size: 12px;
  }
  .detail-table {
    margin-top: 14px;
  }
</style>
