<template>
	<gui-page :custom-header="true" :header-class="['gui-theme-background-color']">
		<template #gHeader>
			<view style="height:44px;" class="gui-flex gui-nowrap gui-rows gui-align-items-center">
				<!-- 使用组件实现返回按钮及返回首页按钮 -->
				<text style="font-size:44rpx;" class="gui-header-leader-btns gui-color-white font-icons"
					@tap="goBack">&#xe6c5;</text>
				<!-- 导航文本此处也可以是其他自定义内容 -->
				<text
					class="gui-h4 gui-blod gui-flex1 gui-text-center gui-ellipsis gui-color-white gui-primary-text">扫描入库明细</text>
				<!-- 此处加一个右侧展位元素与左侧同宽，实现标题居中 -->
				<!-- 实际宽度请根据自己情况设置 -->
				<view style="width:40px;" />
				<!-- 如果右侧有其他内容可以利用条件编译和定位来实现-->
			</view>
		</template>
		<template #gBody>
			<view class="list-content">
				<!-- 库位扫描输入框 -->
				<view class="scan">
					<view class="scan-card">
            <!-- 禁用软键盘 -->
              <!-- <uni-easyinput ref="easyinputStore" v-model="storeName" :input-border="false"
						:clearable="false" type="text" focus @focus="handleInputFocus" placeholder="请扫描库位信息"
						@confirm="confirmStore" /> -->
            <!-- 不禁用软键盘 -->
             <uni-easyinput ref="easyinputStore" v-model="storeName" :input-border="false"
						:clearable="false" type="text" placeholder="请扫描库位信息"
						@confirm="confirmStore" />
						<text class="font-icons" @click="handleScanStore">&#xe6b7;</text>
					</view>
				</view>

				<!-- 物料扫描输入框 -->
			<view class="scan">
				<view class="scan-card">
            <!-- 禁用软键盘 -->
					<!-- <uni-easyinput ref="easyinput" v-model="scanBatchNumber" :input-border="false"
						:clearable="false" type="text" focus @focus="handleInputFocus" placeholder="请扫描物料二维码"
						@confirm="handleKeydown" /> -->
              <!-- 不禁用软键盘 -->
              <uni-easyinput ref="easyinput" v-model="scanBatchNumber" :input-border="false"
						:clearable="false" type="text" placeholder="请扫描物料二维码"
						@confirm="handleKeydown" />
					<text class="font-icons" @click="handleScanMaterial">
						&#xe6b7;
					</text>
				</view>
			</view>
			
			<!-- 不良品是否入库单选按钮 - 仅在 businessType 为 '0' 时显示 -->
			<view class="bad-material-radio" v-if="businessType == '0'||businessType == '9'">
				<text class="radio-label">不良品是否入库：</text>
				<radio-group @change="handleBadMaterialChange" class="radio-group">
					<label class="radio-item">
						<radio value="1" :checked="badMaterialInStockFlag === '1'" color="#00a0e9" />
						<text>是</text>
					</label>
					<label class="radio-item">
						<radio value="0" :checked="badMaterialInStockFlag === '0'" color="#00a0e9" />
						<text>否</text>
					</label>
				</radio-group>
			</view>
			
			<!-- 仓库和货位信息展示 -->
			<view class="info-row">
				<text class="info-label">仓库:</text>
				<text class="info-value">{{ erpStockName }}</text>
				<text class="info-label">货位:</text>
				<text class="info-value">{{ wmsStoreName }}</text>
			</view>
		
		<view class="tabs">
					<div class="tabs-list">
						<text :class="!isBefore?'tabs-item-active':'tabs-item'" @click="isBefore = false">物料需求</text>
						<text :class="isBefore?'tabs-item-active':'tabs-item'" @click="isBefore = true">已扫物料</text>
					</div>
				</view>
				<view v-if="!isBefore" class="custom-table">
					<uni-table border stripe empty-text="暂无更多数据">
						<!-- 表头行 -->
						<uni-tr class="custom-table-head">
							<uni-th align="center" width="180px" class="break-col">推荐库位(数量)</uni-th>
							<uni-th align="center" width="180px">物料</uni-th>
							<uni-th align="center" width="120px">应备数</uni-th>
							<uni-th align="center" width="120px">已备数</uni-th>
						</uni-tr>
						<!-- 表格数据行 -->
             <!-- 可点击 -->
						<uni-tr v-for="(item, key) in tableData" :key="key" @click="handleToDetails(item)">
              <!-- 不可点击行 -->
						<!-- <uni-tr v-for="(item, key) in tableData" :key="key"> -->
							<uni-td align="center" class="break-cell">{{ item.recommendStockLocation }}</uni-td>
							<uni-td align="center">{{ item.materialNo }}({{ item.materialName }})</uni-td>
							<uni-td align="center">{{ item.nowDeliveredQty }}</uni-td>
							<uni-td align="center"
								style="color: orange;font-weight: bold;">{{ item.completedQty }}</uni-td>
						</uni-tr>
					</uni-table>
				</view>
				<!-- 已扫物料 -->
			<view v-else class="custom-table">
				<scroll-view scroll-y class="scroll-box" style="height: 300px;">
          <view class="cell-row">
							<view class="cell">物料</view>
							<view class="cell">数量</view>
							<view class="cell">批号</view>
						</view>
					<view
						v-for="(item, idx) in beforeTableData"
						:key="item.uniqueId || idx"
						class="swipe-row"
						:data-index="idx"
						@touchstart="touchStart"
						@touchmove="touchMove"
						@touchend="touchEnd"
					>
						<!-- 内容区 -->
						<view
							class="content-area"
							:style="{ transform: `translateX(${item.slideX || 0}px)` }"
						>
							<view class="cell">{{ item.materialNo }}({{ item.materialName }})</view>
							<view class="cell">{{ item.batchQty }}</view>
							<view class="cell">{{ item.batchNumber }}</view>
						</view>

						<!-- 按钮区 -->
						<view class="btn-area" @tap.stop="deleteItem(idx)">删除</view>
					</view>
				</scroll-view>
			</view>
				<view class="card-list-item"
				style="margin: 12px 0;display: grid;grid-template-columns: 1fr 1fr;grid-template-rows: 1fr;gap: 10px;">
				<view class="sign-btn" :class="{ disabled: saveDisabled }" @click="handleSave">
					<text :style="saveBtnTextStyle">保存</text>
				</view>
				<view class="sign-btn" :class="{ disabled: submitDisabled }" @click="handleSubmit">
					<text :style="submitBtnTextStyle">提交</text>
				</view>
			</view>
				<gui-modal ref="modalForm" :custom-class="['gui-bg-white', 'gui-dark-bg-level-3', 'gui-border-radius']"
					title="提示">
					<template #content>
						<view class="gui-padding gui-bg-gray gui-dark-bg-level-2">
							<text class="gui-block gui-text-center gui-text gui-color-gray"
								style="line-height:100rpx; padding:10rpx;">备料超出，是否拆分？</text>
						</view>
					</template>

					<!-- 利用 flex 布局 可以放置多个自定义按钮哦  -->
					<template #btns>
						<view class="gui-flex gui-row gui-space-between operation-flex">
							<view hover-class="gui-tap" class="modal-btns gui-flex1" @tap="modalForm.close()">
								<text class="modal-btns gui-color-gray">取消</text>
							</view>
							<view class="line" />
							<view hover-class="gui-tap" class="modal-btns gui-flex1" @tap="handleSplitMaterial">
								<text class="modal-btns gui-primary-color">确认</text>
							</view>
						</view>
					</template>
				</gui-modal>
				<uni-popup ref="errorTip" type="dialog">
							<uni-popup-dialog type="error" cancel-text="关闭" confirm-text="确认" title="提示"
								:content="errorTipMessage" @confirm="handleCloseErrorTipsModal"
								@close="handleCloseErrorTipsModal" />
						</uni-popup>
						<!-- FIFO 二次确认弹窗 -->
						<uni-popup ref="fifoPopup" type="dialog">
							<uni-popup-dialog
								title="FIFO 提示"
								:content="fifoMsg"
								confirm-text="继续"
								cancel-text="取消"
								@confirm="fifoContinue"
								@close="fifoCancel" />
						</uni-popup>
						<!-- 剩余数量输入弹窗 - 仅在 businessType 为 '3' 时使用 -->
						<uni-popup ref="leftQtyPopup" type="dialog">
						  <uni-popup-dialog
						    title="请输入剩余数量"
						    mode="input"
						    type="number"
						    placeholder="请输入剩余数量"
						    :value="leftQtyInputValue"
						    @confirm="handleLeftQtyConfirm"
						    @close="handleLeftQtyCancel"
						  />
						</uni-popup>
			</view>
		</template>
	</gui-page>
