博客主页 😞
uniapp实现仿网易云播放器页面(已实现自动获取音乐时长,上下首,循环播放,拖拽快进后退)(2025.7.18更新版实现真机效果)
uniapp实现仿网易云播放器页面(已实现自动获取音乐时长,上下首,循环播放,拖拽快进后退)(2025.7.18更新版实现真机效果)

Author:

芝麻

©

Wordage:

共计 50781 字

needs:

约 9 分钟

Popular:

70 ℃

Created:

:本文最后更新于2025年07月21日,已经过了172天没有更新,若内容或图片失效,请留言反馈
目 录

因公司业务需求,要求我在小程序中增加一个播放器,ui照着网易云画的,去github屎里掏金了半天都是旧版的仿制,所以自己整了个,当然主要的功能实现还是多亏了claude4,省了我很多事。
接下来就是代码实现(不涉及公司信息)

   <template>
  <view class="flex-col page">
    <view class="flex-col justify-start section">
      <view class="flex-col justify-start section_2">
        <view class="flex-row relative section_3">
          <image class="shrink-0 image_6 pos_7"
            src="https://tmf.bimhui.com/yuanxinapp/434598bf55fad60f3a1bb2fa03846917.png" />
          <image class="shrink-0 image_5 pos_6"
            src="https://tmf.bimhui.com/yuanxinapp/45720b5d243d18bfcd1b24de14186985.png" />

          <text class="text_2 pos_10">音频播放</text>
          <image class="shrink-0 image_7 pos_9"
            src="https://tmf.bimhui.com/yuanxinapp/1d2be95baecf8712ddfd3705a3213a0a.png" @click="goToMusicDetail" />
          <image class="shrink-0 image_10 pos_11"
            src="https://tmf.bimhui.com/yuanxinapp/f2483d8969f6ba88a6a81db169489c3c.png" />
          <image class="shrink-0 image pos"
            src="https://tmf.bimhui.com/yuanxinapp/a2474e70c7d79152aa9fb4955594e4b7.png" />
        </view>
      </view>
    </view>
    <view class="mt-12 flex-col relative group">
      <view class="self-stretch group_2"></view>
      <text class="self-start text_3">{{ musicInfo.title }}</text>
      <text class="self-start font text_4">{{ musicInfo.artist }}</text>
      <view class="flex-col justify-start self-stretch relative group_3">
        <view class="flex-col justify-start items-start section_7" @touchstart="onProgressTouchStart"
          @touchmove="onProgressTouchMove" @touchend="onProgressTouchEnd">
          <view class="shrink-0 section_8" :style="{ width: progressWidth + 'rpx' }"></view>
        </view>
        <view class="section_6"
          :style="{ position: 'absolute', left: progressPosition + 'rpx', top: '-2rpx', zIndex: 10 }"
          @touchstart="onSliderTouchStart" @touchmove="onSliderTouchMove" @touchend="onSliderTouchEnd"></view>
      </view>
      <view class="flex-row justify-between self-stretch group_4">
        <text class="font">{{ formatTime(currentTime) }}</text>
        <text class="font">{{ formatTime(duration) }}</text>
      </view>
      <view class="flex-row justify-between items-center self-stretch group_5">
        <image class="image_13" src="https://tmf.bimhui.com/yuanxinapp/4fe54501edc493932d686ef598cedac6.png"
          @click="toggleLoop" />
        <image class="image_13" src="https://tmf.bimhui.com/yuanxinapp/1f4a2fef79417999a0fff63545714403.png"
          @click="previousSong" />
        <view class="flex-col justify-start items-center image-wrapper_2" @click="togglePlay">
          <image class="image_12" :src="isPlaying ? pauseIcon : playIcon" />
        </view>
        <image class="image_13" src="https://tmf.bimhui.com/yuanxinapp/c0241c91e565d66db79d2e1beca7d2f0.png"
          @click="nextSong" />
        <image class="image_13" src="https://tmf.bimhui.com/yuanxinapp/3115d36bc1043fb8b471788ce5abeca7.png"
          @click="toggleMute" />
      </view>
      <image class="image_11 pos_12" :src="musicInfo.cover" />
    </view>
  </view>
</template>

