1164 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			1164 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			Vue
		
	
	
	
<template>
 | 
						||
  <view class="page-container">
 | 
						||
    <!-- 设备搜索页面 -->
 | 
						||
    <view v-if="!showCamera">
 | 
						||
      <view class="table-list-item">
 | 
						||
        <view class="scan-btn" @click="openCamera">
 | 
						||
          <text style="color: #FFF;">开始识别</text>
 | 
						||
        </view>
 | 
						||
      </view>
 | 
						||
      <view class="table-list-item">
 | 
						||
        <uni-row :gutter="24" style="display: flex; align-items: center">
 | 
						||
          <uni-col :span="6">
 | 
						||
            <text>设备编码</text>
 | 
						||
          </uni-col>
 | 
						||
          <uni-col :span="10">
 | 
						||
            <view>
 | 
						||
              <uni-easyinput v-model="queryCodeParams.maCode" placeholder="请输入内容" />
 | 
						||
            </view>
 | 
						||
          </uni-col>
 | 
						||
          <uni-col :span="6">
 | 
						||
            <view class="coding-btn" @click="getCode()">
 | 
						||
              <text style="color: #fe9a09;text-align: center;">编码检索</text>
 | 
						||
            </view>
 | 
						||
          </uni-col>
 | 
						||
        </uni-row>
 | 
						||
        <div v-if="optionList.length > 1" class="select-container">
 | 
						||
          <uni-data-select
 | 
						||
              v-model="queryCodeParams.maId"
 | 
						||
              :localdata="optionList"
 | 
						||
              @change="changeTag"
 | 
						||
              placeholder="请选择相关联编号"
 | 
						||
              class="data-select"
 | 
						||
              :searchable="false"
 | 
						||
          ></uni-data-select>
 | 
						||
        </div>
 | 
						||
      </view>
 | 
						||
      <view class="table-list-item">
 | 
						||
        <uni-forms :model="codeData" label-width="100" :border="true">
 | 
						||
          <uni-forms-item label="物资名称:" name="materialType">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialType }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="物资类型:" name="materialName">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialName }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="规格型号:" name="materialModel">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialModel }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="设备编码:" name="maCode">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.maCode }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="检验结论:" name="statusName">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.statusName }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="本次检修时间:" name="inspectionTime">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.inspectionTime }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="下次检修时间:" name="nextInspectionTime">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.nextInspectionTime }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="检修员:" name="inspectionPerson">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.inspectionPerson }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="检验员:" name="inspectionVerifier">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.inspectionVerifier }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="联系电话:" name="contactNumber">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.contactNumber }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="领料工程:" name="materialProject">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialProject }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
          <uni-forms-item label="领料单位:" name="materialUnit">
 | 
						||
            <text style="height: 100%;display: flex;align-items: center;">{{ codeData.materialUnit }}</text>
 | 
						||
          </uni-forms-item>
 | 
						||
        </uni-forms>
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- 相机预览页面 -->
 | 
						||
    <view v-if="showCamera" class="camera-container">
 | 
						||
 | 
						||
      <!-- 底部控制区 -->
 | 
						||
      <view class="bottom-controls">
 | 
						||
        <view class="control-btn" @click="closeCamera">
 | 
						||
          <text class="control-icon">✕</text>
 | 
						||
        </view>
 | 
						||
        <view class="photo-btn" @click="takePicture" :class="{ 'taking': isTaking, 'disabled': isFocusing }">
 | 
						||
          <view class="photo-btn-inner"></view>
 | 
						||
        </view>
 | 
						||
        <view class="control-btn" @click="openGallery">
 | 
						||
          <text class="control-icon">📷</text>
 | 
						||
        </view>
 | 
						||
      </view>
 | 
						||
 | 
						||
      <!-- 加载提示 -->
 | 
						||
      <view v-if="isProcessing" class="loading-overlay">
 | 
						||
        <view class="loading-content">
 | 
						||
          <view class="loading-spinner"></view>
 | 
						||
          <text class="loading-text">正在识别中...</text>
 | 
						||
        </view>
 | 
						||
      </view>
 | 
						||
    </view>
 | 
						||
 | 
						||
    <!-- 隐藏的canvas用于图片处理 -->
 | 
						||
    <canvas
 | 
						||
        canvas-id="imageCanvas"
 | 
						||
        style="position: fixed; top: -9999px; left: -9999px; width: 1px; height: 1px;"
 | 
						||
    ></canvas>
 | 
						||
  </view>
 | 
						||
</template>
 | 
						||
 | 
						||
<script>
 | 
						||
import ImageUtils from "../../services/utils/imageUtils";
 | 
						||
 | 
						||