</template>

<script>
import {
  onReachBottom,
  onShow
} from '@dcloudio/uni-app'
import {
  ref,
  onMounted,
  defineComponent,
  onBeforeMount,
  computed,
  watch,
  nextTick
} from 'vue'
import {
  useSetWareHouseStore
} from '@/stores/useSetWareHouseStore.js';

export default defineComponent({
  setup(options) {
    const popup = ref()
    const queryParams = ref({
      pageSize: 20,
      pageNo: 1,
      masterId: options?.id ?? '',
      wmsProductionWorkOrderBomId: options?.id ?? ''
    })
    const errorTip = ref('')
    const errorTipMessage = ref('')
    const easyinput = ref('')
    const easyinputStore = ref('')
    const errorState = ref(0)
    const modalForm = ref()
    const parentRow = uni.getStorageSync('masterId') ?? {}
    const masterId = ref('')
    const businessType = ref('')
    const signInput = ref()
    const signText = ref('')
    const isLightText = ref('')
    const isSubmitLight = ref('')
    const scanBatchNumber = ref('')
    const isBefore = ref(false)
    const tableData = ref([])
    const beforeTableData = ref([])
    const scanMaterialList = ref([])
    // 当前处理的数据行
    const fdIndex = ref(-1)
    // 当前扫描的物料
    const currentScanMaterial = ref([])
    // 前端临时缓存：已扫物料
    const localScannedList = ref([])
    // 控制保存按钮能否点击
    const saveDisabled = ref(false)
    // 控制提交按钮能否点击
    const submitDisabled = ref(false)
    const receiveList = ref([])
	/* -------------------- 剩余数量相关变量 -------------------- */
	const leftQtyPopup = ref(null)
	const leftQtyInputValue = ref('')
	const pendingQrCode = ref('') // 缓存待处理的二维码
	const leftQtyMap = ref(new Map()) // 存储每条已扫物料的leftQty，key为 batchNumber
    
    /* -------------------- 不良品是否入库字段 -------------------- */
    const badMaterialInStockFlag = ref(null)
    
    /* -------------------- FIFO相关变量 -------------------- */
    const fifoPopup = ref(null)   // 弹窗实例
    const fifoMsg = ref('')     // 提示语
    let fifoQrCode = ''          // 缓存本次二维码
    const fifoGo = ref(false)       // 用户是否点了"继续"
    // 存储fifo_check_order_type字典数据
    const fifoCheckOrderTypeDict = ref([])
    // 标记businessType是否在fifo_check_order_type字典内
    const isBusinessTypeInFifoDict = ref(false)
	// 打开 剩余数量 弹窗
	const openLeftQtyModal = (qrCode) => {
	  pendingQrCode.value = qrCode
	  leftQtyInputValue.value = ''
	  leftQtyPopup.value.open()
	}
	
	// 确认输入 剩余数量
	const handleLeftQtyConfirm = (e) => {
	  const val = e.trim()
	  if (!val || isNaN(Number(val)) || Number(val) < 0) {
	    uni.showToast({
	      title: '请输入有效的数量',
	      icon: 'none'
	    })
	    return
	  }
	  leftQtyInputValue.value = val
	  leftQtyPopup.value.close()
	  // 继续处理扫码逻辑，传入剩余数量
	  processScanMaterial(pendingQrCode.value, Number(val))
	}
	
	// 取消输入 剩余数量
	const handleLeftQtyCancel = () => {
	  leftQtyPopup.value.close()
	  pendingQrCode.value = ''
	  leftQtyInputValue.value = ''
	}
    
    /* -------------------- FIFO校验核心方法 -------------------- */
    // 真正继续扫码（关闭弹窗并置标志）
const fifoContinue = () => {
  fifoPopup.value.close()
  fifoGo.value = true
  nextTick(() => {
    if (fifoQrCode) handleKeydown(fifoQrCode)   // 或 handleScanMaterial 里的 ret.resp_result
  })
}
    const fifoCancel = () => { fifoPopup.value.close() } // 什么都不做

    // 统一 FIFO 校验入口
const checkFifo = (qrCode) => {
  if (fifoGo.value) return Promise.resolve(true)   // 关键：放行
  if (!isBusinessTypeInFifoDict.value) return Promise.resolve(true)

  return uni.$reqPost('fifoCheck', { qrCode,
			requestNo: receiveList.value.requestNo,
			businessType: receiveList.value.businessType,
   }).then(({ code, data, msg }) => {
    if (code !== 0) {
      errorTipMessage.value = msg || 'FIFO 校验异常'
      errorTip.value.open()
      return false
    }
    if (data === null) return true
    fifoMsg.value = `当前条码（${qrCode}）不是库存中生产日期最早的物料条码（${data}），是否继续入库？`
    fifoQrCode = qrCode
    fifoPopup.value.open()
    return false
  })
}
    
    // 库位相关变量
    const storeName = ref('') // 库位编码
    const erpStockName = ref('') // 仓库名称
    const wmsStoreName = ref('') // 货位名称

    // onBeforeMount(() => {
    //   const parsedData = JSON.parse(parentRow)
    //   masterId.value = parsedData?.id
    //   businessType.value = parsedData?.businessType || '0'
    //   console.log('获取到的businessType:', businessType.value)
    //   // 获取fifo_check_order_type字典数据
    //   getFifoCheckOrderTypeDict()
    // })
    
    // 获取fifo_check_order_type字典数据
    const getFifoCheckOrderTypeDict = function() {
      uni.$reqGet('getDictDataPage', {
        dictType: 'fifo_check_order_type'
      })
      .then(({ code, data, msg }) => {
        if (code === 0) {
          fifoCheckOrderTypeDict.value = data?.list || []
          console.log('fifo_check_order_type字典数据:', fifoCheckOrderTypeDict.value)
          console.log('fifo_check_order_type字典数据长度:', fifoCheckOrderTypeDict.value.length)
          // 判断businessType是否在字典内
          checkBusinessTypeInFifoDict()
        } else {
          console.error('获取fifo_check_order_type字典失败:', msg)
        }
      })
      .catch(error => {
        console.error('获取fifo_check_order_type字典异常:', error)
      })
    }
    
    // 判断businessType是否在fifo_check_order_type字典内
    const checkBusinessTypeInFifoDict = function() {
      // 使用some方法检查businessType是否存在于字典数据中
      isBusinessTypeInFifoDict.value = fifoCheckOrderTypeDict.value.some(item => 
        String(item?.value).trim() === String(businessType.value).trim()
      )
      console.log('businessType是否在fifo_check_order_type字典内:', isBusinessTypeInFifoDict.value)
    }

    const search = function() {
      uni.$reqGet('getScannedInMatersDetails', { id: masterId.value })
        .then(({ code, data, msg }) => {
          if (code === 0) {
            // 将API返回的数据保存到receiveList
            receiveList.value = data;
            uni.setStorageSync('ids', JSON.stringify(data?.inRequestDetailPDAList?.[0] || {}))

            tableData.value = data?.inRequestDetailPDAList || []
            // 1. 先清空本地已扫列表，准备重新填充
            localScannedList.value = []
            
            // 2. 将后端返回的已扫物料数据塞进localScannedList
            tableData.value.forEach(item => {
              if (item.inRequestSubdetailList?.length) {
                localScannedList.value.push(...item.inRequestSubdetailList)
              }
            })
            
            // 3. 调用flushBeforeTableData()，以localScannedList为准重新生成beforeTableData
            flushBeforeTableData()
          } else {
            uni.showToast({
              title: msg,
              icon: 'none',
              duration: 2000
            })
          }
        })
    }

    onMounted(() => {
      search()
    })
	const initMasterId = () => {
	     const parsedData = JSON.parse(parentRow)
	     masterId.value = parsedData?.id
	    businessType.value = parsedData?.businessType || '0'
	    }
	
	onBeforeMount(() => {
	      initMasterId()
	      getFifoCheckOrderTypeDict()
	    })
	
	// 修改 onShow，只在标记存在时刷新
onShow(() => {
  initMasterId()
  if (masterId.value) {
    // 检查是否需要刷新（从详情页返回时）
    const needRefresh = uni.getStorageSync('needRefreshScanIn')
    if (needRefresh === true || needRefresh === 'true'){
      search()
      uni.removeStorageSync('needRefreshScanIn')
    }
  }
})

    const goBack = function() {
      if (uni.getStorageSync('masterId')) {
        uni.removeStorageSync('masterId')
      }
      if (uni.getStorageSync('ids')) {
        uni.removeStorageSync('ids')
      }
      uni.$goBack('/pages/workbranch/warehouse/scanInOut/In/scanInPage')
    }

    const handleScanMaterial = async function () {
      // #ifdef APP-PLUS 
      // 0. 基本校验
      if (!storeName.value) {
        plus.device.beep(2)
        errorTipMessage.value = '请先扫描库位信息'
        errorTip.value.open()
        return
      }

      // 1. 先扫码
      const mpaasScanModule = uni.requireNativePlugin('Mpaas-Scan-Module')
      const ret = await new Promise(resolve =>
        mpaasScanModule.mpaasScan(
          { scanType: ['qrCode', 'barCode'], hideAlbum: false },
          resolve
        )
      )
      if (ret.resp_code !== 1000) return        // 用户取消

      const qrCode = ret.resp_result

      // 2. FIFO 校验（暂时注释）
      // const pass = await checkFifo(qrCode)
      // if (!pass) return // 用户点了"取消"
	  const pass = true          // FIFO校验直接放行

      // 3. 业务接口
      uni.$reqGet('scanPrepareMaterial', {
        qrCode: qrCode,
        locationCode: storeName.value // 传入库位编码
      })
      .then(async ({ code, data, msg }) => {
        if (code === 0) {
          scanBatchNumber.value = qrCode
          // 更新仓库和货位名称（直接覆盖）
          // erpStockName.value = data[0]?.erpStockName || ''
          // wmsStoreName.value = data[0]?.wmsStoreName || ''
		  // 更新仓库和货位名称（非空才覆盖，保留原有值）
          // erpStockName.value = data[0]?.erpStockName || erpStockName.value
          // wmsStoreName.value = data[0]?.wmsStoreName || wmsStoreName.value
          if (Object.prototype.toString.call(data) === '[object Array]') {
            fdIndex.value = tableData.value.findIndex(item => item?.materialNo === data[0]?.materialNo)
          }
          if (fdIndex.value !== -1) {
            currentScanMaterial.value = data[0] ?? []
            // 校验批号是否已存在
            if (isBatchExist(currentScanMaterial.value.batchNumber)) {
              showBatchRepeatTip();
              return;
            }
            const row = tableData.value[fdIndex.value]
            if (row.nowDeliveredQty > row.completedQty) {
              /* ********  前端暂存开始  ******** */
              // 1) 累加到"已备数"
              row.completedQty += currentScanMaterial.value.receiptQty || 1

              // 2) 扔进本地已扫池（用于已扫物料列表展示）
              localScannedList.value.push({
                ...currentScanMaterial.value,
                batchQty: currentScanMaterial.value.receiptQty || 1, // 本次扫描数量
                supplierName: currentScanMaterial.value.supplierName || ''
              })

              // 3) 刷新已扫物料列表
              flushBeforeTableData()

              // 4) 复位输入框，准备下一次扫码
              setInputFocus()
              /* ********  前端暂存结束  ******** */
            } else {
              // #ifdef APP-PLUS
              plus.device.beep(2)
              // #endif
              errorTipMessage.value = '已备数量已满，无需再扫'
              errorTip.value.open()
              errorState.value = 0
            }
          } else {
            // #ifdef APP-PLUS
            plus.device.beep(2)
            // #endif
            errorTipMessage.value = '请扫描所需物料的物料条码'
            errorTip.value.open()
            errorState.value = 0
          }
        } else {
          // #ifdef APP-PLUS
          plus.device.beep(2)
          // #endif
          errorTipMessage.value = msg
          errorTip.value.open()
          errorState.value = 0
        }
      })
      .catch((err) => {
        // #ifdef APP-PLUS
        plus.device.beep(2)
        // #endif
        errorTipMessage.value = err.msg || '获取物料信息失败'
        errorTip.value.open()
        errorState.value = 0
      }).finally(() => {
        fifoGo.value = false   // ★★★  复位：一次放行即失效  ★★★
      })
      // #endif
    }

    // 物料拆分
    const handleSplitMaterial = function() {
      modalForm.value.close()
      setInputFocus()
    }

    const handleKeydown = async function(e) {
      // 校验库位信息是否存在
      if (!storeName.value) {
        // #ifdef APP-PLUS
        plus.device.beep(2)
        // #endif
        errorTipMessage.value = '请先扫描库位信息'
        errorTip.value.open()
        errorState.value = 0
        return
      }
      
      // FIFO 校验（暂时关闭）
      // const pass = await checkFifo(e)
      // if (!pass && !fifoGo.value) return
		const pass = true          // FIFO校验直接放行
        uni.$reqGet('scanPrepareMaterial', {
          qrCode: e,
          locationCode: storeName.value // 传入库位编码
        })
        .then(async({ code, data, msg }) => {
          fifoGo.value = false
          if (code === 0) {
            scanBatchNumber.value = e
            // 更新仓库和货位名称（直接覆盖）
            // erpStockName.value = data[0]?.erpStockName || ''
            // wmsStoreName.value = data[0]?.wmsStoreName || ''
			// 更新仓库和货位名称（非空才覆盖，保留原有值）
            // erpStockName.value = data[0]?.erpStockName || erpStockName.value
            // wmsStoreName.value = data[0]?.wmsStoreName || wmsStoreName.value
            if (Object.prototype.toString.call(data) === '[object Array]') {
              fdIndex.value = tableData.value.findIndex(item => item?.materialNo === data[0]?.materialNo)
            }
            if (fdIndex.value !== -1) {
              currentScanMaterial.value = data[0] ?? []
              // 校验批号是否已存在
              if (isBatchExist(currentScanMaterial.value.batchNumber)) {
                showBatchRepeatTip();
                return;
              }
              const row = tableData.value[fdIndex.value]
              if (row.nowDeliveredQty > row.completedQty) {
                /* ********  前端暂存开始  ******** */
                // 1) 累加到"已备数"
                row.completedQty += currentScanMaterial.value.receiptQty || 1

                // 2) 扔进本地已扫池（用于已扫物料列表展示）
                localScannedList.value.push({
                  ...currentScanMaterial.value,
                  batchQty: currentScanMaterial.value.receiptQty || 1, // 本次扫描数量
                  supplierName: currentScanMaterial.value.supplierName || ''
                })

                // 3) 刷新已扫物料列表
                flushBeforeTableData()

                // 4) 复位输入框，准备下一次扫码
                setInputFocus()
                /* ********  前端暂存结束  ******** */
              } else {
                // #ifdef APP-PLUS
                plus.device.beep(2)
                // #endif
                errorTipMessage.value = '已备数量已满，无需再扫'
                errorTip.value.open()
                errorState.value = 0
              }
            } else {
              // #ifdef APP-PLUS
              plus.device.beep(2)
              // #endif
              errorTipMessage.value = '请扫描所需物料的物料条码'
              errorTip.value.open()
              errorState.value = 0
            }
          } else {
            // #ifdef APP-PLUS
            plus.device.beep(2)
            // #endif
            errorTipMessage.value = msg
            errorTip.value.open()
            errorState.value = 0
          }
        })
        .catch((err) => {
          // #ifdef APP-PLUS
          plus.device.beep(2)
          // #endif
          errorTipMessage.value = err.msg || '获取物料信息失败'
          errorTip.value.open()
          errorState.value = 0
        }).finally(() => {
          fifoGo.value = false   // ★★★  复位：一次放行即失效  ★★★
        })
      
    }

    const handleToDetails = function(ret) {
      // 设置刷新标记，从详情页返回时需要刷新
      uni.setStorageSync('needRefreshScanIn', true)
      
      uni.navigateTo({
        url: '/pages/workbranch/warehouse/scanInOut/In/materialsDetail'
      })
      uni.setStorageSync('mixMaterialDetail', JSON.stringify(ret))
    }



    const setInputFocus = function() {
      scanBatchNumber.value = ''
      easyinput.value.onBlur()
      easyinput.value.onFocus()
    }

    // 相同物料+相同批号才合并数量，否则新增一行
    const flushBeforeTableData = function() {
      const map = new Map()
      localScannedList.value.forEach(it => {
        // 用"物料编码+批号"当唯一键
        const key = `${it.materialNo}__${it.batchNumber}`
        if (map.has(key)) {
          map.get(key).batchQty += it.batchQty // 同批号累加
        } else {
          map.set(key, { ...it })// 新批号新开一行
        }
      })
      // 为每个项目添加唯一ID和滑动属性
      beforeTableData.value = Array.from(map.values()).map((v, i) => ({
        ...v,
        uniqueId: `${v.materialNo}_${v.batchNumber}_${i}`,
        slideX: 0
      }))
    }

    /* ========= 左滑删除 ========= */
    const touchStart = (e) => {
      const idx = e.currentTarget.dataset.index
      beforeTableData.value.forEach((v, i) => {
        if (i !== idx) v.slideX = 0        // 其余归位
      })
      beforeTableData.value[idx].startX = e.touches[0].pageX
      beforeTableData.value[idx].slideX  = beforeTableData.value[idx].slideX || 0
    }

    const touchMove = (e) => {
      const idx = e.currentTarget.dataset.index
      const row = beforeTableData.value[idx]
      const delta = e.touches[0].pageX - row.startX
      if (delta < 0) {                      // 只允许左滑
        row.slideX = Math.max(delta, -70)   // 70 = 按钮宽度
      } else {
        row.slideX = Math.min(delta, 0)
      }
    }

    const touchEnd = (e) => {
      const idx = e.currentTarget.dataset.index
      const row = beforeTableData.value[idx]
      row.slideX = row.slideX <= -35 ? -70 : 0   // 过半则露出，否则收回
    }

    /** 
     * 删除一条已扫物料 
     * @param {number} idx 在 beforeTableData 中的下标 
     */ 
    const deleteItem = async (idx) => { 
      const target = beforeTableData.value[idx]; 

      /* 1. 有 id 就先调接口删后端 */ 
      if (target.id) { 
        try { 
          const { code, msg } = await uni.$reqPost('deleteScanInMaterial', { id: target.id }); 
          if (code !== 0) { 
            // 接口明确返回失败，提示用户并终止后续逻辑 
            errorTipMessage.value = msg || '后端删除失败'; 
            errorTip.value.open(); 
            return; 
          } 
        } catch (err) { 
          // 网络或其它异常 
          errorTipMessage.value = err.errMsg || '网络异常，删除失败'; 
          errorTip.value.open(); 
          return; 
        } 
      } 

      /* 2. 无论有没有 id、接口成不成功，只要走到这里就删本地 */ 
      // 2.1 从 localScannedList 里删掉对应项（同物料+同批号） 
      const key = `${target.materialNo}__${target.batchNumber}`; 
      const listIdx = localScannedList.value.findIndex( 
        v => `${v.materialNo}__${v.batchNumber}` === key 
      ); 
      if (listIdx > -1) localScannedList.value.splice(listIdx, 1); 

      // 2.2 重新合并生成 beforeTableData 
      flushBeforeTableData(); 

      // 2.3 把对应物料的"已备数"回退 
      const row = tableData.value.find(r => r.materialNo === target.materialNo); 
      if (row) row.completedQty -= target.batchQty; 
    };
    
    // 关闭错误信息弹窗
    const handleCloseErrorTipsModal = async function() {
      errorTip.value.close()
      if (errorState.value === 0) {
        await setInputFocus()
      }
    }
    
    /* ===================== 公共校验函数 ===================== */
    // 仅扫描本地缓存池，不再读 beforeTableData
    const isBatchExist = (batchNo) =>
      beforeTableData.value.some((it) => it.batchNumber === batchNo);

    const showBatchRepeatTip = () => {
      // #ifdef APP-PLUS
      plus.device.beep(2);
      // #endif
      errorTipMessage.value = '该批号已存在，请勿重复扫描';
      errorTip.value.open();
      errorState.value = 0;
    };
    
    // 禁用软键盘
    const handleInputFocus = function() {
      setTimeout(() => {
        uni.hideKeyboard()
      }, 100)
    }
    
    // 处理库位扫码
    const handleScanStore = function() {
      // #ifdef APP-PLUS
      const mpaasScanModule = uni.requireNativePlugin('Mpaas-Scan-Module')
      mpaasScanModule.mpaasScan({
              // 扫码识别类型，参数可多选，qrCode、barCode，不设置，默认识别所有
              'scanType': ['qrCode', 'barCode'],
              // 是否隐藏相册，默认false不隐藏
              'hideAlbum': false
            },
            (ret) => {
              if (ret.resp_code === 1000) {
                confirmStore(ret.resp_result)
              }
            })
      // #endif
      // #ifdef H5
      confirmStore('PC-1')
      // #endif
    }
    
    // 扫描货位输入框回车事件
    const confirmStore = function(e) {
      // 保持输入框里的原始数据不变
      storeName.value = e
      
      uni.$reqGet('getStockLocaltionByCode', {
        code: e
      })
      .then(({ code, data, msg }) => {
        if (code === 0) {
          // 验证是否获取到了货位和仓库信息
          if (!data || !data.erpStockName || !data.wmsStoreName) {
            // #ifdef APP-PLUS
            plus.device.beep(2)
            // #endif
            errorTipMessage.value = '请扫描正确的库位'
            errorTip.value.open()
            errorState.value = 0
            // 清空扫描框
            storeName.value = ''
            return
          }
          erpStockName.value = data.erpStockName // 仓库名称
          wmsStoreName.value = data.wmsStoreName // 货位名称
          // 关键：让物料输入框获得焦点
          nextTick(() => {
            easyinput.value.onFocus()
          })
        } else {
          // #ifdef APP-PLUS
          plus.device.beep(2)
          // #endif
          errorTipMessage.value = '请扫描正确的库位'
          errorTip.value.open()
          errorState.value = 0
          // 清空扫描框
          storeName.value = ''
        }
      })
    }
    
    // 重置库位输入框
    const setStoreInputFocus = function() {
      storeName.value = ''
      easyinputStore.value.onBlur()
      easyinputStore.value.onFocus()
    }
    
    // 把 beforeTableData 追加到 tableData 对应物料的 inRequestSubdetailList
    const mergeBeforeIntoTable = () => {
      // 创建新数组存储合并结果
      const mergedTableData = JSON.parse(JSON.stringify(tableData.value))
      // 先建索引
      const map = {}
      mergedTableData.forEach(row => {
        if (!row.inRequestSubdetailList) row.inRequestSubdetailList = []
        map[row.materialNo] = row
      })

      // 只追加，不新增
      beforeTableData.value.forEach(item => {
        const target = map[item.materialNo]
        if (target) {
          target.inRequestSubdetailList.push({ 
            ...item,
			leftQty: null
          }) // 整条记录丢进去
        }
        // 不存在就跳过
      })
      
      return mergedTableData
    }
    
    // 保存按钮点击事件
    const handleSave = async function() {
      if (saveDisabled.value) return
	  if ((businessType.value == '0' || businessType.value == '9') && badMaterialInStockFlag.value === null) {
	      uni.showToast({
	        title: '请选择不良品是否入库',
	        icon: 'none',
	        duration: 2000
	      })
	      return
	    }
      saveDisabled.value = true
      try {
        // 保存逻辑实现
        isLightText.value = true
        // 先合并数据，获取合并后的新数组
        const mergedData = mergeBeforeIntoTable()
        // 调用保存API，使用合并后的新数组作为参数
        await uni.$reqPost('saveScannedInMaterials', {
          id: receiveList.value?.id,
          requestNo: receiveList.value?.requestNo,
          requestType: receiveList.value?.requestType,
          businessType: receiveList.value?.businessType,
          businessSubType: receiveList.value?.businessSubType,
          status: receiveList.value?.status,
          priority: receiveList.value?.priority,
          badMaterialInStockFlag: badMaterialInStockFlag.value,
          inRequestDetailPDAList: mergedData
        })
          .then(({ code, data, msg }) => {
            if (code === 0) {
              uni.showModal({
                title: '提示',
                content: '保存成功',
                showCancel: false
              })
              search();
            } else {
              // #ifdef APP-PLUS
              plus.device.beep(2)
              // #endif
              errorTipMessage.value = msg
              errorTip.value.open()
              errorState.value = 0
            }
          })
      } finally {
        saveDisabled.value = false
      }
    }
    
    // 提交按钮点击事件
    const handleSubmit = async function() {
      if (submitDisabled.value) return
	  if ((businessType.value == '0' || businessType.value == '9') && badMaterialInStockFlag.value === null) {
	      uni.showToast({
	        title: '请选择不良品是否入库',
	        icon: 'none',
	        duration: 2000
	      })
	      return
	    }
      submitDisabled.value = true
      try {
        // 提交逻辑实现
        isSubmitLight.value = true
        // 先合并数据，获取合并后的新数组
        const mergedData = mergeBeforeIntoTable()
        // 调用提交API，使用合并后的新数组作为参数
        await uni.$reqPut('submitScannedInMaterials', {
          id: receiveList.value?.id,
          requestNo: receiveList.value?.requestNo,
          requestType: receiveList.value?.requestType,
          businessType: receiveList.value?.businessType,
          businessSubType: receiveList.value?.businessSubType,
          status: receiveList.value?.status,
          priority: receiveList.value?.priority,
          badMaterialInStockFlag: badMaterialInStockFlag.value,
          inRequestDetailPDAList: mergedData
        })
          .then(({ code, data, msg }) => {
            if (code === 0) {
              uni.showModal({
                title: '提示',
                content: '提交成功',
                showCancel: false
              })
              search();
            } else {
              // #ifdef APP-PLUS
              plus.device.beep(2)
              // #endif
              errorTipMessage.value = msg
              errorTip.value.open()
              errorState.value = 0
            }
          })
      } finally {
        submitDisabled.value = false
      }
    }

    // uniapp移动端触底事件
    // onReachBottom(() => {
    //   queryParams.value.pageNo += 1
    //   uni.$reqGet('getPrepareMaterialList', queryParams.value)
    //     .then(({ data }) => {
    //       Array.prototype.push.call(scanMaterialList.value, ...data?.list ?? [])
    //     })
    // })
    
    // 保存按钮文字样式
    const saveBtnTextStyle = computed(() => ({
      fontSize: '14px',
      fontWeight: 'bold',
      color: saveDisabled.value ? '#ccc' : (isLightText.value ? 'rgba(0,160,233,1)' : '')
    }))
    
    // 提交按钮文字样式
    const submitBtnTextStyle = computed(() => ({
      fontSize: '14px',
      fontWeight: 'bold',
      color: submitDisabled.value ? '#ccc' : (isSubmitLight.value ? 'rgba(0,160,233,1)' : '')
    }))
    
    // 处理不良品是否入库单选按钮变化
    const handleBadMaterialChange = function(e) {
      badMaterialInStockFlag.value = e.detail.value
    }
    
    // 移除status监听，提交按钮和保存按钮均始终可用
    // watch(() => receiveList.value?.status, (newStatus) => {
    //   submitDisabled.value = newStatus !== 0
    // })
    
    return {
      goBack,
      popup,
      signText,
      isBefore,
      tableData,
      beforeTableData,
      signInput,
      modalForm,
      easyinput,
      easyinputStore,
      errorTip,
      errorTipMessage,
      handleInputFocus,
      isLightText,
      isSubmitLight,
      handleKeydown,
      scanBatchNumber,
      handleScanMaterial,
      scanMaterialList,
      handleSplitMaterial,
      handleToDetails,
      handleCloseErrorTipsModal,
      handleSave,
      handleSubmit,
      saveDisabled,
      submitDisabled,
      saveBtnTextStyle,
      submitBtnTextStyle,
      // 库位相关
        storeName,
        erpStockName,
        wmsStoreName,
        
        handleScanStore,
        confirmStore,
      // 左滑删除相关
      touchStart,
      touchMove,
      touchEnd,
      deleteItem,
      // FIFO相关
      fifoPopup,
      fifoMsg,
      fifoContinue,
      fifoCancel,
      // 不良品是否入库相关
      businessType,
      badMaterialInStockFlag,
      handleBadMaterialChange
    }
  }
})
</script>