<script>
export default {
  components: {},
  props: {},
  data() {
    return {
      // 歌曲列表
      playlist: [
        {
          title: '希望有羽毛和翅膀',
          artist: '知更鸟,HOYO-MiX,Chevy',
          cover: 'https://tmf.bimhui.com/yuanxinapp/e1865e1e52e5d214d0d02aa358075881.png',
          src: 'https://tmf.bimhui.com/yuanxinapp/知更鸟,HOYO-MiX,Chevy - 希望有羽毛和翅膀.mp3',
          duration: 0 // 预估时长设为0,防止无法判断音频是否加载成功
        },
        {
          title: '不眠之夜',
          artist: '张杰,HOYO-MiX',
          cover: 'https://tmf.bimhui.com/yuanxinapp/e1865e1e52e5d214d0d02aa358075881.png',
          src: 'https://tmf.bimhui.com/yuanxinapp/张杰,HOYO-MiX - 不眠之夜.mp3',
          duration: 0 // 预估时长设为0,防止无法判断音频是否加载成功
        },
        {
          title: '在银河中孤独摇摆',
          artist: '知更鸟,HOYO-MiX,Chevy',
          cover: 'https://tmf.bimhui.com/yuanxinapp/e1865e1e52e5d214d0d02aa358075881.png', // 暂时使用相同封面
          src: 'https://tmf.bimhui.com/yuanxinapp/知更鸟,HOYO-MiX,Chevy - 在银河中孤独摇摆.mp3',
          duration: 0 // 预估时长设为0,防止无法判断音频是否加载成功
        },
        {
          title: '耀斑',
          artist: 'HOYO-MiX,YMIR',
          cover: 'https://tmf.bimhui.com/yuanxinapp/e1865e1e52e5d214d0d02aa358075881.png', // 暂时使用相同封面
          src: 'https://tmf.bimhui.com/yuanxinapp/HOYO-MiX,YMIR - 耀斑.mp3',
          duration: 0 // 预估时长设为0,防止无法判断音频是否加载成功
        },
        {
          title: '拂晓 Proi Proi',
          artist: 'HOYO-MiX,NIDA',
          cover: 'https://tmf.bimhui.com/yuanxinapp/e1865e1e52e5d214d0d02aa358075881.png', // 暂时使用相同封面
          src: 'https://tmf.bimhui.com/yuanxinapp/HOYO-MiX,NIDA - 拂晓 Proi Proi.mp3',
          duration: 0 // 预估时长设为0,防止无法判断音频是否加载成功
        },
      ],
      
      // 当前播放的歌曲索引
      currentSongIndex: 0,
      // 播放状态
      isPlaying: false,
      currentTime: 0,
      duration: 0, // 当前歌曲的时长,初始为0,实际播放时会自动获取
      isLoop: false,
      isMuted: false,

      // 音频状态管理
      audioState: 'idle', // idle, loading, ready, playing, paused, error
      isAudioOperationInProgress: false, // 防止并发操作
      audioLoadPromise: null, // 音频加载Promise

      // 进度条相关
      progressBarWidth: 686, // 进度条总宽度(rpx)
      isDragging: false,
      dragStartTime: 0, // 拖拽开始时的时间
      lastDragTime: 0, // 上次拖拽的时间,用于防抖

      // 测试用的模拟播放
      testTimer: null,
      seekTimer: null, // 防抖跳转定时器

      // 音频管理
      audioContext: null,
      backgroundAudioManager: null,

      // 图标
      playIcon: 'https://tmf.bimhui.com/yuanxinapp/c941904fbcb7613763d50b98889c9f0a.png',
      pauseIcon: 'https://tmf.bimhui.com/yuanxinapp/pause.png', // 暂时使用相同图标

      // URL管理
      originalUrl: '', // 存储原始URL
      encodedUrl: '', // 存储编码后的URL
      
      // 环境检测
      isRealDevice: false // 是否为真机环境
    };
  },

  computed: {
    // 计算进度条宽度
    progressWidth() {
      if (this.duration === 0) return 0;
      return (this.currentTime / this.duration) * this.progressBarWidth;
    },

    // 计算滑块位置
    progressPosition() {
      if (this.duration === 0) return -17; // 初始位置在左边缘
      // 计算进度百分比,然后转换为实际像素位置
      const progress = this.currentTime / this.duration;
      const maxPosition = this.progressBarWidth - 34; // 减去滑块宽度
      return Math.max(-17, Math.min(progress * this.progressBarWidth - 17, maxPosition - 17));
    },

    // 获取当前播放的歌曲信息
    musicInfo() {
      return this.playlist[this.currentSongIndex];
    }
  },

    mounted() {
    const systemInfo = uni.getSystemInfoSync();
    
    // 检查小程序基础库版本
    if (systemInfo.SDKVersion) {
      const sdkVersion = systemInfo.SDKVersion;
      console.log('小程序基础库版本:', sdkVersion);
      
      // 背景音频需要基础库 1.2.0 以上
      const minVersion = '1.2.0';
      if (this.compareVersion(sdkVersion, minVersion) < 0) {
        console.warn(`当前基础库版本 ${sdkVersion} 过低,背景音频需要 ${minVersion} 以上版本`);
        uni.showModal({
          title: '版本提示',
          content: '当前微信版本过低,可能无法正常播放背景音频,建议升级到最新版本',
          showCancel: false
        });
      }
    }

    // 验证播放列表数据完整性
    if (!this.playlist || this.playlist.length === 0) {
      return;
    }
    
    if (!this.musicInfo) {
      return;
    }
    
    if (!this.musicInfo.src) {
      return;
    }

    // 初始化第一首歌曲的时长
    this.updateCurrentDuration();

    // 处理初始歌曲的URL编码
    this.processCurrentSongUrl();

    // 设置初始音频状态
    this.audioState = 'loading';
    
    this.initAudio();
    
    // 初始化完成后设置为就绪状态
    setTimeout(() => {
      if (this.audioState === 'loading') {
        this.audioState = 'ready';
        
        // 检查音频管理器是否成功初始化
        if (!this.backgroundAudioManager && !this.audioContext) {
          uni.showModal({
            title: '初始化失败',
            content: '音频播放器初始化失败,可能是由于权限限制或设备兼容性问题。将使用演示模式。',
            showCancel: false,
            confirmText: '知道了',
            success: () => {
              this.showAudioError();
            }
          });
        }
      }
    }, 500);

    // 检测真机环境
    this.detectRealDevice();

    // 延迟启动测试模式,确保音频初始化完成
    setTimeout(() => {
      this.startTestMode();
    }, 500);
  },

  methods: {
    // 初始化音频
    initAudio() {
      try {
        // 小程序使用背景音频管理器
        this.backgroundAudioManager = uni.getBackgroundAudioManager();
        
        if (!this.backgroundAudioManager) {
          throw new Error('背景音频管理器初始化失败');
        }
        
        this.setupBackgroundAudioEvents();
        
        // 如果背景音频管理器不可用,尝试使用InnerAudioContext作为备用方案
        if (!this.backgroundAudioManager) {
          try {
            this.audioContext = uni.createInnerAudioContext();
            if (this.audioContext) {
              this.setupAudioContextEvents();
            }
          } catch (innerAudioError) {
            // 静默处理错误
          }
        }

        // 智能处理音频URL
        if (this.musicInfo.src) {
          // 检查URL是否有效
          if (this.musicInfo.src.startsWith('http://') || this.musicInfo.src.startsWith('https://')) {
            // 检查URL是否包含需要编码的字符(中文、逗号、空格等)
            const needsEncoding = /[\u4e00-\u9fa5,\s]/.test(this.musicInfo.src);
            
            if (needsEncoding) {
              try {
                // 分离URL的基础部分和文件名部分
                const urlParts = this.musicInfo.src.split('/');
                const protocol = urlParts[0]; // https:
                const domain = urlParts[2]; // tmf.bimhui.com
                const pathParts = urlParts.slice(3); // ['yuanxinapp', '知更鸟,HOYO-MiX,Chevy - 希望有羽毛和翅膀.mp3']
                
                // 对路径的每个部分进行编码
                const encodedPathParts = pathParts.map(part => encodeURIComponent(part));
                
                // 重新组装URL
                const encodedUrl = `${protocol}//${domain}/${encodedPathParts.join('/')}`;

                // 存储两个版本的URL
                this.originalUrl = this.musicInfo.src;
                this.encodedUrl = encodedUrl;
                
                // 直接使用编码后的URL替换原始URL
                this.musicInfo.src = encodedUrl;
              } catch (encodeError) {
                // 静默处理编码错误
              }
            }
          }
        }
      } catch (error) {
        uni.showToast({
          title: '音频初始化失败',
          icon: 'none'
        });
      }
    },



    // 设置背景音频管理器事件(小程序)
    setupBackgroundAudioEvents() {
      if (!this.backgroundAudioManager) return;

      // 添加安全访问背景音频管理器属性的方法
      const safeGetProperty = (property, defaultValue = 0) => {
        try {
          return this.backgroundAudioManager[property] || defaultValue;
        } catch (error) {
          return defaultValue;
        }
      };

      this.backgroundAudioManager.onCanplay(() => {
        try {
          const duration = safeGetProperty('duration');
          this.updateAudioDuration('onCanplay', duration);
        } catch (error) {
          // 静默处理
        }
      });
      
      // 添加更多事件来确保获取准确时长
      this.backgroundAudioManager.onLoadedMetadata && this.backgroundAudioManager.onLoadedMetadata(() => {
        try {
          const duration = safeGetProperty('duration');
          this.updateAudioDuration('onLoadedMetadata', duration);
        } catch (error) {
          // 静默处理
        }
      });
      
      this.backgroundAudioManager.onTimeUpdate(() => {
        try {
          if (!this.isDragging) {
            this.currentTime = safeGetProperty('currentTime', 0);
            
            // 在播放过程中也检查时长是否有更新
            const duration = safeGetProperty('duration');
            if (duration && duration !== this.duration) {
              this.updateAudioDuration('onTimeUpdate', duration);
            }
            
            // 检查并校正时长
            this.checkAndCorrectDuration();
          }
        } catch (error) {
          // 静默处理
        }
      });

      this.backgroundAudioManager.onPlay(() => {
        this.isPlaying = true;
        this.audioState = 'playing';
      });

      this.backgroundAudioManager.onPause(() => {
        this.isPlaying = false;
        this.audioState = 'paused';
      });

      this.backgroundAudioManager.onEnded(() => {
        this.onAudioEnded();
      });

      this.backgroundAudioManager.onError((error) => {
        // 安全获取背景音频管理器的状态信息
        let audioManagerInfo = {};
        try {
          audioManagerInfo = {
            src: this.backgroundAudioManager.src || 'undefined',
            title: this.backgroundAudioManager.title || 'undefined',
            paused: this.backgroundAudioManager.paused,
            currentTime: this.backgroundAudioManager.currentTime || 0,
            duration: this.backgroundAudioManager.duration || 0
          };
        } catch (stateError) {
          audioManagerInfo = { error: '无法获取状态信息' };
        }
        
        this.isPlaying = false;
        this.audioState = 'error';
        this.isAudioOperationInProgress = false; // 重置操作锁
        
        // 根据错误码和错误信息提供更具体的错误信息
        let errorMessage = '音频播放失败';
        
        // 检查是否是 getTingAudioState 相关错误
        if (error.errMsg && error.errMsg.includes('getTingAudioState')) {
          errorMessage = '音频状态获取失败,可能是设备兼容性问题';
        } else if (error.errCode === 10001) {
          errorMessage = '系统错误';
        } else if (error.errCode === 10002) {
          errorMessage = '网络错误,请检查网络连接';
        } else if (error.errCode === 10003) {
          errorMessage = '文件错误,音频文件可能损坏';
        } else if (error.errCode === 10004) {
          errorMessage = '格式错误,不支持的音频格式';
        } else if (audioManagerInfo.src === 'undefined' || audioManagerInfo.src === 'null') {
          errorMessage = '音频源地址为空,请检查音频文件路径';
        }
        
        uni.showToast({
          title: errorMessage,
          icon: 'none',
          duration: 3000
        });
        
        // 如果是状态获取错误或音频源问题,尝试使用备用方案
        if ((error.errMsg && error.errMsg.includes('getTingAudioState')) || 
            audioManagerInfo.src === 'undefined' || 
            audioManagerInfo.src === 'null') {
          setTimeout(() => {
            this.handleBackgroundAudioFallback();
          }, 1000);
        }
      });

      this.backgroundAudioManager.onWaiting(() => {
        // 音频缓冲中
      });
    },

    // 设置音频上下文事件(H5和App)
    setupAudioContextEvents() {
      if (!this.audioContext) return;

      this.audioContext.onTimeUpdate(() => {
        if (!this.isDragging) {
          this.currentTime = this.audioContext.currentTime || 0;
          
          // 在播放过程中也检查时长是否有更新
          if (this.audioContext.duration && this.audioContext.duration !== this.duration) {
            this.updateAudioDuration('audioContext-onTimeUpdate', this.audioContext.duration);
          }
          
          // 检查并校正时长
          this.checkAndCorrectDuration();
        }
      });

      this.audioContext.onCanplay(() => {
        this.updateAudioDuration('audioContext-onCanplay', this.audioContext.duration);
      });
      
      // 添加loadedmetadata事件监听
      this.audioContext.onLoadedMetadata && this.audioContext.onLoadedMetadata(() => {
        this.updateAudioDuration('audioContext-onLoadedMetadata', this.audioContext.duration);
      });

      this.audioContext.onPlay(() => {
        this.isPlaying = true;
        this.audioState = 'playing';
        console.log('音频开始播放');
      });

      this.audioContext.onPause(() => {
        this.isPlaying = false;
        this.audioState = 'paused';
        console.log('音频暂停');
      });

      this.audioContext.onEnded(() => {
        console.log('音频播放结束');
        this.onAudioEnded();
      });

      this.audioContext.onError((error) => {
        console.error('音频播放错误:', error);
        this.isPlaying = false;
        this.audioState = 'error';
        this.isAudioOperationInProgress = false; // 重置操作锁
        uni.showToast({
          title: '音频播放失败,请检查网络连接',
          icon: 'none'
        });
      });

      this.audioContext.onWaiting(() => {
        console.log('音频缓冲中...');
      });
    },

    // 验证音频源是否有效
    validateAudioSource() {
      if (!this.musicInfo) {
        return false;
      }
      
      if (!this.musicInfo.src) {
        return false;
      }
      
      if (typeof this.musicInfo.src !== 'string') {
        return false;
      }
      
      if (this.musicInfo.src.trim() === '') {
        return false;
      }
      
      if (!this.musicInfo.src.startsWith('http://') && !this.musicInfo.src.startsWith('https://')) {
        return false;
      }
      
      return true;
    },

    // 测试音频URL是否可访问
    testAudioUrl() {
      return new Promise((resolve) => {
        // 先验证音频源
        if (!this.validateAudioSource()) {
          resolve(false);
          return;
        }
        
        uni.request({
          url: this.musicInfo.src,
          method: 'HEAD',
          success: (res) => {
            if (res.statusCode === 200) {
              resolve(true);
            } else {
              resolve(false);
            }
          },
          fail: (error) => {
            resolve(false);
          }
        });
      });
    },



    // 播放/暂停切换
    async togglePlay() {
      // 防止并发操作
      if (this.isAudioOperationInProgress) {
        return;
      }

      this.isAudioOperationInProgress = true;

      try {
        if (this.audioState === 'error') {
          await this.reinitializeAudio();
        }

        // 如果还没播放过,先测试URL
        if (this.audioState === 'idle' || (this.audioState === 'ready' && this.currentTime === 0)) {
          await this.tryPlayAudio();
        } else {
          await this.doTogglePlay();
        }
        
      } catch (error) {
        this.audioState = 'error';
        uni.showToast({
          title: '播放失败: ' + error.message,
          icon: 'none'
        });
        // 如果真实播放失败,回退到测试模式
        this.toggleTestPlay();
      } finally {
        this.isAudioOperationInProgress = false;
      }
    },

    // 重新初始化音频
    async reinitializeAudio() {
      this.audioState = 'loading';
      
      try {
        // 停止当前音频
        this.stopCurrentSong();
        
        // 重新初始化
        await new Promise(resolve => {
          setTimeout(() => {
            this.initAudio();
            this.audioState = 'ready';
            resolve();
          }, 100);
        });
      } catch (error) {
        this.audioState = 'error';
        throw error;
      }
    },

    // 尝试播放音频,如果失败则尝试其他URL
    async tryPlayAudio() {
      try {
        this.audioState = 'loading';
        
        console.log('开始测试音频URL可用性');
        console.log('当前使用的URL:', this.musicInfo.src);
        
        // 测试当前URL(已经在初始化时进行了编码处理)
        const isValid = await this.testAudioUrl();
        
        if (isValid) {
          console.log('音频URL可用,开始播放');
          this.audioState = 'ready';
          await this.doTogglePlay();
        } else {
          console.log('音频URL测试失败');
          
          // 如果当前URL失败,且存在原始URL,尝试原始URL
          if (this.originalUrl && this.originalUrl !== this.musicInfo.src) {
            console.log('尝试使用原始URL:', this.originalUrl);
            const currentSrc = this.musicInfo.src;
            this.musicInfo.src = this.originalUrl;

            const originalValid = await this.testAudioUrl();
            if (originalValid) {
              console.log('原始URL可用,开始播放');
              this.audioState = 'ready';
              await this.doTogglePlay();
            } else {
              // 恢复编码URL并显示错误
              this.musicInfo.src = currentSrc;
              console.log('所有URL都无法访问');
              this.audioState = 'error';
              this.showAudioError();
            }
          } else {
            console.log('没有备用URL可尝试');
            this.audioState = 'error';
            this.showAudioError();
          }
        }
      } catch (error) {
        console.error('尝试播放音频失败:', error);
        this.audioState = 'error';
        throw error;
      }
    },

    // 处理背景音频降级
    handleBackgroundAudioFallback() {
      // 先尝试使用 InnerAudioContext
      if (!this.audioContext) {
        try {
          this.audioContext = uni.createInnerAudioContext();
          if (this.audioContext) {
            this.setupAudioContextEvents();
            
            uni.showModal({
              title: '切换播放方式',
              content: '背景音频不兼容,已切换到普通音频播放模式。\n\n注意:切换到其他应用时音频会暂停。',
              showCancel: false,
              confirmText: '知道了'
            });
            return;
          }
        } catch (error) {
          // 静默处理
        }
      }
      
      // 如果 InnerAudioContext 也不可用,显示错误信息
      this.showAudioError();
    },

    // 显示音频错误提示
    showAudioError() {
      uni.showModal({
        title: '音频播放器提示',
        content: '真机环境下音频播放可能受到以下因素影响:\n\n1. 小程序音频播放权限\n2. 网络连接状态\n3. 音频文件访问权限\n4. 设备兼容性问题\n5. 微信版本或基础库版本\n6. getTingAudioState API兼容性\n\n现在将使用演示模式展示播放器功能(进度条会动但无声音)',
        showCancel: false,
        confirmText: '知道了',
        success: () => {
          this.toggleTestPlay();
        }
      });
    },

    // 执行实际的播放/暂停操作
    async doTogglePlay() {
      try {
        // 小程序优先使用背景音频管理器
        if (this.backgroundAudioManager) {
          console.log('使用背景音频管理器播放');
          if (this.isPlaying) {
            console.log('=== 执行暂停操作 ===');
            console.log('当前音频状态:', {
              src: this.backgroundAudioManager.src,
              paused: this.backgroundAudioManager.paused,
              currentTime: this.backgroundAudioManager.currentTime
            });
            
            await this.safeAudioOperation(() => {
              this.backgroundAudioManager.pause();
            });
            this.audioState = 'paused';
            console.log('暂停操作完成');
          } else {
            console.log('=== 执行播放操作 ===');
            
            // 验证音频源
            if (!this.validateAudioSource()) {
              console.error('音频源验证失败,无法播放');
              throw new Error('音频源无效');
            }
            
            // 检查是否是续播(已有音频源且处于暂停状态)
            const isResume = this.backgroundAudioManager.src && 
                           this.backgroundAudioManager.src === this.musicInfo.src &&
                           this.audioState === 'paused';
            
            if (isResume) {
              console.log('检测到续播操作');
              
              // 使用专门的真机续播处理方法
              const resumeSuccess = await this.handleRealDeviceResume();
              
              if (!resumeSuccess) {
                console.log('真机续播失败,尝试标准续播方法');
                await this.safeAudioOperation(() => {
                  this.backgroundAudioManager.play();
                });
                this.audioState = 'playing';
              }
              
              console.log('续播操作完成');
            } else {
              console.log('检测到新播放操作,设置音频信息');
              console.log('设置背景音频信息:', {
                title: this.musicInfo.title,
                singer: this.musicInfo.artist,
                cover: this.musicInfo.cover,
                src: this.musicInfo.src
              });
              
              // 安全设置背景音频管理器属性
              try {
                console.log('开始设置背景音频管理器属性');
                
                // 逐个安全设置属性
                const setProperty = (property, value, description) => {
                  try {
                    this.backgroundAudioManager[property] = value;
                    console.log(`${description}设置成功:`, value);
                  } catch (error) {
                    console.warn(`${description}设置失败:`, error);
                    // 继续执行,不抛出错误
                  }
                };
                
                setProperty('title', this.musicInfo.title, '音频标题');
                setProperty('singer', this.musicInfo.artist, '音频演唱者');
                setProperty('coverImgUrl', this.musicInfo.cover, '音频封面');
                
                // 延迟设置src,确保其他属性先设置完成
                setTimeout(() => {
                  try {
                    this.backgroundAudioManager.src = this.musicInfo.src;
                  } catch (srcError) {
                    // 如果src设置失败,尝试降级方案
                    this.handleBackgroundAudioFallback();
                  }
                }, 150);
                
              } catch (managerError) {
                console.error('设置背景音频管理器失败:', managerError);
                // 不直接抛出错误,而是尝试降级方案
                this.handleBackgroundAudioFallback();
                return;
              }
              
              // 设置src后会自动播放
              this.audioState = 'playing';
            }
          }
          return;
        }
        
        // 如果背景音频管理器不可用,使用InnerAudioContext
        if (this.audioContext) {
          console.log('背景音频管理器不可用,使用InnerAudioContext');
          if (this.isPlaying) {
            console.log('暂停播放');
            await this.safeAudioOperation(() => {
              this.audioContext.pause();
            });
            this.audioState = 'paused';
          } else {
            console.log('开始播放');
            
            // 验证音频源
            if (!this.validateAudioSource()) {
              console.error('音频源验证失败,无法播放');
              throw new Error('音频源无效');
            }
            
            this.audioContext.src = this.musicInfo.src;
            console.log('InnerAudioContext音频源设置完成');
            
            await this.safeAudioOperation(() => {
              this.audioContext.play();
            });
            this.audioState = 'playing';
          }
          return;
        }

        // 如果没有音频管理器,使用测试模式
        console.log('使用测试播放模式');
        this.toggleTestPlay();
        this.audioState = this.isPlaying ? 'playing' : 'paused';

      } catch (error) {
        console.error('实际播放操作失败:', error);
        this.audioState = 'error';
        this.toggleTestPlay();
      }
    },

    // 真机环境下的续播处理
    async handleRealDeviceResume() {
      console.log('=== 真机续播处理 ===');
      
      if (!this.backgroundAudioManager) {
        console.warn('背景音频管理器不存在,无法续播');
        return false;
      }
      
      try {
        // 检查背景音频管理器状态
        const audioState = {
          src: this.backgroundAudioManager.src,
          paused: this.backgroundAudioManager.paused,
          currentTime: this.backgroundAudioManager.currentTime,
          duration: this.backgroundAudioManager.duration
        };
        
        console.log('当前背景音频状态:', audioState);
        
        // 如果音频处于暂停状态且有有效的src,尝试续播
        if (audioState.src && audioState.src === this.musicInfo.src) {
          console.log('执行续播操作');
          
          // 在真机环境下,有时需要强制重新设置一些属性
          this.backgroundAudioManager.title = this.musicInfo.title;
          
          // 调用play方法
          this.backgroundAudioManager.play();
          
          // 等待播放状态更新
          return new Promise((resolve) => {
            let checkCount = 0;
            const maxChecks = 10;
            
            const checkPlayState = () => {
              checkCount++;
              console.log(`续播状态检查 ${checkCount}/${maxChecks}:`, {
                isPlaying: this.isPlaying,
                paused: this.backgroundAudioManager.paused
              });
              
              if (this.isPlaying || checkCount >= maxChecks) {
                if (this.isPlaying) {
                  console.log('续播成功');
                  resolve(true);
                } else {
                  console.warn('续播可能失败,但不阻断流程');
                  resolve(false);
                }
              } else {
                setTimeout(checkPlayState, 200);
              }
            };
            
            setTimeout(checkPlayState, 100);
          });
        } else {
          console.log('不满足续播条件,需要重新设置音频源');
          return false;
        }
      } catch (error) {
        console.error('真机续播处理失败:', error);
        return false;
      }
    },

    // 安全的音频操作包装器
    async safeAudioOperation(operation) {
      return new Promise((resolve, reject) => {
        try {
          const result = operation();
          
          // 如果operation返回Promise,等待它完成
          if (result && typeof result.then === 'function') {
            result
              .then(() => resolve())
              .catch((error) => {
                console.warn('音频操作Promise被拒绝:', error);
                // 即使Promise被拒绝,也不完全失败,可能是快速切换导致的
                resolve();
              });
          } else {
            // 给操作一些时间完成
            setTimeout(() => resolve(), 50);
          }
        } catch (error) {
          console.error('音频操作执行失败:', error);
          reject(error);
        }
      });
    },

    // 音频播放结束
    onAudioEnded() {
      console.log('音频播放结束,循环模式:', this.isLoop);
      
      if (this.isLoop) {
        // 循环播放当前歌曲
        console.log('循环播放:重新开始当前歌曲');
        this.currentTime = 0;
        
        // 确保播放状态正确,为重新播放做准备
        this.isPlaying = false;
        
        // 清理可能存在的定时器
        if (this.testTimer) {
          clearInterval(this.testTimer);
          this.testTimer = null;
        }
        
        // 重新开始播放
        setTimeout(() => {
          console.log('循环播放:开始播放');
          this.togglePlay();
        }, 200);
      } else {
        // 检查是否还有下一首歌曲
        if (this.currentSongIndex < this.playlist.length - 1) {
          console.log('当前歌曲播放完毕,自动播放下一首');
          // 自动播放下一首
          this.nextSong();
        } else {
          console.log('播放列表已结束');
          this.isPlaying = false;
          this.currentTime = 0;
          uni.showToast({
            title: '播放列表已结束',
            icon: 'none'
          });
        }
      }
    },

    // 设置播放时间
    setCurrentTime(time) {
      // 这个方法现在由 seekToPosition 替代,保留是为了向后兼容
      this.seekToPosition(time);
    },

    // 更新当前歌曲的时长
    updateCurrentDuration() {
      this.duration = this.musicInfo.duration;
      console.log('初始化歌曲时长:', this.formatTime(this.duration));
      console.log('注意:实际播放时长可能与预设不同,系统会自动校正');
    },

    // 更新音频时长
    updateAudioDuration(eventType, duration) {
      if (duration !== undefined && duration !== null && duration > 0) {
        const oldDuration = this.duration;
        this.duration = duration;
        
        if (Math.abs(oldDuration - duration) > 1) { // 时长差异超过1秒才输出日志
          console.log(`音频时长更新 (事件: ${eventType}): ${this.formatTime(oldDuration)} → ${this.formatTime(this.duration)}`);
        }
        
        // 更新playlist中的时长信息
        this.playlist[this.currentSongIndex].duration = duration;
      } else {
        console.warn(`音频时长未从事件 ${eventType} 获取到有效值,当前时长:`, this.formatTime(this.duration));
      }
    },
    
    // 检查并校正时长
    checkAndCorrectDuration() {
      // 如果当前播放时间超过了预设时长,说明时长不准确
      if (this.currentTime > this.duration && this.currentTime > 0) {
        const newDuration = Math.ceil(this.currentTime + 10); // 增加10秒缓冲
        console.log(`时长校正: 播放时间 ${this.formatTime(this.currentTime)} 超过预设时长 ${this.formatTime(this.duration)}`);
        console.log(`自动校正时长为: ${this.formatTime(newDuration)}`);
        this.updateAudioDuration('auto-correction', newDuration);
      }
    },

    // 进度条触摸开始
    onProgressTouchStart(e) {
      this.isDragging = true;
      this.dragStartTime = this.currentTime;
      this.updateProgress(e);
      
      // 添加视觉反馈
      this.showProgressFeedback(true);
      
      // 防止页面滚动
      e.preventDefault && e.preventDefault();
    },

    // 进度条触摸移动
    onProgressTouchMove(e) {
      if (this.isDragging) {
        this.updateProgress(e);
        
        // 防止页面滚动
        e.preventDefault && e.preventDefault();
      }
    },

    // 进度条触摸结束
    onProgressTouchEnd(e) {
      console.log('进度跳转到:', this.formatTime(this.currentTime));
      
      this.isDragging = false;
      this.showProgressFeedback(false);
      
      // 执行音乐跳转(添加防抖)
      this.debouncedSeek(this.currentTime);
    },

    // 滑块触摸开始
    onSliderTouchStart(e) {
      this.isDragging = true;
      this.dragStartTime = this.currentTime;
      this.showProgressFeedback(true);
      
      // 防止页面滚动
      e.preventDefault && e.preventDefault();
    },

    // 滑块触摸移动
    onSliderTouchMove(e) {
      if (this.isDragging) {
        this.updateProgress(e);
        
        // 防止页面滚动
        e.preventDefault && e.preventDefault();
      }
    },

    // 滑块触摸结束
    onSliderTouchEnd(e) {
      console.log('进度跳转到:', this.formatTime(this.currentTime));
      
      this.isDragging = false;
      this.showProgressFeedback(false);
      
      // 执行音乐跳转(添加防抖)
      this.debouncedSeek(this.currentTime);
    },

    // 更新进度
    updateProgress(e) {
      const touch = e.touches[0];

      uni.createSelectorQuery().in(this).select('.section_7').boundingClientRect((rect) => {
        if (rect) {
          let offsetX = touch.clientX - rect.left;
          offsetX = Math.max(0, Math.min(offsetX, rect.width));
          const progress = offsetX / rect.width;
          const newTime = progress * this.duration;
          
          // 限制在有效范围内
          this.currentTime = Math.max(0, Math.min(newTime, this.duration));
        }
      }).exec();
    },
    
    // 防抖跳转
    debouncedSeek(time) {
      // 清除之前的定时器
      if (this.seekTimer) {
        clearTimeout(this.seekTimer);
      }
      
      // 设置新的定时器
      this.seekTimer = setTimeout(() => {
        this.seekToPosition(time);
        this.seekTimer = null;
      }, 200); // 200ms 防抖
    },
    
    // 跳转到指定位置
    seekToPosition(time) {
      try {
        console.log('执行音乐跳转到:', this.formatTime(time));
        
        if (this.backgroundAudioManager) {
          try {
            // 安全检查背景音频管理器状态
            const src = this.backgroundAudioManager.src;
            if (src && src !== 'undefined' && src !== 'null') {
              // 小程序背景音频跳转
              this.backgroundAudioManager.seek(time);
              uni.showToast({
                title: `跳转到 ${this.formatTime(time)}`,
                icon: 'none',
                duration: 1000
              });
              return;
            } else {
              console.warn('背景音频src为空,无法执行跳转');
            }
          } catch (seekError) {
            console.warn('背景音频跳转失败:', seekError);
            // 继续尝试其他方案
          }
        }
        
        if (this.audioContext && this.audioContext.src) {
          // InnerAudioContext音频跳转
          this.audioContext.seek(time);
          uni.showToast({
            title: `跳转到 ${this.formatTime(time)}`,
            icon: 'none',
            duration: 1000
          });
          return;
        }
        
        // 如果是测试模式,直接设置时间
        console.log('测试模式下的时间跳转');
        uni.showToast({
          title: `演示模式:跳转到 ${this.formatTime(time)}`,
          icon: 'none',
          duration: 1000
        });
        
        // 在测试模式下,如果正在播放,重新同步定时器
        if (this.isPlaying && this.testTimer) {
          // 重新开始计时
          clearInterval(this.testTimer);
          this.testTimer = setInterval(() => {
            if (!this.isDragging) {
              this.currentTime += 1;
              if (this.currentTime >= this.duration) {
                if (this.isLoop) {
                  this.currentTime = 0;
                } else {
                  this.currentTime = this.duration;
                  clearInterval(this.testTimer);
                  this.testTimer = null;
                  this.isPlaying = false;
                }
              }
            }
          }, 1000);
        }
        
      } catch (error) {
        console.error('音乐跳转失败:', error);
        uni.showToast({
          title: '跳转失败',
          icon: 'none'
        });
      }
    },
    
    // 显示进度反馈
    showProgressFeedback(show) {
      // 可以在这里添加拖拽时的视觉效果
      // 可以添加一些视觉反馈,比如改变滑块颜色等
      // 这里可以设置一些状态来改变UI样式
    },

    // 格式化时间
    formatTime(seconds) {
      const mins = Math.floor(seconds / 60);
      const secs = Math.floor(seconds % 60);
      return `${mins}:${secs.toString().padStart(2, '0')}`;
    },

    // 切换循环模式
    toggleLoop() {
      this.isLoop = !this.isLoop;
      uni.showToast({
        title: this.isLoop ? '已开启循环播放' : '已关闭循环播放',
        icon: 'none'
      });
    },

    // 切换静音
    toggleMute() {
      try {
        // #ifdef H5 || APP-PLUS
        if (this.audioContext) {
          // InnerAudioContext 没有直接的静音方法,这里通过暂停实现
          if (this.isMuted) {
            this.audioContext.play();
          } else {
            this.audioContext.pause();
          }
        }
        // #endif

        this.isMuted = !this.isMuted;
        uni.showToast({
          title: this.isMuted ? '已静音' : '已取消静音',
          icon: 'none'
        });
      } catch (error) {
        console.error('静音操作失败:', error);
      }
    },

    // 上一首
    previousSong() {
      console.log('切换到上一首歌曲');
      
      // 保存当前播放状态
      const wasPlaying = this.isPlaying;
      console.log('保存播放状态:', wasPlaying);
      
      // 停止当前播放并重置状态
      this.stopCurrentSong();
      this.resetPlaybackState();
      
      // 切换到上一首
      this.currentSongIndex = this.currentSongIndex === 0 
        ? this.playlist.length - 1 
        : this.currentSongIndex - 1;
      
      // 更新歌曲时长
      this.updateCurrentDuration();
      
      console.log('当前歌曲:', this.musicInfo.title);
      
      // 显示切换提示
      uni.showToast({
        title: `切换到:${this.musicInfo.title}`,
        icon: 'none',
        duration: 2000
      });
      
      // 重新初始化音频并恢复播放状态
      setTimeout(() => {
        // 先处理新歌曲的URL编码
        this.processCurrentSongUrl();
        this.initAudio();
        
        // 如果之前在播放,自动播放新歌曲
        if (wasPlaying) {
          console.log('恢复播放状态,开始播放新歌曲');
          setTimeout(() => {
            this.togglePlay();
          }, 500);
        }
      }, 300);
    },

    // 下一首
    nextSong() {
      console.log('切换到下一首歌曲');
      
      // 保存当前播放状态
      const wasPlaying = this.isPlaying;
      console.log('保存播放状态:', wasPlaying);
      
      // 停止当前播放并重置状态
      this.stopCurrentSong();
      this.resetPlaybackState();
      
      // 切换到下一首
      this.currentSongIndex = (this.currentSongIndex + 1) % this.playlist.length;
      
      // 更新歌曲时长
      this.updateCurrentDuration();
      
      console.log('当前歌曲:', this.musicInfo.title);
      
      // 显示切换提示
      uni.showToast({
        title: `切换到:${this.musicInfo.title}`,
        icon: 'none',
        duration: 2000
      });
      
      // 重新初始化音频并恢复播放状态
      setTimeout(() => {
        // 先处理新歌曲的URL编码
        this.processCurrentSongUrl();
        this.initAudio();
        
        // 如果之前在播放,自动播放新歌曲
        if (wasPlaying) {
          console.log('恢复播放状态,开始播放新歌曲');
          setTimeout(() => {
            this.togglePlay();
          }, 500);
        }
      }, 300);
    },

    // 返回上一页
    goToMusicDetail() {
      // 停止播放
      this.stopAudio();
      uni.navigateBack({
        delta: 1
      });
    },

    // 停止音频播放
    stopAudio() {
      try {
        if (this.backgroundAudioManager) {
          this.backgroundAudioManager.stop();
        }

        if (this.audioContext) {
          this.audioContext.stop();
        }

        this.isPlaying = false;

        // 清除测试定时器
        if (this.testTimer) {
          clearInterval(this.testTimer);
          this.testTimer = null;
        }
      } catch (error) {
        // 静默处理
      }
    },

    // 停止当前播放的歌曲
    stopCurrentSong() {
      // 清理定时器
      if (this.testTimer) {
        clearInterval(this.testTimer);
        this.testTimer = null;
      }
      
      if (this.seekTimer) {
        clearTimeout(this.seekTimer);
        this.seekTimer = null;
      }
      
      // 清理音频源
      try {
        if (this.backgroundAudioManager) {
          this.backgroundAudioManager.stop();
        }
        
        if (this.audioContext) {
          this.audioContext.stop();
        }
      } catch (error) {
        // 静默处理
      }
    },

    // 重置播放状态
    resetPlaybackState() {
      this.isPlaying = false;
      this.currentTime = 0;
      this.isDragging = false;
      
      // 清理定时器
      if (this.testTimer) {
        clearInterval(this.testTimer);
        this.testTimer = null;
      }
      
      if (this.seekTimer) {
        clearTimeout(this.seekTimer);
        this.seekTimer = null;
      }
    },

    // 检测真机环境
    detectRealDevice() {
      const systemInfo = uni.getSystemInfoSync();
      const isRealDevice = systemInfo.platform !== 'devtools';
      
      if (isRealDevice) {
        // 存储真机环境标识
        this.isRealDevice = true;
      } else {
        this.isRealDevice = false;
      }
    },

    // 显示URL编码测试结果


    // 启动测试模式(用于验证滑块跟随)
    startTestMode() {
      // 音频播放器已就绪
    },

    // 处理当前歌曲的URL编码
    processCurrentSongUrl() {
      if (!this.musicInfo || !this.musicInfo.src) {
        return;
      }

      const originalSrc = this.musicInfo.src;

      // 检查URL是否包含需要编码的字符(中文、逗号、空格等)
      const needsEncoding = /[\u4e00-\u9fa5,\s]/.test(originalSrc);
      
      if (needsEncoding && originalSrc.startsWith('http')) {
        try {
          // 分离URL的基础部分和文件名部分
          const urlParts = originalSrc.split('/');
          const protocol = urlParts[0]; // https:
          const domain = urlParts[2]; // tmf.bimhui.com
          const pathParts = urlParts.slice(3); // ['yuanxinapp', '知更鸟,HOYO-MiX,Chevy - 希望有羽毛和翅膀.mp3']
          
          // 对路径的每个部分进行编码
          const encodedPathParts = pathParts.map(part => encodeURIComponent(part));
          
          // 重新组装URL
          const encodedUrl = `${protocol}//${domain}/${encodedPathParts.join('/')}`;

          // 存储两个版本的URL
          this.originalUrl = originalSrc;
          this.encodedUrl = encodedUrl;
          
          // 直接使用编码后的URL替换原始URL
          this.musicInfo.src = encodedUrl;
        } catch (encodeError) {
          // 静默处理编码错误
        }
      } else {
        // 清空之前的编码缓存
        this.originalUrl = '';
        this.encodedUrl = '';
      }
    },

    // 版本比较函数
    compareVersion(version1, version2) {
      const v1 = version1.split('.');
      const v2 = version2.split('.');
      const len = Math.max(v1.length, v2.length);

      while (v1.length < len) {
        v1.push('0');
      }
      while (v2.length < len) {
        v2.push('0');
      }

      for (let i = 0; i < len; i++) {
        const num1 = parseInt(v1[i]);
        const num2 = parseInt(v2[i]);

        if (num1 > num2) {
          return 1;
        } else if (num1 < num2) {
          return -1;
        }
      }

      return 0;
    },

    // 切换测试播放
    toggleTestPlay() {
      if (this.isPlaying) {
        // 停止测试
        if (this.testTimer) {
          clearInterval(this.testTimer);
          this.testTimer = null;
        }
        this.isPlaying = false;
      } else {
        // 开始测试播放
        this.isPlaying = true;
        this.testTimer = setInterval(() => {
          if (!this.isDragging) {
            this.currentTime += 1;
            
            // 在测试模式中也检查并校正时长
            this.checkAndCorrectDuration();
            
            if (this.currentTime >= this.duration) {
              if (this.isLoop) {
                this.currentTime = 0;
              } else {
                this.currentTime = this.duration;
                clearInterval(this.testTimer);
                this.testTimer = null;
                this.isPlaying = false;
              }
            }
          }
        }, 1000);
      }
    }
  },

  // 页面卸载时停止播放
  beforeDestroy() {
    this.stopAudio();

    // 清理测试定时器
    if (this.testTimer) {
      clearInterval(this.testTimer);
      this.testTimer = null;
    }
    
    // 清理防抖定时器
    if (this.seekTimer) {
      clearTimeout(this.seekTimer);
      this.seekTimer = null;
    }

    try {
      if (this.audioContext) {
        this.audioContext.destroy();
      }
          } catch (error) {
        // 静默处理
      }
  }
};
</script>

