|
|
@@ -1,516 +0,0 @@
|
|
|
-<template>
|
|
|
- <view class="scan-container">
|
|
|
- <!-- 顶部导航栏 -->
|
|
|
- <view class="nav-bar">
|
|
|
- <view class="nav-left" @click="goBack">
|
|
|
- <text class="iconfont icon-back"></text>
|
|
|
- </view>
|
|
|
- <view class="nav-title">扫码支付</view>
|
|
|
- <view class="nav-right">
|
|
|
- <text class="iconfont icon-album" @click="chooseImage"></text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 扫码区域 -->
|
|
|
- <view class="scan-area">
|
|
|
- <view class="scan-frame">
|
|
|
- <view class="scan-line"></view>
|
|
|
- <view class="scan-corners">
|
|
|
- <view class="corner top-left"></view>
|
|
|
- <view class="corner top-right"></view>
|
|
|
- <view class="corner bottom-left"></view>
|
|
|
- <view class="corner bottom-right"></view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="scan-tip">将二维码放入框内,即可自动扫描</view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 底部操作区域 -->
|
|
|
- <view class="bottom-actions">
|
|
|
- <view class="action-item" @click="toggleFlash">
|
|
|
- <view class="action-icon" :class="{ active: flashOn }">
|
|
|
- <text class="iconfont icon-flashlight"></text>
|
|
|
- </view>
|
|
|
- <text class="action-text">{{ flashOn ? '关闭闪光灯' : '开启闪光灯' }}</text>
|
|
|
- </view>
|
|
|
- <view class="action-item" @click="chooseImage">
|
|
|
- <view class="action-icon">
|
|
|
- <text class="iconfont icon-album"></text>
|
|
|
- </view>
|
|
|
- <text class="action-text">相册</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-
|
|
|
- <!-- 扫码结果弹窗 -->
|
|
|
- <view class="scan-result-modal" v-if="showResultModal" @click="closeResultModal">
|
|
|
- <view class="modal-content" @click.stop>
|
|
|
- <view class="modal-header">
|
|
|
- <text class="modal-title">扫码结果</text>
|
|
|
- <text class="modal-close" @click="closeResultModal">×</text>
|
|
|
- </view>
|
|
|
- <view class="modal-body">
|
|
|
- <view class="result-info">
|
|
|
- <view class="result-item">
|
|
|
- <text class="label">订单金额:</text>
|
|
|
- <text class="value">¥{{ scanResult.amount || '0.00' }}</text>
|
|
|
- </view>
|
|
|
- <view class="result-item" v-if="scanResult.description">
|
|
|
- <text class="label">订单描述:</text>
|
|
|
- <text class="value">{{ scanResult.description }}</text>
|
|
|
- </view>
|
|
|
- <view class="result-item" v-if="scanResult.merchantId">
|
|
|
- <text class="label">商户ID:</text>
|
|
|
- <text class="value">{{ scanResult.merchantId }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="modal-footer">
|
|
|
- <button class="modal-btn cancel" @click="closeResultModal">取消</button>
|
|
|
- <button class="modal-btn confirm" @click="goToPay">确认支付</button>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-import { parseScanResult } from '@/api/payment';
|
|
|
-
|
|
|
-export default {
|
|
|
- name: 'ScanPage',
|
|
|
- data() {
|
|
|
- return {
|
|
|
- flashOn: false,
|
|
|
- showResultModal: false,
|
|
|
- scanResult: {},
|
|
|
- scanning: false
|
|
|
- }
|
|
|
- },
|
|
|
- onLoad() {
|
|
|
- this.startScan();
|
|
|
- },
|
|
|
- onUnload() {
|
|
|
- this.stopScan();
|
|
|
- },
|
|
|
- methods: {
|
|
|
- // 返回上一页
|
|
|
- goBack() {
|
|
|
- uni.navigateBack();
|
|
|
- },
|
|
|
-
|
|
|
- // 开始扫码
|
|
|
- startScan() {
|
|
|
- // #ifdef APP-PLUS
|
|
|
- this.startCameraScan();
|
|
|
- // #endif
|
|
|
-
|
|
|
- // #ifdef H5
|
|
|
- this.startH5Scan();
|
|
|
- // #endif
|
|
|
- },
|
|
|
-
|
|
|
- // 停止扫码
|
|
|
- stopScan() {
|
|
|
- // #ifdef APP-PLUS
|
|
|
- if (this.cameraContext) {
|
|
|
- this.cameraContext.stopRecord();
|
|
|
- }
|
|
|
- // #endif
|
|
|
- },
|
|
|
-
|
|
|
- // APP端扫码
|
|
|
- startCameraScan() {
|
|
|
- // #ifdef APP-PLUS
|
|
|
- uni.scanCode({
|
|
|
- success: (res) => {
|
|
|
- this.handleScanResult(res.result);
|
|
|
- },
|
|
|
- fail: (err) => {
|
|
|
- console.error('扫码失败:', err);
|
|
|
- uni.showToast({
|
|
|
- title: '扫码失败',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- // #endif
|
|
|
- },
|
|
|
-
|
|
|
- // H5端扫码
|
|
|
- startH5Scan() {
|
|
|
- // H5端需要用户手动选择图片进行扫码
|
|
|
- uni.showToast({
|
|
|
- title: '请点击相册选择二维码图片',
|
|
|
- icon: 'none',
|
|
|
- duration: 2000
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- // 处理扫码结果
|
|
|
- handleScanResult(result) {
|
|
|
- if (this.scanning) return;
|
|
|
- this.scanning = true;
|
|
|
-
|
|
|
- try {
|
|
|
- // 解析扫码结果
|
|
|
- const parsedResult = parseScanResult(result);
|
|
|
-
|
|
|
- if (parsedResult) {
|
|
|
- this.scanResult = parsedResult;
|
|
|
- this.showResultModal = true;
|
|
|
- } else {
|
|
|
- uni.showToast({
|
|
|
- title: '无效的二维码',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('处理扫码结果失败:', error);
|
|
|
- uni.showToast({
|
|
|
- title: '扫码失败',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- } finally {
|
|
|
- this.scanning = false;
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 选择图片
|
|
|
- chooseImage() {
|
|
|
- uni.chooseImage({
|
|
|
- count: 1,
|
|
|
- sizeType: ['compressed'],
|
|
|
- sourceType: ['album'],
|
|
|
- success: (res) => {
|
|
|
- const tempFilePath = res.tempFilePaths[0];
|
|
|
- this.scanImage(tempFilePath);
|
|
|
- },
|
|
|
- fail: (err) => {
|
|
|
- console.error('选择图片失败:', err);
|
|
|
- uni.showToast({
|
|
|
- title: '选择图片失败',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- // 扫描图片中的二维码
|
|
|
- scanImage(imagePath) {
|
|
|
- // #ifdef APP-PLUS
|
|
|
- uni.scanCode({
|
|
|
- scanType: ['qrCode'],
|
|
|
- path: imagePath,
|
|
|
- success: (res) => {
|
|
|
- this.handleScanResult(res.result);
|
|
|
- },
|
|
|
- fail: (err) => {
|
|
|
- console.error('图片扫码失败:', err);
|
|
|
- uni.showToast({
|
|
|
- title: '未识别到二维码',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- // #endif
|
|
|
-
|
|
|
- // #ifdef H5
|
|
|
- // H5端模拟扫码结果
|
|
|
- setTimeout(() => {
|
|
|
- const mockResult = 'https://example.com/pay?orderId=ORDER123456&amount=99.99&description=测试订单&merchantId=MERCHANT001';
|
|
|
- this.handleScanResult(mockResult);
|
|
|
- }, 1000);
|
|
|
- // #endif
|
|
|
- },
|
|
|
-
|
|
|
- // 切换闪光灯
|
|
|
- toggleFlash() {
|
|
|
- this.flashOn = !this.flashOn;
|
|
|
- // #ifdef APP-PLUS
|
|
|
- if (this.cameraContext) {
|
|
|
- this.cameraContext.toggleTorch({
|
|
|
- success: () => {
|
|
|
- console.log('闪光灯状态切换成功');
|
|
|
- },
|
|
|
- fail: (err) => {
|
|
|
- console.error('闪光灯切换失败:', err);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- // #endif
|
|
|
- },
|
|
|
-
|
|
|
- // 关闭结果弹窗
|
|
|
- closeResultModal() {
|
|
|
- this.showResultModal = false;
|
|
|
- this.scanResult = {};
|
|
|
- },
|
|
|
-
|
|
|
- // 跳转到支付页面
|
|
|
- goToPay() {
|
|
|
- this.closeResultModal();
|
|
|
-
|
|
|
- // 跳转到支付页面,传递扫码结果
|
|
|
- uni.navigateTo({
|
|
|
- url: `/admin/ghs/pay?amount=${this.scanResult.amount || '0.00'}&orderId=${this.scanResult.orderId || ''}&description=${encodeURIComponent(this.scanResult.description || '')}`
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-</script>
|
|
|
-
|
|
|
-<style lang="scss" scoped>
|
|
|
-.scan-container {
|
|
|
- min-height: 100vh;
|
|
|
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-
|
|
|
-.nav-bar {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- padding: 20rpx 30rpx;
|
|
|
- background: rgba(255, 255, 255, 0.1);
|
|
|
- backdrop-filter: blur(10px);
|
|
|
-
|
|
|
- .nav-left, .nav-right {
|
|
|
- width: 60rpx;
|
|
|
- height: 60rpx;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- }
|
|
|
-
|
|
|
- .nav-title {
|
|
|
- color: #fff;
|
|
|
- font-size: 36rpx;
|
|
|
- font-weight: 600;
|
|
|
- }
|
|
|
-
|
|
|
- .icon-back, .icon-album {
|
|
|
- color: #fff;
|
|
|
- font-size: 40rpx;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.scan-area {
|
|
|
- flex: 1;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- padding: 100rpx 40rpx;
|
|
|
-
|
|
|
- .scan-frame {
|
|
|
- width: 500rpx;
|
|
|
- height: 500rpx;
|
|
|
- position: relative;
|
|
|
- margin-bottom: 60rpx;
|
|
|
-
|
|
|
- .scan-line {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- width: 100%;
|
|
|
- height: 4rpx;
|
|
|
- background: linear-gradient(90deg, transparent, #00ff00, transparent);
|
|
|
- animation: scanMove 2s linear infinite;
|
|
|
- }
|
|
|
-
|
|
|
- .scan-corners {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
-
|
|
|
- .corner {
|
|
|
- position: absolute;
|
|
|
- width: 60rpx;
|
|
|
- height: 60rpx;
|
|
|
- border: 6rpx solid #00ff00;
|
|
|
-
|
|
|
- &.top-left {
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- border-right: none;
|
|
|
- border-bottom: none;
|
|
|
- }
|
|
|
-
|
|
|
- &.top-right {
|
|
|
- top: 0;
|
|
|
- right: 0;
|
|
|
- border-left: none;
|
|
|
- border-bottom: none;
|
|
|
- }
|
|
|
-
|
|
|
- &.bottom-left {
|
|
|
- bottom: 0;
|
|
|
- left: 0;
|
|
|
- border-right: none;
|
|
|
- border-top: none;
|
|
|
- }
|
|
|
-
|
|
|
- &.bottom-right {
|
|
|
- bottom: 0;
|
|
|
- right: 0;
|
|
|
- border-left: none;
|
|
|
- border-top: none;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .scan-tip {
|
|
|
- color: rgba(255, 255, 255, 0.8);
|
|
|
- font-size: 28rpx;
|
|
|
- text-align: center;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.bottom-actions {
|
|
|
- position: fixed;
|
|
|
- bottom: 100rpx;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- display: flex;
|
|
|
- justify-content: space-around;
|
|
|
- padding: 0 60rpx;
|
|
|
-
|
|
|
- .action-item {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
-
|
|
|
- .action-icon {
|
|
|
- width: 100rpx;
|
|
|
- height: 100rpx;
|
|
|
- border-radius: 50%;
|
|
|
- background: rgba(255, 255, 255, 0.2);
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- transition: all 0.3s ease;
|
|
|
-
|
|
|
- &.active {
|
|
|
- background: rgba(255, 255, 255, 0.4);
|
|
|
- }
|
|
|
-
|
|
|
- .iconfont {
|
|
|
- color: #fff;
|
|
|
- font-size: 50rpx;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .action-text {
|
|
|
- color: rgba(255, 255, 255, 0.8);
|
|
|
- font-size: 24rpx;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.scan-result-modal {
|
|
|
- position: fixed;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
- background: rgba(0, 0, 0, 0.5);
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- z-index: 999;
|
|
|
-
|
|
|
- .modal-content {
|
|
|
- background: #fff;
|
|
|
- border-radius: 20rpx;
|
|
|
- width: 600rpx;
|
|
|
- overflow: hidden;
|
|
|
-
|
|
|
- .modal-header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- padding: 40rpx;
|
|
|
- border-bottom: 1rpx solid #f0f0f0;
|
|
|
-
|
|
|
- .modal-title {
|
|
|
- font-size: 36rpx;
|
|
|
- font-weight: 600;
|
|
|
- color: #333;
|
|
|
- }
|
|
|
-
|
|
|
- .modal-close {
|
|
|
- font-size: 50rpx;
|
|
|
- color: #999;
|
|
|
- cursor: pointer;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .modal-body {
|
|
|
- padding: 40rpx;
|
|
|
-
|
|
|
- .result-info {
|
|
|
- .result-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 20rpx;
|
|
|
-
|
|
|
- &:last-child {
|
|
|
- margin-bottom: 0;
|
|
|
- }
|
|
|
-
|
|
|
- .label {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #666;
|
|
|
- width: 160rpx;
|
|
|
- }
|
|
|
-
|
|
|
- .value {
|
|
|
- font-size: 28rpx;
|
|
|
- color: #333;
|
|
|
- font-weight: 600;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .modal-footer {
|
|
|
- display: flex;
|
|
|
- padding: 0 40rpx 40rpx;
|
|
|
-
|
|
|
- .modal-btn {
|
|
|
- flex: 1;
|
|
|
- height: 80rpx;
|
|
|
- border-radius: 40rpx;
|
|
|
- border: none;
|
|
|
- font-size: 32rpx;
|
|
|
- margin: 0 10rpx;
|
|
|
-
|
|
|
- &.cancel {
|
|
|
- background: #f0f0f0;
|
|
|
- color: #666;
|
|
|
- }
|
|
|
-
|
|
|
- &.confirm {
|
|
|
- background: #667eea;
|
|
|
- color: #fff;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-@keyframes scanMove {
|
|
|
- 0% {
|
|
|
- transform: translateY(0);
|
|
|
- }
|
|
|
- 100% {
|
|
|
- transform: translateY(500rpx);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-</style>
|