<style lang="scss" scoped>
	/* 左滑删除样式 */
	.scroll-box{
		height:100%;                 /* 继承父级高度 */
	}
  .cell-row {
		display: flex;
		background-color: #f5f7fa;
		font-weight: bold;
		border-radius: 4px;
		margin-bottom: 5px;
	}
	.swipe-row{
		position:relative;
		width:100%;
		height:44px;
		overflow:hidden;
		border-bottom:1px solid #eee;
	}
	.content-area{
		position:absolute;
		left:0;
		top:0;
		right:0;
		bottom:0;
		z-index:2;
		background:#fff;
		display:flex;
		align-items:center;
		transition: transform .25s;
	}
	.cell{
		flex:1;
		text-align:center;
		font-size:14px;
		color:#333;
	}
	.btn-area{
		position:absolute;
		right:0;
		top:0;
		width:70px;
		height:100%;
		background:#e54d42;
		color:#fff;
		display:flex;
		align-items:center;
		justify-content:center;
		font-size:14px;
		z-index:1;
	}

	.gui-header-leader-btns {
		color: black;
		font-size: 24px !important;
		margin-left: 24rpx;
	}

	.list-content {
		margin-top: 80px;
		background-color: #edeeee;
	}

	.scan {
		margin-top: 10px;
		height: 55px;
		display: flex;
		justify-content: center;
		background-color: white;
	}

	.scan-card {
		display: flex;
		flex-direction: row;
		justify-content: space-between;
		align-items: center;
		width: 100%;
		padding-left: 10px;
		padding-right: 10px;
	}

	.store-info {
		margin-top: 10px;
		height: 140px;
		display: flex;
		justify-content: center;
		background-color: white;
	}

	.store-info-card {
		display: flex;
		flex-direction: column;
		
		.row-space-between {
			flex: 1;
			width: calc(100vw - 40px);
			display: flex;
			flex-direction: row;
			justify-content: space-between;
			align-items: center;
			padding-top: 12px;
			padding-bottom: 12px;
		}
	}

	.card-list-flexbox {
		display: flex;
		flex-direction: row;
		align-items: center;
		flex-wrap: wrap;
		margin: 3px 2px;

		.card-list-item {
			width: 750rpx;
			height: 40px;
		}
	}
	
	.card-list-item {
		margin: 2rpx 0;
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		background-color: #fff;

		uni-text {
			font-size: 14px;
			height: 50rpx;
			text-align: left;
			padding: 0 12px;
			display: flex;
			flex-direction: row;
			align-items: center;
		}

		.text-1 {
			flex: 1;
			height: 40px;
			justify-content: flex-start;
		}

		.text-2 {
			flex: 3;
			height: 40px;
			justify-content: flex-end;
			margin-right: 4px;
			padding: 2px 6px;
		}
	}

	.card-list-flexbox:nth-of-type(2) {
		margin-top: 48px;
	}

	.fixedTop {
		bottom: 0 !important;
		top: 3.25rem !important;
	}

	.popup-content {
		height: 75vh;
		overflow-y: scroll;
		background-color: #edeeee;
	}

	.font-icons {
		width: 40px;
		font-size: 20px;
	}

	.scan {
		height: 45px;
		width: calc(100% - 48px);
		margin: 12px;
		padding: 0 12px;
		display: flex;
		justify-content: space-between;
		align-items: center;
		border-radius: 6px;
		background-color: white;

		.scan-card {
			width: 100%;
			display: grid;
			grid-template-rows: 1fr;
			grid-template-columns: 7fr 2fr;
			align-items: center;

			input {
				height: 35px;
				line-height: 35px;
			}

			text {
				width: 100%;
				text-align: right;
			}
		}
	}

	.custom-table {
		height: calc(100vh - 265px);
		min-height: 230px;
		margin: 5px 0;
		// min-height: 600px;
		overflow-y: scroll;
	}

  .custom-table-head .uni-th {
    color: #333;
    font-weight: bold;
  }

  .custom-table .uni-tr:nth-child(even) {
    background-color: #f5f7fa;
  }

  .custom-table .uni-tr:nth-child(odd) {
    background-color: #ffffff;
  }

  /* 仓库和货位信息展示样式 */
  .info-row {
    display: flex;
    align-items: center;
    padding: 10rpx 20rpx;
    background-color: #f0f2f5;
    border-radius: 8rpx;
    margin: 10rpx 20rpx;
  }

  .info-label {
    color: #666;
    font-size: 28rpx;
    margin-right: 8rpx;
  }

  .info-value {
    color: #333;
    font-size: 28rpx;
    margin-right: 20rpx;
    font-weight: 500;
  }

  /* 不良品是否入库单选按钮样式 */
  .bad-material-radio {
    display: flex;
    align-items: center;
    padding: 10rpx 24rpx;
    background-color: #fff;
    margin: 0 24rpx 10rpx 24rpx;
    border-radius: 6px;
    
    .radio-label {
      font-size: 28rpx;
      color: #333;
      margin-right: 20rpx;
      white-space: nowrap;
    }
    
    .radio-group {
      display: flex;
      flex-direction: row;
    }
    
    .radio-item {
      display: flex;
      align-items: center;
      margin-right: 30rpx;
      
      radio {
        transform: scale(0.8);
      }
      
      text {
        font-size: 28rpx;
        color: #333;
        margin-left: 8rpx;
      }
    }
  }

	.modal-btns {
		height: 100rpx;
		line-height: 100rpx;
		display: flex;
		justify-content: center;
		align-items: center;
	}

	.line {
		margin-top: 10rpx;
		height: 80rpx;
		width: 1rpx;
		background-color: #dcdcdc;
	}

	.tabs {
		width: 100%;
		height: 45px;
		display: flex;
		align-items: flex-end;
		padding: 0 2px;
		background-color: white;
		
		.tabs-list {
			border-radius: 3px;
			overflow: hidden;
		}
	
		.tabs-item {
			display: inline-block;
			width: 72px;
			height: 30px;
			line-height: 30px;
			padding: 0 8px;
			font-size: 14px;
			font-weight: bold;
			text-align: center;
			color: black;
			border-bottom: 1.5px dashed #00a0e9;
			transition: all .5s ease-in-out;
		}
	
		.tabs-item-active {
			position: relative;
			display: inline-block;
			width: 72px;
			height: 30px;
			line-height: 30px;
			padding: 0 8px;
			font-size: 14px;
			font-weight: bold;
			text-align: center;
			color: white;
			border-left: 1px solid #00a0e9;
			border-top: 1px solid #00a0e9;
			border-right: 1px solid #00a0e9;
			border-bottom: 1.5px solid #00a0e9;
			border-radius: 5px 5px 0 0;
			animation: .3s linear show;
			background-color: #00a0e9;
		}
	
		.tabs-item-active::before {
			content: '';
			position: absolute;
			left: -10px;
			bottom: 0;
			width: 10px;
			height: 10px;
			background: radial-gradient(circle at 0% 25%, transparent 10px, #00a0e9 0)
		}
	
		.tabs-item-active::after {
			content: '';
			position: absolute;
			right: -10px;
			bottom: 0;
			width: 10px;
			height: 10px;
			background: radial-gradient(circle at 100% 25%, transparent 10px, #00a0e9 0)
		}
	}
	
	@keyframes show {
		from {
			transform: translateY(5%);
		}
	
		to {
			transform: translateY(0%);
		}
	}

	.sign-btn {
		display: flex;
		align-items: center;
		justify-content: center;
		margin: 0 8px;
		border: 1px solid #999999;
		background-color: white;
		border-radius: 6px;
	}

	.sign-btn.disabled {
		opacity: 0.5;
		pointer-events: none;
	}
	// /* 表头也允许换行，保证与列宽对齐 */
	// ::v-deep .break-col {
	//   white-space: normal !important;
	//   word-break: break-all !important;
	//   line-height: 1.2;
	// }
	
	// /* 单元格超长自动分段 */
	// ::v-deep .break-cell {
	//   white-space: normal !important;
	//   word-break: break-all !important;
	//   line-height: 1.2;
	//   padding: 4px 2px;          /* 上下留一点空隙，视觉更舒服 */
	// }
</style>