<style scoped lang="scss">
.ml-11 {
  margin-left: 22rpx;
}

.page {
  padding-bottom: 166rpx;
  background-color: #1d1311;
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  height: 100%;

  .section {
    background-color: #1d1311;
    overflow: hidden;
    height: 176.4rpx;

    .section_2 {
      overflow: hidden;
      background-color: #ffffff00;
      height: 184rpx;
      border-left: solid 2rpx #707070;
      border-right: solid 2rpx #707070;
      border-top: solid 2rpx #707070;
      border-bottom: solid 2rpx #707070;

      .section_3 {
        padding: 26rpx 12rpx 64rpx 34rpx;
        background-color: #ffffff00;
        overflow: hidden;
        height: 230rpx;

        .image_6 {
          width: 36rpx;
          height: 32rpx;
        }

        .pos_7 {
          position: absolute;
          right: 210rpx;
          top: 50%;
          transform: translateY(-50%);
        }

        .image_4 {
          width: 48rpx;
          height: 22.66rpx;
        }

        .pos_5 {
          position: absolute;
          right: 30rpx;
          top: 36rpx;
        }

        .image_3 {
          width: 30rpx;
          height: 22rpx;
        }

        .pos_4 {
          position: absolute;
          right: 88rpx;
          top: 36rpx;
        }

        .image_2 {
          width: 34rpx;
          height: 22rpx;
        }

        .pos_3 {
          position: absolute;
          right: 128rpx;
          top: 36rpx;
        }

        .text-wrapper {
          padding: 8rpx 0;
          overflow: hidden;
          width: 64rpx;

          .text {
            color: #ffffff;
            font-size: 30rpx;
            line-height: 22.26rpx;
          }
        }

        .pos_2 {
          position: absolute;
          left: 62rpx;
          top: 26rpx;
        }

        .image_5 {
          width: 46rpx;
          height: 12rpx;
        }

        .pos_6 {
          position: absolute;
          left: 78rpx;
          top: 86rpx;
        }

        .section_4 {
          padding: 12rpx 24rpx;
          background-color: #4c4c4c80;
          border-radius: 50rpx;
          width: 174rpx;
          border-left: solid 1rpx #96969633;
          border-right: solid 1rpx #96969633;
          border-top: solid 1rpx #96969633;
          border-bottom: solid 1rpx #96969633;

          .image-wrapper {
            width: 38rpx;

            .image_9 {
              width: 38rpx;
              height: 14rpx;
            }

            .image_8 {
              width: 34rpx;
              height: 34rpx;
            }
          }

          .section_5 {
            background-color: #00000033;
            width: 1rpx;
            height: 37rpx;
          }
        }

        .pos_8 {
          position: absolute;
          right: 12rpx;
          top: 100rpx;
        }

        .text_2 {
          color: #ffffff;
          font-size: 34rpx;
          font-family: Alibaba PuHuiTi;
          line-height: 31.62rpx;
          opacity: 0.9;
        }

        .pos_10 {
          position: absolute;
          right: 300rpx;
          bottom: 82.38rpx;
        }

        .image_7 {
          width: 18rpx;
          height: 34rpx;
        }

        .pos_9 {
          position: absolute;
          left: 34rpx;
          bottom: 80rpx;
        }

        .image_10 {
          width: 4rpx;
          height: 4rpx;
        }

        .pos_11 {
          position: absolute;
          left: 124rpx;
          bottom: 64rpx;
        }

        .image {
          width: 97.6vw;
          height: 23.4667vw;
        }

        .pos {
          position: absolute;
          left: 18rpx;
          right: 0;
          top: 0;
        }
      }
    }
  }

  .group {
    padding: 0 32rpx;

    .group_2 {
      border-radius: 56rpx;
      overflow: hidden;
      height: 972rpx;
    }

    .text_3 {
      margin-top: 28rpx;
      color: #ffffff;
      font-size: 40rpx;
      font-family: PingFang SC;
      line-height: 37.36rpx;
    }

    .text_4 {
      margin-top: 24rpx;
      line-height: 25.9rpx;
    }

    .group_3 {
      margin-top: 32rpx;
      padding: 12rpx 0;

      .section_7 {
        background-color: #363d46;
        border-radius: 40rpx;
        overflow: hidden;
        width: 686rpx;
        height: 12rpx;
        position: relative;

        .section_8 {
          background-color: #7bcdc3;
          border-radius: 40rpx;
          overflow: hidden;
          width: 184rpx;
          height: 12rpx;
        }
      }

      .section_6 {
        background-color: #ffffff;
        border-radius: 50%;
        width: 34rpx;
        height: 34rpx;
      }

    }

    .group_4 {
      margin-top: 36rpx;
    }

    .group_5 {
      margin-top: 32rpx;

      .image_13 {
        width: 56rpx;
        height: 56rpx;
      }

      .image-wrapper_2 {
        padding: 20rpx 0;
        background-color: #49515a6b;
        border-radius: 50%;
        width: 104rpx;
        height: 104rpx;

        .image_12 {
          width: 64rpx;
          height: 64rpx;
        }
      }
    }

    .image_11 {
      border-radius: 56rpx;
      overflow: hidden;
      width: 91.2vw;
      height: 129.6vw;
    }

    .pos_12 {
      position: absolute;
      left: 34rpx;
      right: 32rpx;
      top: 0;
    }
  }

  .font {
    font-size: 28rpx;
    font-family: PingFang SC;
    line-height: 20.78rpx;
    color: #a9b0c2;
  }
}
</style>
文章二维码
uniapp实现仿网易云播放器页面(已实现自动获取音乐时长,上下首,循环播放,拖拽快进后退)(2025.7.18更新版实现真机效果)
 博主关闭了当前页面的评论
博客主页 芝麻博客·单人主站 哦哈哟
萌ICP备20220001号 苏ICP备2021051187号-1 本站已运行 3 年 69 天 16 小时 24 分 自豪地使用 Typecho 建站,并搭配 MyDiary 主题 Copyright © 2022 ~ 2026. 芝麻博客·单人主站 All rights reserved.
打赏图
打赏博主
欢迎
欢迎
欢迎访问芝麻博客·单人主站
与其说是博客,不如说是错题集
搜 索
足 迹
分 类
  • 默认分类
  • 前端
  • 后端
  • 模型
  • 游戏
  • 日语
  • 博客