export default {
 | 
						||
  data() {
 | 
						||
    return {
 | 
						||
      showCamera: false,
 | 
						||
      isTaking: false,
 | 
						||
      isProcessing: false,
 | 
						||
      cameraStarted: false,
 | 
						||
      queryParams: {},
 | 
						||
      queryCodeParams: {
 | 
						||
        maCode: "",
 | 
						||
        maId: ""
 | 
						||
      },
 | 
						||
      codeData: {},
 | 
						||
      optionList: [],
 | 
						||
      cameraReady: false,
 | 
						||
      deviceReadyTimeout: null,
 | 
						||
      pluginCheckRetries: 0,
 | 
						||
      maxRetries: 10,
 | 
						||
      // 聚焦相关状态
 | 
						||
      isFocusing: false,
 | 
						||
      focusSuccess: false,
 | 
						||
      showFocusIndicator: false,
 | 
						||
      focusIndicatorStyle: {},
 | 
						||
      autoFocusEnabled: true,
 | 
						||
      focusTimeout: null,
 | 
						||
      // 设备信息
 | 
						||
      systemInfo: null
 | 
						||
    }
 | 
						||
  },
 | 
						||
  onShow() {
 | 
						||
    this.initializeCordova();
 | 
						||
    // 获取设备信息用于相机配置
 | 
						||
    this.getDeviceInfo();
 | 
						||
  },
 | 
						||
  onHide() {
 | 
						||
    this.cleanup();
 | 
						||
  },
 | 
						||
  beforeDestroy() {
 | 
						||
    this.cleanup();
 | 
						||
  },
 | 
						||
  methods: {
 | 
						||
    // 获取设备信息
 | 
						||
    getDeviceInfo() {
 | 
						||
      try {
 | 
						||
        const systemInfo = uni.getSystemInfoSync();
 | 
						||
        console.log('设备信息:', systemInfo);
 | 
						||
        this.systemInfo = systemInfo;
 | 
						||
      } catch (error) {
 | 
						||
        console.error('获取设备信息失败:', error);
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 设置全屏模式(仅Android)
 | 
						||
    setFullscreenMode(enable) {
 | 
						||
      try {
 | 
						||
        // #ifdef APP-PLUS
 | 
						||
        if (this.systemInfo && this.systemInfo.platform === 'android') {
 | 
						||
          if (enable) {
 | 
						||
            // 隐藏状态栏和导航栏
 | 
						||
            plus.navigator.setFullscreen(true);
 | 
						||
          } else {
 | 
						||
            // 恢复状态栏和导航栏
 | 
						||
            plus.navigator.setFullscreen(false);
 | 
						||
          }
 | 
						||
        }
 | 
						||
        // #endif
 | 
						||
      } catch (error) {
 | 
						||
        console.error('设置全屏模式失败:', error);
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 初始化Cordova - 改进版本
 | 
						||
    initializeCordova() {
 | 
						||
      console.log('开始初始化Cordova...');
 | 
						||
      this.pluginCheckRetries = 0;
 | 
						||
      // 清除之前的超时
 | 
						||
      if (this.deviceReadyTimeout) {
 | 
						||
        clearTimeout(this.deviceReadyTimeout);
 | 
						||
      }
 | 
						||
      // 如果Cordova已经准备好,直接检查插件
 | 
						||
      if (this.isCordovaReady()) {
 | 
						||
        this.onDeviceReady();
 | 
						||
        return;
 | 
						||
      }
 | 
						||
      // 监听deviceready事件
 | 
						||
      document.addEventListener('deviceready', this.onDeviceReady, false);
 | 
						||
      // 设置超时,防止无限等待
 | 
						||
      this.deviceReadyTimeout = setTimeout(() => {
 | 
						||
        console.warn('Cordova初始化超时,尝试直接检查插件');
 | 
						||
        this.checkCameraPlugin();
 | 
						||
      }, 5000);
 | 
						||
    },
 | 
						||
 | 
						||
    // 检查Cordova是否准备好
 | 
						||
    isCordovaReady() {
 | 
						||
      return !!(window.cordova && (
 | 
						||
          document.readyState === 'complete' ||
 | 
						||
          window.cordova.platformId
 | 
						||
      ));
 | 
						||
    },
 | 
						||
 | 
						||
    // 设备准备就绪
 | 
						||
    onDeviceReady() {
 | 
						||
      console.log('Cordova设备准备就绪');
 | 
						||
      // 清除超时
 | 
						||
      if (this.deviceReadyTimeout) {
 | 
						||
        clearTimeout(this.deviceReadyTimeout);
 | 
						||
        this.deviceReadyTimeout = null;
 | 
						||
      }
 | 
						||
      // 移除事件监听器,避免重复调用
 | 
						||
      document.removeEventListener('deviceready', this.onDeviceReady);
 | 
						||
      // 检查相机插件
 | 
						||
      this.checkCameraPlugin();
 | 
						||
    },
 | 
						||
 | 
						||
    // 检查相机插件 - 改进版本
 | 
						||
    checkCameraPlugin() {
 | 
						||
      console.log(`检查相机插件... (尝试 ${this.pluginCheckRetries + 1}/${this.maxRetries})`);
 | 
						||
      // 按优先级检查插件可用性
 | 
						||
      const pluginAvailable = this.getCameraPlugin();
 | 
						||
      if (pluginAvailable) {
 | 
						||
        console.log('相机插件可用:', pluginAvailable);
 | 
						||
        this.cameraReady = true;
 | 
						||
        return;
 | 
						||
      }
 | 
						||
      this.pluginCheckRetries++;
 | 
						||
      if (this.pluginCheckRetries < this.maxRetries) {
 | 
						||
        // 递增延迟重试
 | 
						||
        const delay = Math.min(1000 * this.pluginCheckRetries, 5000);
 | 
						||
        console.log(`插件未就绪,${delay}ms后重试...`);
 | 
						||
        setTimeout(() => {
 | 
						||
          this.checkCameraPlugin();
 | 
						||
        }, delay);
 | 
						||
      } else {
 | 
						||
        console.error('相机插件检查失败,已达到最大重试次数');
 | 
						||
        this.cameraReady = false;
 | 
						||
        uni.showToast({
 | 
						||
          title: '相机插件初始化失败',
 | 
						||
          icon: 'none',
 | 
						||
          duration: 3000
 | 
						||
        });
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 获取相机插件引用 - 改进版本
 | 
						||
    getCameraPlugin() {
 | 
						||
      // 按照插件注册的实际路径检查
 | 
						||
      const possiblePaths = [
 | 
						||
        () => window.CameraPreview,  // 全局注册的路径
 | 
						||
        () => window.cordova?.plugins?.CameraPreview,
 | 
						||
        () => window.plugins?.CameraPreview,
 | 
						||
        () => cordova?.plugins?.CameraPreview
 | 
						||
      ];
 | 
						||
      for (let getPlugin of possiblePaths) {
 | 
						||
        try {
 | 
						||
          const plugin = getPlugin();
 | 
						||
          if (plugin && typeof plugin.startCamera === 'function') {
 | 
						||
            console.log('找到相机插件:', plugin);
 | 
						||
            return plugin;
 | 
						||
          }
 | 
						||
        } catch (e) {
 | 
						||
          // 忽略访问错误,继续尝试下一个路径
 | 
						||
        }
 | 
						||
      }
 | 
						||
      return null;
 | 
						||
    },
 | 
						||
 | 
						||
    // 打开相机 - 改进版本
 | 
						||
    async openCamera() {
 | 
						||
      console.log('尝试打开相机...');
 | 
						||
      if (!this.cameraReady) {
 | 
						||
        // 再次尝试检查插件
 | 
						||
        this.checkCameraPlugin();
 | 
						||
        if (!this.cameraReady) {
 | 
						||
          uni.showModal({
 | 
						||
            title: '提示',
 | 
						||
            content: '相机插件未准备好,请确保应用已正确安装相机插件,或尝试重启应用',
 | 
						||
            showCancel: false
 | 
						||
          });
 | 
						||
          return;
 | 
						||
        }
 | 
						||
      }
 | 
						||
      try {
 | 
						||
        // 显示相机界面
 | 
						||
        this.showCamera = true;
 | 
						||
        // 等待UI更新
 | 
						||
        await this.$nextTick();
 | 
						||
        // 初始化相机
 | 
						||
        await this.initCamera();
 | 
						||
      } catch (error) {
 | 
						||
        console.error('打开相机失败:', error);
 | 
						||
        this.showCamera = false;
 | 
						||
        uni.showToast({
 | 
						||
          title: error.message || '打开相机失败',
 | 
						||
          icon: 'none',
 | 
						||
          duration: 2000
 | 
						||
        });
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 初始化相机 - 改进版本,添加聚焦支持
 | 
						||
    async initCamera() {
 | 
						||
      return new Promise((resolve, reject) => {
 | 
						||
        console.log('初始化相机预览...');
 | 
						||
        const CameraPreview = this.getCameraPlugin();
 | 
						||
        if (!CameraPreview) {
 | 
						||
          reject(new Error('相机插件不可用'));
 | 
						||
          return;
 | 
						||
        }
 | 
						||
 | 
						||
        // 获取屏幕尺寸和状态栏高度
 | 
						||
        const systemInfo = this.systemInfo || uni.getSystemInfoSync();
 | 
						||
        const screenWidth = systemInfo.screenWidth;
 | 
						||
        const screenHeight = systemInfo.screenHeight;
 | 
						||
        const statusBarHeight = systemInfo.statusBarHeight || 0;
 | 
						||
 | 
						||
        // 相机预览配置 - 始终使用全屏背景模式
 | 
						||
        let cameraY = 0;
 | 
						||
        let cameraHeight = screenHeight;
 | 
						||
        let toBack = true; // 设置为背景层,让UI元素显示在上方
 | 
						||
 | 
						||
        console.log('屏幕信息:', {
 | 
						||
          platform: systemInfo.platform,
 | 
						||
          screenWidth,
 | 
						||
          screenHeight,
 | 
						||
          statusBarHeight,
 | 
						||
          cameraY,
 | 
						||
          cameraHeight,
 | 
						||
          toBack
 | 
						||
        });
 | 
						||
 | 
						||
        const options = {
 | 
						||
          x: 0,
 | 
						||
          y: cameraY,
 | 
						||
          width: screenWidth,
 | 
						||
          height: cameraHeight,
 | 
						||
          camera: CameraPreview.CAMERA_DIRECTION?.BACK || 'back',
 | 
						||
          tapPhoto: false,
 | 
						||
          previewDrag: false,
 | 
						||
          toBack: toBack,  // 根据平台动态设置
 | 
						||
          alpha: 1,
 | 
						||
          tapFocus: true,  // 启用点击聚焦
 | 
						||
          disableExifHeaderStripping: false
 | 
						||
        };
 | 
						||
 | 
						||
        console.log('相机配置:', options);
 | 
						||
        CameraPreview.startCamera(
 | 
						||
            options,
 | 
						||
            (result) => {
 | 
						||
              console.log('相机启动成功:', result);
 | 
						||
              this.cameraStarted = true;
 | 
						||
              // 启动后进行一次自动聚焦
 | 
						||
              this.performAutoFocus();
 | 
						||
              resolve();
 | 
						||
            },
 | 
						||
            (error) => {
 | 
						||
              console.error('相机启动失败:', error);
 | 
						||
              this.cameraStarted = false;
 | 
						||
              reject(new Error(`相机启动失败: ${error}`));
 | 
						||
            }
 | 
						||
        );
 | 
						||
      });
 | 
						||
    },
 | 
						||
 | 
						||
    // 执行自动聚焦
 | 
						||
    async performAutoFocus() {
 | 
						||
      if (!this.cameraStarted || !this.autoFocusEnabled) {
 | 
						||
        return;
 | 
						||
      }
 | 
						||
 | 
						||
      const CameraPreview = this.getCameraPlugin();
 | 
						||
      if (!CameraPreview || typeof CameraPreview.setFocus !== 'function') {
 | 
						||
        console.log('相机插件不支持聚焦功能');
 | 
						||
        return;
 | 
						||
      }
 | 
						||
 | 
						||
      try {
 | 
						||
        this.isFocusing = true;
 | 
						||
        this.focusSuccess = false;
 | 
						||
 | 
						||
        // 清除之前的聚焦超时
 | 
						||
        if (this.focusTimeout) {
 | 
						||
          clearTimeout(this.focusTimeout);
 | 
						||
        }
 | 
						||
 | 
						||
        console.log('开始自动聚焦...');
 | 
						||
 | 
						||
        await new Promise((resolve, reject)=> {
 | 
						||
          // 设置聚焦超时
 | 
						||
          this.focusTimeout = setTimeout(() => {
 | 
						||
            console.log('聚焦超时');
 | 
						||
            this.isFocusing = false;
 | 
						||
            resolve();
 | 
						||
          }, 3000);
 | 
						||
 | 
						||
          CameraPreview.setFocus(
 | 
						||
              (result) => {
 | 
						||
                console.log('自动聚焦成功:', result);
 | 
						||
                if (this.focusTimeout) {
 | 
						||
                  clearTimeout(this.focusTimeout);
 | 
						||
                  this.focusTimeout = null;
 | 
						||
                }
 | 
						||
                this.isFocusing = false;
 | 
						||
                this.focusSuccess = true;
 | 
						||
                // 显示聚焦成功状态一段时间
 | 
						||
                setTimeout(() => {
 | 
						||
                  this.focusSuccess = false;
 | 
						||
                }, 1000);
 | 
						||
                resolve();
 | 
						||
              },
 | 
						||
              (error) => {
 | 
						||
                console.error('自动聚焦失败:', error);
 | 
						||
                if (this.focusTimeout) {
 | 
						||
                  clearTimeout(this.focusTimeout);
 | 
						||
                  this.focusTimeout = null;
 | 
						||
                }
 | 
						||
                this.isFocusing = false;
 | 
						||
                resolve(); // 即使聚焦失败也继续
 | 
						||
              }
 | 
						||
          );
 | 
						||
        });
 | 
						||
      } catch (error) {
 | 
						||
        console.error('聚焦过程出错:', error);
 | 
						||
        this.isFocusing = false;
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 关闭相机
 | 
						||
    async closeCamera() {
 | 
						||
      console.log('关闭相机...');
 | 
						||
      try {
 | 
						||
        await this.stopCamera();
 | 
						||
      } catch (error) {
 | 
						||
        console.error('关闭相机出错:', error);
 | 
						||
      } finally {
 | 
						||
        this.showCamera = false;
 | 
						||
        this.cameraStarted = false;
 | 
						||
        this.resetFocusState();
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 重置聚焦状态
 | 
						||
    resetFocusState() {
 | 
						||
      this.isFocusing = false;
 | 
						||
      this.focusSuccess = false;
 | 
						||
      this.showFocusIndicator = false;
 | 
						||
      if (this.focusTimeout) {
 | 
						||
        clearTimeout(this.focusTimeout);
 | 
						||
        this.focusTimeout = null;
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 停止相机
 | 
						||
    async stopCamera() {
 | 
						||
      if (!this.cameraStarted) {
 | 
						||
        return Promise.resolve();
 | 
						||
      }
 | 
						||
      const CameraPreview = this.getCameraPlugin();
 | 
						||
      if (!CameraPreview) {
 | 
						||
        return Promise.resolve();
 | 
						||
      }
 | 
						||
      return new Promise((resolve) => {
 | 
						||
        CameraPreview.stopCamera(
 | 
						||
            () => {
 | 
						||
              console.log('相机已停止');
 | 
						||
              this.cameraStarted = false;
 | 
						||
              resolve();
 | 
						||
            },
 | 
						||
            (error) => {
 | 
						||
              console.error('停止相机失败:', error);
 | 
						||
              this.cameraStarted = false;
 | 
						||
              resolve(); // 即使失败也继续
 | 
						||
            }
 | 
						||
        );
 | 
						||
      });
 | 
						||
    },
 | 
						||
 | 
						||
    // 拍照 - 添加聚焦逻辑
 | 
						||
    async takePicture() {
 | 
						||
      if (!this.cameraStarted || this.isTaking || this.isFocusing) {
 | 
						||
        if (this.isFocusing) {
 | 
						||
          uni.showToast({
 | 
						||
            title: '正在聚焦,请稍候...',
 | 
						||
            icon: 'none',
 | 
						||
            duration: 1000
 | 
						||
          });
 | 
						||
        }
 | 
						||
        return;
 | 
						||
      }
 | 
						||
 | 
						||
      console.log('开始拍照流程...');
 | 
						||
      this.isTaking = true;
 | 
						||
 | 
						||
      try {
 | 
						||
        const CameraPreview = this.getCameraPlugin();
 | 
						||
        if (!CameraPreview) {
 | 
						||
          throw new Error('相机插件不可用');
 | 
						||
        }
 | 
						||
 | 
						||
        // 拍照前先进行聚焦
 | 
						||
        console.log('拍照前聚焦...');
 | 
						||
        await this.performFocusBeforeCapture();
 | 
						||
 | 
						||
        console.log('开始拍照...');
 | 
						||
        const imageData = await new Promise((resolve, reject)=>
 | 
						||
                {
 | 
						||
                  const options = {
 | 
						||
                    quality: 85,
 | 
						||
                    targetHeight: 1920,
 | 
						||
                    targetWidth: 1080
 | 
						||
                  };
 | 
						||
 | 
						||
                  CameraPreview.takePicture(
 | 
						||
                      options,
 | 
						||
                      (imageData) => {
 | 
						||
                        console.log('拍照成功');
 | 
						||
                        resolve(imageData);
 | 
						||
                      },
 | 
						||
                      (error) => {
 | 
						||
                        console.error('拍照失败:', error);
 | 
						||
                        reject(new Error(`拍照失败: ${error}`));
 | 
						||
                      }
 | 
						||
                  );
 | 
						||
                }
 | 
						||
            )
 | 
						||
        ;
 | 
						||
 | 
						||
        await this.processImage(imageData);
 | 
						||
      } catch (error) {
 | 
						||
        console.error('拍照过程出错:', error);
 | 
						||
        uni.showToast({
 | 
						||
          title: error.message || '拍照失败',
 | 
						||
          icon: 'none'
 | 
						||
        });
 | 
						||
      } finally {
 | 
						||
        this.isTaking = false;
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 拍照前聚焦
 | 
						||
    async performFocusBeforeCapture() {
 | 
						||
      const CameraPreview = this.getCameraPlugin();
 | 
						||
      if (!CameraPreview || typeof CameraPreview.setFocus !== 'function') {
 | 
						||
        console.log('相机插件不支持聚焦,直接拍照');
 | 
						||
        return;
 | 
						||
      }
 | 
						||
 | 
						||
      try {
 | 
						||
        this.isFocusing = true;
 | 
						||
 | 
						||
        console.log('拍照前执行聚焦...');
 | 
						||
 | 
						||
        await new Promise((resolve, reject)=>
 | 
						||
            {
 | 
						||
              // 设置聚焦超时
 | 
						||
              const focusTimeout = setTimeout(() => {
 | 
						||
                console.log('拍照前聚焦超时,继续拍照');
 | 
						||
                this.isFocusing = false;
 | 
						||
                resolve();
 | 
						||
              }, 2000); // 拍照前聚焦时间稍短
 | 
						||
 | 
						||
              CameraPreview.setFocus(
 | 
						||
                  (result) => {
 | 
						||
                    console.log('拍照前聚焦成功:', result);
 | 
						||
                    clearTimeout(focusTimeout);
 | 
						||
                    this.isFocusing = false;
 | 
						||
                    // 聚焦成功后稍等一下再拍照,确保聚焦稳定
 | 
						||
                    setTimeout(resolve, 200);
 | 
						||
                  },
 | 
						||
                  (error) => {
 | 
						||
                    console.error('拍照前聚焦失败:', error);
 | 
						||
                    clearTimeout(focusTimeout);
 | 
						||
                    this.isFocusing = false;
 | 
						||
                    resolve(); // 即使聚焦失败也继续拍照
 | 
						||
                  }
 | 
						||
              );
 | 
						||
            }
 | 
						||
        )
 | 
						||
        ;
 | 
						||
      } catch (error) {
 | 
						||
        console.error('拍照前聚焦过程出错:', error);
 | 
						||
        this.isFocusing = false;
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 处理图片 - 使用ImageUtils
 | 
						||
    async processImage(imageData) {
 | 
						||
      this.isProcessing = true;
 | 
						||
      try {
 | 
						||
        console.log('开始处理图片...');
 | 
						||
        // 使用ImageUtils处理图片
 | 
						||
        const processedBase64 = await ImageUtils.processImage(imageData, {
 | 
						||
          maxWidth: 1024,
 | 
						||
          maxHeight: 1024,
 | 
						||
          quality: 50,
 | 
						||
          outputFormat: 'base64'
 | 
						||
        });
 | 
						||
 | 
						||
        console.log('图片处理完成,开始OCR识别...');
 | 
						||
        const response = await new Promise((resolve, reject)=>
 | 
						||
                {
 | 
						||
                  uni.request({
 | 
						||
                    url: '/material/app/ocr/getOcrCode',
 | 
						||
                    method: 'POST',
 | 
						||
                    data: {
 | 
						||
                      image: processedBase64,
 | 
						||
                      jiju_type: '',
 | 
						||
                      auth_lic: 'xIWDlaDVdijcBB4mjhGCPYk5Kvk8tHZJbUn+vW+ih15+MYx98e/PXyBmKL5gFcWMPznLgDA15QuSAnZQSLddwdy9HkZgtuQDEEZZ351Eyb1eiDUccUnyoSGIrNimbx5TooBNNPYqU4qJeFrPJXAqjBHzRrxoBxuR2CEGKQPgHC4='
 | 
						||
                    },
 | 
						||
                    timeout: 30000,
 | 
						||
                    success: resolve,
 | 
						||
                    fail: reject
 | 
						||
                  });
 | 
						||
                }
 | 
						||
            )
 | 
						||
        ;
 | 
						||
 | 
						||
        if (response.data?.data?.result) {
 | 
						||
          this.queryCodeParams.maCode = response.data.data.result;
 | 
						||
          await this.closeCamera();
 | 
						||
          this.getCode();
 | 
						||
          uni.showToast({
 | 
						||
            title: '识别成功',
 | 
						||
            icon: 'success'
 | 
						||
          });
 | 
						||
        } else {
 | 
						||
          uni.showToast({
 | 
						||
            title: '未识别到有效编码',
 | 
						||
            icon: 'none'
 | 
						||
          });
 | 
						||
        }
 | 
						||
      } catch (error) {
 | 
						||
        console.error('图片处理或OCR识别失败:', error);
 | 
						||
        uni.showToast({
 | 
						||
          title: '识别失败,请重试',
 | 
						||
          icon: 'none'
 | 
						||
        });
 | 
						||
      } finally {
 | 
						||
        this.isProcessing = false;
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 打开相册
 | 
						||
    openGallery() {
 | 
						||
      uni.chooseImage({
 | 
						||
        count: 1,
 | 
						||
        sourceType: ['album'],
 | 
						||
        success: (res) => {
 | 
						||
          if (res.tempFilePaths && res.tempFilePaths.length > 0) {
 | 
						||
            this.convertImageToBase64(res.tempFilePaths[0]);
 | 
						||
          }
 | 
						||
        },
 | 
						||
        fail: (error) => {
 | 
						||
          console.error('选择图片失败:', error);
 | 
						||
          uni.showToast({
 | 
						||
            title: '选择图片失败',
 | 
						||
            icon: 'none'
 | 
						||
          });
 | 
						||
        }
 | 
						||
      });
 | 
						||
    },
 | 
						||
 | 
						||
    // 将图片转换为base64 - 使用ImageUtils处理
 | 
						||
    async convertImageToBase64(filePath) {
 | 
						||
      this.isProcessing = true;
 | 
						||
      try {
 | 
						||
        console.log('开始处理相册图片...');
 | 
						||
        // 使用ImageUtils处理相册图片
 | 
						||
        const processedBase64 = await ImageUtils.processImage(filePath, {
 | 
						||
          maxWidth: 1024,
 | 
						||
          maxHeight: 1024,
 | 
						||
          quality: 50,
 | 
						||
          outputFormat: 'base64'
 | 
						||
        });
 | 
						||
 | 
						||
        console.log('相册图片处理完成,开始OCR识别...');
 | 
						||
        const response = await new Promise((resolve, reject)=>
 | 
						||
                {
 | 
						||
                  uni.request({
 | 
						||
                    url: '/material/app/ocr/getOcrCode',
 | 
						||
                    method: 'POST',
 | 
						||
                    data: {
 | 
						||
                      image: processedBase64,
 | 
						||
                      jiju_type: '',
 | 
						||
                      auth_lic: 'xIWDlaDVdijcBB4mjhGCPYk5Kvk8tHZJbUn+vW+ih15+MYx98e/PXyBmKL5gFcWMPznLgDA15QuSAnZQSLddwdy9HkZgtuQDEEZZ351Eyb1eiDUccUnyoSGIrNimbx5TooBNNPYqU4qJeFrPJXAqjBHzRrxoBxuR2CEGKQPgHC4='
 | 
						||
                    },
 | 
						||
                    timeout: 30000,
 | 
						||
                    success: resolve,
 | 
						||
                    fail: reject
 | 
						||
                  });
 | 
						||
                }
 | 
						||
            )
 | 
						||
        ;
 | 
						||
 | 
						||
        if (response.data?.data?.result) {
 | 
						||
          this.queryCodeParams.maCode = response.data.data.result;
 | 
						||
          await this.closeCamera();
 | 
						||
          this.getCode();
 | 
						||
          uni.showToast({
 | 
						||
            title: '识别成功',
 | 
						||
            icon: 'success'
 | 
						||
          });
 | 
						||
        } else {
 | 
						||
          uni.showToast({
 | 
						||
            title: '未识别到有效编码',
 | 
						||
            icon: 'none'
 | 
						||
          });
 | 
						||
        }
 | 
						||
      } catch (error) {
 | 
						||
        console.error('相册图片处理或OCR识别失败:', error);
 | 
						||
        uni.showToast({
 | 
						||
          title: '识别失败,请重试',
 | 
						||
          icon: 'none'
 | 
						||
        });
 | 
						||
      } finally {
 | 
						||
        this.isProcessing = false;
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 编码检索
 | 
						||
    async getCode() {
 | 
						||
      if (!this.queryCodeParams.maCode.trim()) {
 | 
						||
        uni.showToast({
 | 
						||
          title: '请输入设备编码',
 | 
						||
          icon: 'none'
 | 
						||
        });
 | 
						||
        return;
 | 
						||
      }
 | 
						||
      try {
 | 
						||
        const response = await new Promise((resolve, reject)=>
 | 
						||
                {
 | 
						||
                  uni.request({
 | 
						||
                    url: '/material/ma_machine/getHisByCode',
 | 
						||
                    method: 'GET',
 | 
						||
                    data: {maCode: this.queryCodeParams.maCode},
 | 
						||
                    success: resolve,
 | 
						||
                    fail: reject
 | 
						||
                  });
 | 
						||
                }
 | 
						||
            )
 | 
						||
        ;
 | 
						||
 | 
						||
        if (response.data?.data && response.data.data.length > 0) {
 | 
						||
          this.optionList = response.data.data.map(option => ({
 | 
						||
            value: option.maId,
 | 
						||
            text: option.maCode
 | 
						||
          }));
 | 
						||
          if (response.data.data.length === 1) {
 | 
						||
            this.codeData = response.data.data[0];
 | 
						||
          }
 | 
						||
        } else {
 | 
						||
          uni.showToast({
 | 
						||
            title: '未查询到该编号信息',
 | 
						||
            icon: 'none'
 | 
						||
          });
 | 
						||
        }
 | 
						||
      } catch (error) {
 | 
						||
        console.error('查询失败:', error);
 | 
						||
        uni.showToast({
 | 
						||
          title: '查询失败,请重试',
 | 
						||
          icon: 'none'
 | 
						||
        });
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 标签改变
 | 
						||
    async changeTag() {
 | 
						||
      if (!this.queryCodeParams.maId) return;
 | 
						||
      try {
 | 
						||
        const response = await new Promise((resolve, reject)=>
 | 
						||
                {
 | 
						||
                  uni.request({
 | 
						||
                    url: '/material/ma_machine/getHisByCode',
 | 
						||
                    method: 'GET',
 | 
						||
                    data: {maId: this.queryCodeParams.maId},
 | 
						||
                    success: resolve,
 | 
						||
                    fail: reject
 | 
						||
                  });
 | 
						||
                }
 | 
						||
            )
 | 
						||
        ;
 | 
						||
 | 
						||
        if (response.data?.data && response.data.data.length > 0) {
 | 
						||
          this.codeData = response.data.data[0];
 | 
						||
        } else {
 | 
						||
          uni.showToast({
 | 
						||
            title: '获取编号信息失败',
 | 
						||
            icon: 'none'
 | 
						||
          });
 | 
						||
        }
 | 
						||
      } catch (error) {
 | 
						||
        console.error("获取编号信息失败", error);
 | 
						||
        uni.showToast({
 | 
						||
          title: '获取信息失败',
 | 
						||
          icon: 'none'
 | 
						||
        });
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 清理资源
 | 
						||
    cleanup() {
 | 
						||
      console.log('清理资源...');
 | 
						||
      // 清除超时
 | 
						||
      if (this.deviceReadyTimeout) {
 | 
						||
        clearTimeout(this.deviceReadyTimeout);
 | 
						||
        this.deviceReadyTimeout = null;
 | 
						||
      }
 | 
						||
      if (this.focusTimeout) {
 | 
						||
        clearTimeout(this.focusTimeout);
 | 
						||
        this.focusTimeout = null;
 | 
						||
      }
 | 
						||
      // 移除事件监听
 | 
						||
      document.removeEventListener('deviceready', this.onDeviceReady);
 | 
						||
      // 停止相机
 | 
						||
      if (this.cameraStarted) {
 | 
						||
        this.stopCamera().catch(console.error);
 | 
						||
      }
 | 
						||
      // 重置聚焦状态
 | 
						||
      this.resetFocusState();
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
</script>
 | 
						||
 | 
						||
<style>
 | 
						||
.page-container {
 | 
						||
  display: flex;
 | 
						||
  height: auto;
 | 
						||
  flex-direction: column;
 | 
						||
  background-color: #f7f8fa;
 | 
						||
  padding: 24rpx;
 | 
						||
}
 | 
						||
 | 
						||
.table-list-item {
 | 
						||
  background: #fff;
 | 
						||
  padding: 32rpx;
 | 
						||
  border-radius: 20rpx;
 | 
						||
  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
 | 
						||
  margin-bottom: 24rpx;
 | 
						||
}
 | 
						||
 | 
						||
.scan-btn {
 | 
						||
  height: 88rpx;
 | 
						||
  background: #4b8eff;
 | 
						||
  display: flex;
 | 
						||
  justify-content: center;
 | 
						||
  align-items: center;
 | 
						||
  border-radius: 12rpx;
 | 
						||
  font-size: 32rpx;
 | 
						||
  font-weight: 600;
 | 
						||
  box-shadow: 0 6rpx 20rpx rgba(55, 132, 251, 0.2);
 | 
						||
}
 | 
						||
 | 
						||
.coding-btn {
 | 
						||
  padding: 10rpx 0;
 | 
						||
  color: #fe9a09;
 | 
						||
  background-color: #fff7eb;
 | 
						||
  border: 1px solid #fe9a09;
 | 
						||
  border-radius: 12rpx;
 | 
						||
  text-align: center;
 | 
						||
  font-size: 28rpx;
 | 
						||
  font-weight: 600;
 | 
						||
}
 | 
						||
 | 
						||
/* 相机预览样式 */
 | 
						||
.camera-container {
 | 
						||
  position: fixed;
 | 
						||
  top: 0;
 | 
						||
  left: 0;
 | 
						||
  width: 100vw;
 | 
						||
  height: 100vh;
 | 
						||
  background-color: #000000;
 | 
						||
  z-index: 1000; /* 降低层级,让UI元素显示在上方 */
 | 
						||
}
 | 
						||
 | 
						||
.top-tip {
 | 
						||
  position: absolute;
 | 
						||
  top: 120rpx;
 | 
						||
  left: 50%;
 | 
						||
  transform: translateX(-50%);
 | 
						||
  z-index: 10001; /* 提高层级,确保显示在相机之上 */
 | 
						||
  width: 100%;
 | 
						||
  text-align: center;
 | 
						||
}
 | 
						||
 | 
						||
.tip-text {
 | 
						||
  background-color: rgba(0, 0, 0, 0.5);
 | 
						||
  color: #fff;
 | 
						||
  font-size: 32rpx;
 | 
						||
  font-weight: bold;
 | 
						||
  padding: 20rpx 30rpx;
 | 
						||
  border-radius: 10rpx;
 | 
						||
}
 | 
						||
 | 
						||
/* 聚焦状态提示 */
 | 
						||
.focus-tip {
 | 
						||
  position: absolute;
 | 
						||
  top: 200rpx;
 | 
						||
  left: 50%;
 | 
						||
  transform: translateX(-50%);
 | 
						||
  z-index: 10001; /* 提高层级 */
 | 
						||
  text-align: center;
 | 
						||
}
 | 
						||
 | 
						||
.focus-text {
 | 
						||
  background-color: rgba(75, 142, 255, 0.9);
 | 
						||
  color: #fff;
 | 
						||
  font-size: 28rpx;
 | 
						||
  font-weight: bold;
 | 
						||
  padding: 15rpx 25rpx;
 | 
						||
  border-radius: 8rpx;
 | 
						||
}
 | 
						||
 | 
						||
.viewfinder-container {
 | 
						||
  position: absolute;
 | 
						||
  top: 50%;
 | 
						||
  left: 50%;
 | 
						||
  transform: translate(-50%, -50%);
 | 
						||
  z-index: 10001; /* 提高取景框层级 */
 | 
						||
}
 | 
						||
 | 
						||
.viewfinder {
 | 
						||
  position: relative;
 | 
						||
  width: 500rpx;
 | 
						||
  height: 600rpx;
 | 
						||
  background-color: transparent;
 | 
						||
  border: 2rpx solid rgba(255, 255, 255, 0.5);
 | 
						||
  border-radius: 20rpx;
 | 
						||
  box-shadow: 0 0 20rpx rgba(75, 142, 255, 0.5);
 | 
						||
}
 | 
						||
 | 
						||
/* 扫描线动画 */
 | 
						||
.scan-line {
 | 
						||
  position: absolute;
 | 
						||
  top: 0;
 | 
						||
  left: 0;
 | 
						||
  width: 100%;
 | 
						||
  height: 4rpx;
 | 
						||
  background: linear-gradient(90deg, transparent, rgba(75, 142, 255, 0.8), transparent);
 | 
						||
  animation: scan 2s linear infinite;
 | 
						||
  border-radius: 2rpx;
 | 
						||
}
 | 
						||
 | 
						||
@keyframes scan {
 | 
						||
  0% {
 | 
						||
    top: 0;
 | 
						||
    opacity: 1;
 | 
						||
  }
 | 
						||
  50% {
 | 
						||
    opacity: 1;
 | 
						||
  }
 | 
						||
  100% {
 | 
						||
    top: calc(100% - 4rpx);
 | 
						||
    opacity: 0;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.corner {
 | 
						||
  position: absolute;
 | 
						||
  width: 60rpx;
 | 
						||
  height: 60rpx;
 | 
						||
  border: 6rpx solid rgba(75, 142, 255, 0.8);
 | 
						||
  background-color: transparent;
 | 
						||
}
 | 
						||
 | 
						||
.corner-top-left {
 | 
						||
  top: -6rpx;
 | 
						||
  left: -6rpx;
 | 
						||
  border-right: none;
 | 
						||
  border-bottom: none;
 | 
						||
  border-top-left-radius: 20rpx;
 | 
						||
}
 | 
						||
 | 
						||
.corner-top-right {
 | 
						||
  top: -6rpx;
 | 
						||
  right: -6rpx;
 | 
						||
  border-left: none;
 | 
						||
  border-bottom: none;
 | 
						||
  border-top-right-radius: 20rpx;
 | 
						||
}
 | 
						||
 | 
						||
.corner-bottom-left {
 | 
						||
  bottom: -6rpx;
 | 
						||
  left: -6rpx;
 | 
						||
  border-right: none;
 | 
						||
  border-top: none;
 | 
						||
  border-bottom-left-radius: 20rpx;
 | 
						||
}
 | 
						||
 | 
						||
.corner-bottom-right {
 | 
						||
  bottom: -6rpx;
 | 
						||
  right: -6rpx;
 | 
						||
  border-left: none;
 | 
						||
  border-top: none;
 | 
						||
  border-bottom-right-radius: 20rpx;
 | 
						||
}
 | 
						||
 | 
						||
/* 聚焦指示器 */
 | 
						||
.focus-indicator {
 | 
						||
  position: absolute;
 | 
						||
  width: 60rpx;
 | 
						||
  height: 60rpx;
 | 
						||
  z-index: 10002; /* 聚焦指示器最高层级 */
 | 
						||
}
 | 
						||
 | 
						||
.focus-ring {
 | 
						||
  width: 100%;
 | 
						||
  height: 100%;
 | 
						||
  border: 3rpx solid #4b8eff;
 | 
						||
  border-radius: 50%;
 | 
						||
  background-color: transparent;
 | 
						||
  transition: all 0.3s ease;
 | 
						||
}
 | 
						||
 | 
						||
.focus-ring.focusing {
 | 
						||
  border-color: #ff9500;
 | 
						||
  animation: focusPulse 1s ease-in-out infinite;
 | 
						||
}
 | 
						||
 | 
						||
.focus-ring.focused {
 | 
						||
  border-color: #00ff00;
 | 
						||
  transform: scale(1.2);
 | 
						||
}
 | 
						||
 | 
						||
@keyframes focusPulse {
 | 
						||
  0%, 100% {
 | 
						||
    transform: scale(1);
 | 
						||
    opacity: 1;
 | 
						||
  }
 | 
						||
  50% {
 | 
						||
    transform: scale(1.3);
 | 
						||
    opacity: 0.7;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.bottom-controls {
 | 
						||
  position: absolute;
 | 
						||
  bottom: 100rpx;
 | 
						||
  left: 0;
 | 
						||
  right: 0;
 | 
						||
  display: flex;
 | 
						||
  flex-direction: row;
 | 
						||
  justify-content: space-between;
 | 
						||
  align-items: center;
 | 
						||
  padding: 0 100rpx;
 | 
						||
  z-index: 10001; /* 提高底部控制按钮层级 */
 | 
						||
}
 | 
						||
 | 
						||
.control-btn {
 | 
						||
  width: 100rpx;
 | 
						||
  height: 100rpx;
 | 
						||
  background-color: rgba(0, 0, 0, 0.3);
 | 
						||
  border-radius: 50%;
 | 
						||
  display: flex;
 | 
						||
  justify-content: center;
 | 
						||
  align-items: center;
 | 
						||
  border: 2rpx solid rgba(255, 255, 255, 0.3);
 | 
						||
}
 | 
						||
 | 
						||
.control-icon {
 | 
						||
  color: #fff;
 | 
						||
  font-size: 40rpx;
 | 
						||
  font-weight: bold;
 | 
						||
}
 | 
						||
 | 
						||
.photo-btn {
 | 
						||
  width: 140rpx;
 | 
						||
  height: 140rpx;
 | 
						||
  background-color: rgba(0, 0, 0, 0.3);
 | 
						||
  border-radius: 50%;
 | 
						||
  display: flex;
 | 
						||
  justify-content: center;
 | 
						||
  align-items: center;
 | 
						||
  border: 6rpx solid rgba(255, 255, 255, 0.5);
 | 
						||
  transition: all 0.2s ease;
 | 
						||
}
 | 
						||
 | 
						||
.photo-btn.taking {
 | 
						||
  transform: scale(0.9);
 | 
						||
  background-color: rgba(75, 142, 255, 0.8);
 | 
						||
}
 | 
						||
 | 
						||
.photo-btn.disabled {
 | 
						||
  opacity: 0.5;
 | 
						||
  background-color: rgba(255, 255, 255, 0.1);
 | 
						||
}
 | 
						||
 | 
						||
.photo-btn-inner {
 | 
						||
  width: 100rpx;
 | 
						||
  height: 100rpx;
 | 
						||
  background-color: rgba(255, 255, 255, 0.9);
 | 
						||
  border-radius: 50%;
 | 
						||
  transition: all 0.2s ease;
 | 
						||
}
 | 
						||
 | 
						||
.photo-btn.taking .photo-btn-inner {
 | 
						||
  background-color: #4b8eff;
 | 
						||
}
 | 
						||
 | 
						||
.photo-btn.disabled .photo-btn-inner {
 | 
						||
  background-color: #ccc;
 | 
						||
}
 | 
						||
 | 
						||
.loading-overlay {
 | 
						||
  position: absolute;
 | 
						||
  top: 0;
 | 
						||
  left: 0;
 | 
						||
  width: 100%;
 | 
						||
  height: 100%;
 | 
						||
  background-color: rgba(0, 0, 0, 0.7);
 | 
						||
  display: flex;
 | 
						||
  justify-content: center;
 | 
						||
  align-items: center;
 | 
						||
  z-index: 10003; /* 加载层级最高 */
 | 
						||
}
 | 
						||
 | 
						||
.loading-content {
 | 
						||
  display: flex;
 | 
						||
  flex-direction: column;
 | 
						||
  align-items: center;
 | 
						||
  background-color: rgba(255, 255, 255, 0.9);
 | 
						||
  padding: 60rpx;
 | 
						||
  border-radius: 20rpx;
 | 
						||
}
 | 
						||
 | 
						||
.loading-spinner {
 | 
						||
  width: 60rpx;
 | 
						||
  height: 60rpx;
 | 
						||
  border: 6rpx solid #f3f3f3;
 | 
						||
  border-top: 6rpx solid #4b8eff;
 | 
						||
  border-radius: 50%;
 | 
						||
  animation: spin 1s linear infinite;
 | 
						||
  margin-bottom: 30rpx;
 | 
						||
}
 | 
						||
 | 
						||
@keyframes spin {
 | 
						||
  0% {
 | 
						||
    transform: rotate(0deg);
 | 
						||
  }
 | 
						||
  100% {
 | 
						||
    transform: rotate(360deg);
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.loading-text {
 | 
						||
  color: #333;
 | 
						||
  font-size: 32rpx;
 | 
						||
  font-weight: bold;
 | 
						||
}
 | 
						||
</style> |