yeg
2026-09-17 dd22571b369970b35ca6c48f32010e2876b43d25
rencaika_merchant/pages/scan/scan.uvue
@@ -11,6 +11,8 @@
         <cover-view class="frame-corner corner-tl"></cover-view><cover-view class="frame-corner corner-tr"></cover-view><cover-view class="frame-corner corner-bl"></cover-view><cover-view class="frame-corner corner-br"></cover-view>
      </cover-view>
      <cover-view v-if="scanState == 'scanning'" class="scan-tip">将取景框对准二维码,即可自动扫描</cover-view>
      <!-- 相机不好用或屏幕反光时的兜底:顾客屏幕上的 6 位数字码可以手输。 -->
      <cover-view v-if="scanState == 'scanning'" class="manual-entry" @tap.stop="openManualEntry">手输 6 位核销码</cover-view>
      <cover-view v-if="scanState != 'scanning'" class="status-panel">
         <cover-view v-if="scanState == 'verifying'" class="loading-spinner"></cover-view>
@@ -39,6 +41,7 @@
   let openedManualConsumption = false
   let openedConfirmation = false
   let openedVerificationFailure = false
   let openedManualEntry = false
   let cancelVerificationRequest: (() => void) | null = null
   const cancelPendingRequest = () => {
@@ -190,16 +193,27 @@
      })
   }
   // 手输 6 位数字码:扫码机上放不下普通 view(会被原生相机压住),所以入口做成 cover-view,
   // 输入本身放到一个独立页面里(cover-view 里不能放 input)。
   const openManualEntry = () => {
      openedManualEntry = true
      uni.navigateTo({
         url: '/pages/verification-code-input/verification-code-input',
         fail: () => { openedManualEntry = false }
      })
   }
   onUnload(() => {
      cancelPendingRequest()
      lastScanResult = ''
   })
   onShow(() => {
      if (!openedManualConsumption && !openedConfirmation && !openedVerificationFailure) return
      if (!openedManualConsumption && !openedConfirmation && !openedVerificationFailure && !openedManualEntry) return
      openedManualConsumption = false
      openedConfirmation = false
      openedVerificationFailure = false
      openedManualEntry = false
      resetScanning()
   })
</script>
@@ -225,6 +239,7 @@
   .corner-br { right: 0; bottom: 0; border-right-width: 7rpx; border-bottom-width: 7rpx; }
   .scan-line { width: 100%; height: 3rpx; position: absolute; top: 0; left: 0; background-color: rgba(133, 255, 192, 0.9); box-shadow: 0 0 15rpx rgba(111, 241, 170, 0.8); animation: scan-line-move 1800ms ease-in-out infinite alternate; }
   .scan-tip { width: 100%; position: absolute; top: 975rpx; left: 0; z-index: 10; color: #FFFFFF; font-size: 27rpx; line-height: 38rpx; text-align: center; }
   .manual-entry { display: flex; width: 320rpx; height: 76rpx; position: absolute; top: 1060rpx; left: 215rpx; z-index: 10; justify-content: center; align-items: center; border-width: 2rpx; border-style: solid; border-color: rgba(255, 255, 255, 0.65); border-radius: 38rpx; color: #FFFFFF; font-size: 27rpx; line-height: 38rpx; }
   .status-panel { display: none; }
   .loading-spinner { width: 58rpx; height: 58rpx; border-width: 6rpx; border-style: solid; border-color: #FFD5BF; border-top-color: #FF6B2C; border-radius: 50%; animation: loading-rotate 800ms linear infinite; }
   .status-mark { display: flex; width: 64rpx; height: 64rpx; justify-content: center; align-items: center; border-radius: 32rpx; color: #FFFFFF; font-size: 38rpx; line-height: 64rpx; font-weight: 700; text-align: center; }