用支付宝原生api写了个拍摄组件供参考,可拍图片和视频,预览图片和视频以及上传。

camera.acss

/* 照片拍摄组件样式 */
.photo-capture-container {
  background: white;
  margin: 20rpx;
  padding: 30rpx;
  border-radius: 16rpx;
  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}

/* 标题区域 */
.photo-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 30rpx;
}

.photo-title {
  font-size: 32rpx;
  font-weight: bold;
  color: #333;
}

.photo-count {
  font-size: 28rpx;
  color: #666;
  margin-left: 10rpx;
}

.sample-btn {
  padding: 10rpx 20rpx;
  background: #f0f0f0;
  border-radius: 20rpx;
  font-size: 24rpx;
  color: #666;
}

.sample-btn:active {
  background: #e0e0e0;
}

/* 照片网格 */
.photo-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 20rpx;
}

.photo-item {
  position: relative;
  width: 200rpx;
  height: 200rpx;
  border-radius: 12rpx;
  overflow: hidden;
  background: #f5f5f5;
}

.photo-image, .photo-video {
  width: 100%;
  height: 100%;
  border-radius: 12rpx;
}

/* 视频播放图标 */
.video-play-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 60rpx;
  height: 60rpx;
  background: rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  backdrop-filter: blur(8rpx);
  border: 4rpx solid rgba(255, 255, 255, 0.8);
}

.play-icon {
  color: white;
  font-size: 24rpx;
  margin-left: 4rpx;
  font-weight: bold;
}

/* 删除按钮 */
.photo-delete {
  position: absolute;
  top: 8rpx;
  right: 8rpx;
  width: 40rpx;
  height: 40rpx;
  background: rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.delete-icon {
  color: white;
  font-size: 28rpx;
  font-weight: bold;
}

/* 只读模式隐藏删除按钮 */
.photo-item.readonly .photo-delete {
  display: none;
}

/* 添加照片按钮 */
.add-photo-btn {
  width: 200rpx;
  height: 200rpx;
  border: 2rpx dashed #ccc;
  border-radius: 12rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #fafafa;
}

.add-photo-btn:active {
  background: #f0f0f0;
}

.add-icon {
  font-size: 60rpx;
  color: #999;
  margin-bottom: 10rpx;
}

.add-text {
  font-size: 24rpx;
  color: #999;
}

/* 提示信息 */
.photo-tips {
  margin-top: 30rpx;
  padding-top: 20rpx;
  border-top: 2rpx solid #f0f0f0;
}

.photo-tips text {
  display: block;
  font-size: 24rpx;
  color: #999;
  line-height: 1.6;
  margin-bottom: 8rpx;
}

.photo-tips text:last-child {
  margin-bottom: 0;
}

/* 照片预览弹窗 */
.photo-viewer {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.9);
}

.viewer-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: transparent;
}

.viewer-content {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center; /* 居中对齐 */
  justify-content: center; /* 垂直居中 */
  background: transparent;
  padding: 80rpx 20rpx 180rpx 20rpx; /* 为顶部关闭按钮和底部控制区域预留空间 */
}

.photo-swiper {
  width: 100%;
  height: 70vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 预览图片样式 */
.viewer-image {
  width: 90%; /* 限制宽度 */
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
  border-radius: 8px;
}

/* 预览视频样式 */
.viewer-video {
  width: 90%; /* 限制宽度 */
  max-width: 100%;
  max-height: 65vh; /* 减少视频高度,为进度条预留更多空间 */
  background: transparent;
  border-radius: 8px;
  object-fit: contain;
}

.viewer-controls {
  position: absolute;
  bottom: 20rpx; /* 减少底部距离,避免过于靠近底部 */
  left: 0;
  right: 0;
  padding: 30rpx 30rpx 40rpx 30rpx; /* 调整内边距,底部稍大一些 */
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
  border-radius: 20rpx 20rpx 0 0; /* 添加顶部圆角 */
}

.photo-counter {
  text-align: center;
  font-size: 28rpx; /* 稍微减小字体 */
  color: white;
  margin-bottom: 20rpx; /* 减少间距 */
  text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.5);
}

.viewer-buttons {
  display: flex;
  gap: 20rpx; /* 减少按钮间距 */
  justify-content: center;
  flex-wrap: wrap; /* 允许换行 */
}

.viewer-btn {
  min-width: 100rpx; /* 减少最小宽度 */
  padding: 20rpx 28rpx; /* 减少内边距 */
  text-align: center;
  font-size: 28rpx; /* 减小字体 */
  font-weight: 500;
  border-radius: 40rpx; /* 减少圆角 */
  background: rgba(255, 255, 255, 0.9);
  color: #333;
  border: 2rpx solid rgba(255, 255, 255, 0.3);
  backdrop-filter: blur(10rpx);
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.3);
}

.viewer-btn:active {
  background: rgba(255, 255, 255, 0.7);
  transform: scale(0.95);
}

.viewer-btn.delete {
  background: rgba(255, 71, 87, 0.9);
  color: white;
  border: 2rpx solid rgba(255, 71, 87, 0.3);
}

.viewer-btn.delete:active {
  background: rgba(255, 55, 66, 0.7);
  transform: scale(0.95);
}

/* 关闭按钮 */
.viewer-close {
  position: absolute;
  top: 60rpx;
  right: 40rpx;
  width: 80rpx;
  height: 80rpx;
  background: rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1001;
  backdrop-filter: blur(10rpx);
  border: 2rpx solid rgba(255, 255, 255, 0.3);
}

.viewer-close:active {
  background: rgba(0, 0, 0, 0.8);
  transform: scale(0.9);
}

.close-icon {
  color: white;
  font-size: 48rpx;
  font-weight: bold;
  line-height: 1;
}

/* 样例图片查看器 */
.sample-viewer {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.9);
}

.sample-viewer .viewer-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: transparent;
}

.sample-viewer-title {
  position: absolute;
  top: 60rpx;
  left: 40rpx;
  z-index: 1001;
}

.sample-viewer-title .viewer-title {
  color: white;
  font-size: 32rpx;
  font-weight: bold;
}

.sample-viewer .viewer-content {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: transparent;
  padding: 80rpx 20rpx 180rpx 20rpx;
}

.sample-swiper {
  width: 100%;
  height: 70vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.sample-viewer .viewer-image {
  width: 90%;
  max-width: 100%;
  max-height: 100%;
  border-radius: 8rpx;
}

.sample-viewer .viewer-controls {
  position: absolute;
  bottom: 40rpx;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20rpx;
}

.sample-viewer .photo-counter {
  color: white;
  font-size: 28rpx;
  background: rgba(0, 0, 0, 0.5);
  padding: 10rpx 20rpx;
  border-radius: 20rpx;
}

.sample-viewer .viewer-buttons {
  display: flex;
  gap: 20rpx;
}

.sample-viewer .viewer-btn {
  padding: 20rpx 40rpx;
  background: rgba(255, 255, 255, 0.2);
  color: white;
  border-radius: 40rpx;
  font-size: 28rpx;
  backdrop-filter: blur(10rpx);
  border: 1rpx solid rgba(255, 255, 255, 0.3);
}

.sample-viewer .viewer-btn:active {
  background: rgba(255, 255, 255, 0.3);
  transform: scale(0.95);
}

camera.axml

<!-- 照片拍摄组件 -->
<view>
  <!-- 标题区域 -->
  <view class="photo-header">
    <text>请拍摄完整清晰照片</text>
    <text a:if="{{mode === 'add' || mode === 'edit'}}" class="photo-count">({{photoList.length}}/{{maxCount}})</text>
    <view class="sample-btn" onTap="onViewSample">
      <text>查看样例</text>
    </view>
  </view>

  <!-- 照片网格 -->
  <view class="photo-grid">
    <!-- 只读模式:显示已提交的图片 -->
    <block a:if="{{mode === 'readonly'}}">
      <view a:for="{{photoList}}" a:key="index" class="photo-item readonly">
        <image
          a:if="{{item.imgType === 'image' || !item.imgType}}"
          src="{{item.url || item.imgUrl || item}}"
          class="photo-image"
          mode="aspectFill"
          onTap="onPreviewPhoto"
          data-index="{{index}}"
        />
        <video
          a:if="{{item.imgType === 'video'}}"
          src="{{item.url || item.imgUrl || item}}"
          class="photo-video"
          poster="{{item.poster || item.thumbUrl}}"
          controls="{{false}}"
          onTap="onPreviewPhoto"
          data-index="{{index}}"
        />
        <view a:if="{{item.imgType === 'video'}}" class="video-play-icon">
          <text class="play-icon">▶</text>
        </view>
      </view>
    </block>
    
    <!-- 编辑模式:可以删除和添加照片 -->
    <block a:else>
      <view a:for="{{photoList}}" a:key="index" class="photo-item editable">
        <image
          a:if="{{item.imgType === 'image' || !item.imgType}}"
          src="{{item.url || item.imgUrl || item}}"
          class="photo-image"
          mode="aspectFill"
          onTap="onPreviewPhoto"
          data-index="{{index}}"
        />
        <video
          a:if="{{item.imgType === 'video'}}"
          src="{{item.url || item.imgUrl || item}}"
          class="photo-video"
          poster="{{item.poster || item.thumbUrl}}"
          controls="{{false}}"
          onTap="onPreviewPhoto"
          data-index="{{index}}"
        />
        <view a:if="{{item.imgType === 'video'}}" class="video-play-icon">
          <text class="play-icon">▶</text>
        </view>
        <view class="photo-delete" onTap="onDeletePhoto" data-index="{{index}}">
          <text class="delete-icon">×</text>
        </view>
      </view>
    </block>

    <!-- 添加照片按钮 - 只在添加/编辑模式显示 -->
    <view a:if="{{(mode === 'add' || mode === 'edit') && photoList.length < maxCount}}" class="add-photo-btn" onTap="onAddPhoto">
      <text class="add-icon">+</text>
      <text class="add-text">拍摄</text>
    </view>
  </view>

  <!-- 提示信息 -->
  <!-- <view class="photo-tips">
    <text a:if="{{mode === 'readonly'}}">• 以上为已提交的照片内容</text>
    <text a:if="{{mode === 'readonly'}}">• 点击照片可查看大图</text>
    
    <text a:if="{{mode === 'add' || mode === 'edit'}}">• 请拍摄清晰的照片或视频,确保广告牌内容可见</text>
    <text a:if="{{mode === 'add' || mode === 'edit'}}">• 建议拍摄角度正面,距离适中</text>
    <text a:if="{{mode === 'add' || mode === 'edit'}}">• 最多可拍摄{{maxCount}}张照片或视频</text>
    <text a:if="{{mode === 'add' || mode === 'edit'}}">• 建议在指定位置100米范围内拍摄</text>
  </view> -->
</view>

<!-- 照片预览弹窗 -->
<view a:if="{{showPhotoViewer}}" class="photo-viewer">
  <view class="viewer-overlay" onTap="onCloseViewer"></view>
  <!-- 关闭按钮 -->
  <view class="viewer-close" onTap="onCloseViewer">
    <text class="close-icon">×</text>
  </view>
  <view class="viewer-content">
    <swiper 
      class="photo-swiper"
      current="{{currentPhotoIndex}}"
      bindchange="onPhotoSwiperChange"
      circular="{{true}}"
      indicator-dots="{{false}}"
    >
      <swiper-item a:for="{{photoList}}" a:key="index">
        <image 
          a:if="{{item.imgType === 'image' || !item.imgType}}"
          src="{{item.url || item.imgUrl || item}}" 
          class="viewer-image" 
          mode="aspectFit" 
          onTap="onCloseViewer" 
        />
        <video
          a:if="{{item.imgType === 'video'}}"
          src="{{item.url || item.imgUrl || item}}"
          class="viewer-video"
          poster="{{item.poster || item.thumbUrl}}"
          controls="{{true}}"
          autoplay="{{false}}"
          loop="{{false}}"
        />
      </swiper-item>
    </swiper>
    <view class="viewer-controls">
      <view class="photo-counter">{{currentPhotoIndex + 1}}/{{photoList.length}}</view>
      <view class="viewer-buttons">
        <view class="viewer-btn" onTap="onPrevPhoto">上一张</view>
        <view class="viewer-btn" onTap="onNextPhoto">下一张</view>
        <view a:if="{{mode === 'add' || mode === 'edit'}}" class="viewer-btn delete" onTap="onDeleteCurrentPhoto">删除</view>
      </view>
    </view>
  </view>
</view>

<!-- 样例图片查看器 -->
<view a:if="{{showSampleViewer}}" class="sample-viewer">
  <view class="viewer-overlay" onTap="onCloseSampleViewer"></view>
  <!-- 关闭按钮 -->
  <view class="viewer-close" onTap="onCloseSampleViewer">
    <text class="close-icon">×</text>
  </view>
  <!-- 标题 -->
  <view class="sample-viewer-title">
    <text class="viewer-title">拍摄样例</text>
  </view>
  <view class="viewer-content">
    <swiper 
      class="sample-swiper"
      current="{{currentSampleIndex}}"
      bindchange="onSampleSwiperChange"
      circular="{{true}}"
      indicator-dots="{{false}}"
    >
      <swiper-item a:for="{{sampleImages}}" a:key="index">
        <image
          src="{{item}}"
          class="viewer-image"
          mode="aspectFit"
          onTap="onCloseSampleViewer"
        />
      </swiper-item>
    </swiper>
    <view class="viewer-controls">
      <view class="photo-counter">{{currentSampleIndex + 1}}/{{sampleImages.length}}</view>
      <view class="viewer-buttons">
        <view class="viewer-btn" onTap="onPrevSampleImage">上一张</view>
        <view class="viewer-btn" onTap="onNextSampleImage">下一张</view>
      </view>
    </view>
  </view>
</view>

camera.js

Component({
  /**
   * 组件属性
   */
  props: {
    // 照片列表
    photoList: {
      type: Array,
      value: []
    },
    // 模式:readonly(只读), add(添加), edit(编辑)
    mode: {
      type: String,
      value: 'add'
    },
    // 标题
    title: {
      type: String,
      value: '拍摄'
    },
    // 最大照片数量
    maxCount: {
      type: Number,
      value: 5
    },
    // 样例图片列表
    sampleImages: {
      type: Array,
      value: []
    }
  },

  /**
   * 组件数据
   */
  data: {
    showPhotoViewer: false, // 是否显示照片预览
    currentPhotoIndex: 0, // 当前预览的照片索引
    currentPhoto: null, // 当前预览的照片
    showSampleViewer: false, // 是否显示样例查看器
    currentSampleIndex: 0 // 当前查看的样例图片索引
  },

  /**
   * 组件方法
   */
  methods: {
    /**
     * 获取文件扩展名
     */
    getFileExtension(filePath) {
      if (!filePath) return '';
      const lastDotIndex = filePath.lastIndexOf('.');
      return lastDotIndex !== -1 ? filePath.substring(lastDotIndex + 1).toLowerCase() : '';
    },
    /**
     * 添加照片
     */
    onAddPhoto() {
      if (this.props.mode === 'readonly') {
        return;
      }

      if (this.props.photoList.length >= this.props.maxCount) {
        my.showToast({
          content: `最多只能拍摄${this.props.maxCount}张照片`,
          type: 'fail'
        });
        return;
      }

      // 显示选择拍摄类型的弹窗
      my.showActionSheet({
        title: '选择拍摄类型',
        items: ['拍摄照片', '拍摄视频'],
        success: (res) => {
          if (res.index === 0) {
            // 拍摄照片
            this.capturePhoto();
          } else if (res.index === 1) {
            // 拍摄视频
            this.captureVideo();
          }
        }
      });
    },

    /**
     * 拍摄照片
     */
    capturePhoto() {
      my.chooseImage({
        count: 1,
        sourceType: ['camera'], // 只允许拍照
        success: (res) => {
          const tempFilePath = res.tempFilePaths[0];

          // 上传文件
          this.uploadFile(tempFilePath, 'image')
            .then((uploadResult) => {
              console.log('照片上传结果:', uploadResult);
              console.log('当前照片列表:', this.props.photoList);

              // 触发onChange回调,传递上传后的结果
              const newPhotoList = [...this.props.photoList, uploadResult];
              if (this.props.onChange) {
                this.props.onChange({
                  photoList: newPhotoList,
                  action: 'add',
                  photo: uploadResult,
                  mediaType: 'image'
                });
              }

              my.showToast({
                content: '照片上传成功',
                type: 'success'
              });
            })
            .catch((error) => {
              console.error('照片上传失败:', error);
              my.showToast({
                content: error.message || '照片上传失败',
                type: 'fail'
              });
            });
        },
        fail: (error) => {
          console.error('拍照失败:', error);
          my.showToast({
            content: '拍照失败,请重试',
            type: 'fail'
          });
        }
      });
    },

    /**
     * 拍摄视频
     */
    captureVideo() {
      my.chooseVideo({
        sourceType: ['album', 'camera'], // 只允许拍摄
        maxDuration: 60, // 最长60秒
        success: (res) => {
          console.log('拍摄视频信息:', res);
          const fs = my.getFileSystemManager();
          const oldPath = res.tempFilePath;
          const newPath = `${my.env.USER_DATA_PATH}/upload_${Date.now()}.mp4`;

          // 把临时文件.video文件拷贝成一个 .mp4 文件
          fs.copyFile({
            srcPath: oldPath,
            destPath: newPath,
            success: () => {
              // 上传文件
              this.uploadFile(newPath, 'video')
                .then((uploadResult) => {
                  my.hideLoading();

                  // 触发onChange回调,传递上传后的结果
                  const newPhotoList = [...this.props.photoList, uploadResult];
                  if (this.props.onChange) {
                    this.props.onChange({
                      photoList: newPhotoList,
                      action: 'add',
                      photo: uploadResult,
                      mediaType: 'video'
                    });
                  }

                  my.showToast({
                    content: '视频上传成功',
                    type: 'success'
                  });
                })
                .catch((error) => {
                  my.hideLoading();
                  console.error('视频上传失败:', error);
                  my.showToast({
                    content: error.message || '视频上传失败',
                    type: 'fail'
                  });
                });
            },
            fail: (err) => {
              console.error('拷贝文件失败', err);
            }
          });
          
        },
        fail: (error) => {
          console.error('拍摄视频失败:', error);
          my.showToast({
            content: '视频拍摄失败,请重试',
            type: 'fail'
          });
        }
      });
    },

    /**
     * 删除照片
     */
    onDeletePhoto(e) {
      if (this.props.mode === 'readonly') {
        return;
      }

      const index = parseInt(e.target.dataset.index);
      const photoToDelete = this.props.photoList[index];

      my.confirm({
        title: '确认删除',
        content: '确定要删除这张照片吗?',
        success: (result) => {
          if (result.confirm) {
            const newPhotoList = this.props.photoList.filter((_, i) => i !== index);

            // 触发onChange回调
            if (this.props.onChange) {
              this.props.onChange({
                photoList: newPhotoList,
                action: 'delete',
                photo: photoToDelete,
                index: index
              });
            }
          }
        }
      });
    },

    /**
     * 预览照片
     */
    onPreviewPhoto(e) {
      const index = parseInt(e.target.dataset.index);
      const photo = this.props.photoList[index];

      this.setData({
        showPhotoViewer: true,
        currentPhotoIndex: index,
        currentPhoto: photo
      });
    },

    /**
     * 关闭预览
     */
    onCloseViewer() {
      this.setData({
        showPhotoViewer: false,
        currentPhotoIndex: 0,
        currentPhoto: null
      });
    },

    /**
     * 照片滑动事件
     */
    onPhotoSwiperChange(e) {
      const { current } = e.detail;
      this.setData({
        currentPhotoIndex: current,
        currentPhoto: this.props.photoList[current]
      });
    },

    /**
     * 上一张照片
     */
    onPrevPhoto() {
      const newIndex = this.data.currentPhotoIndex > 0
        ? this.data.currentPhotoIndex - 1
        : this.props.photoList.length - 1;

      this.setData({
        currentPhotoIndex: newIndex,
        currentPhoto: this.props.photoList[newIndex]
      });
    },

    /**
     * 下一张照片
     */
    onNextPhoto() {
      const newIndex = this.data.currentPhotoIndex < this.props.photoList.length - 1
        ? this.data.currentPhotoIndex + 1
        : 0;

      this.setData({
        currentPhotoIndex: newIndex,
        currentPhoto: this.props.photoList[newIndex]
      });
    },

    /**
     * 删除当前预览的照片
     */
    onDeleteCurrentPhoto() {
      if (this.props.mode === 'readonly') {
        return;
      }

      const index = this.data.currentPhotoIndex;
      const photoToDelete = this.props.photoList[index];

      my.confirm({
        title: '确认删除',
        content: '确定要删除这张照片吗?',
        success: (result) => {
          if (result.confirm) {
            const newPhotoList = this.props.photoList.filter((_, i) => i !== index);

            // 关闭预览
            this.onCloseViewer();

            // 触发onChange回调
            this.props.onChange({
              photoList: newPhotoList,
              action: 'delete',
              photo: photoToDelete,
              index: index
            })
          }
        }
      });
    },

    /**
     * 查看样例
     */
    onViewSample() {
      // if (this.props.mode === 'readonly') {
      //   return;
      // }

      // 触发查看样例事件
      this.props.onChange({
        sampleImages: this.props.sampleImages
      });

    },

    /**
     * 上传文件
     */
    uploadFile(filePath, fileType) {
      return new Promise((resolve, reject) => {
        // 获取token
        let token = '';
        const tokenData = my.getStorageSync({ key: 'token' });
        token = tokenData.data || '';

        // 尝试从全局获取BASE_URL
        let baseUrl = '';
        try {
          const app = getApp();
          if (app && app.globalData && app.globalData.BASE_URL) {
            baseUrl = app.globalData.BASE_URL;
          } else {
            // 可以尝试从本地存储或配置文件获取
            const configData = my.getStorageSync({ key: 'config' });
            if (configData.data && configData.data.baseUrl) {
              baseUrl = configData.data.baseUrl;
            }
          }
        } catch (e) {
          console.error('获取全局BASE_URL失败:', e);
        }

        if (!baseUrl) {
          console.error('BASE_URL为空,无法上传');
          reject(new Error('服务器地址配置错误'));
          return;
        }

        const uploadUrl = `${baseUrl}/common/upload`;
        console.log('上传URL:', uploadUrl);

        // 设置超时定时器
        const timeoutId = setTimeout(() => {
          console.error('上传超时');
          reject(new Error('上传超时,请检查网络连接'));
        }, 30000); // 30秒超时

        my.uploadFile({
          url: uploadUrl,
          filePath: filePath, // 使用修正后的文件路径
          name: 'file',
          fileType: fileType,
          header: {
            'Authorization': token ? `Bearer ${token}` : '',
            // 'Content-Type': 'multipart/form-data'
          },
          success: (res) => {
            clearTimeout(timeoutId);
            console.log('文件上传成功,原始响应:', res);

            if (!res.data) {
              console.error('上传响应数据为空');
              reject(new Error('服务器响应异常'));
              return;
            }

            try {
              const responseData = JSON.parse(res.data);
              console.log('解析后的响应数据:', responseData);

              if (responseData.code === 200 && responseData.data) {
                // 返回上传结果,包含文件URL和类型信息
                const uploadResult = {
                  imgType: fileType === 'video' ? 'video' : 'image',
                  imgUrl: responseData.data, // 后端返回的URL字符串
                  remark: null,
                  filePath: filePath // 保留本地文件路径
                };

                console.log('构造的上传结果:', uploadResult);
                resolve(uploadResult);
              } else {
                console.error('上传失败,服务器返回:', responseData);
                reject(new Error(responseData.message || responseData.msg || '上传失败'));
              }
            } catch (parseError) {
              console.error('解析上传响应失败:', parseError, '原始数据:', res.data);
              reject(new Error('解析服务器响应失败'));
            }
          },
          fail: (error) => {
            clearTimeout(timeoutId);
            console.error('文件上传失败:', error);

            let errorMessage = '文件上传失败';
            if (error.error) {
              errorMessage += ': ' + error.error;
            } else if (error.errorMessage) {
              errorMessage += ': ' + error.errorMessage;
            }

            reject(new Error(errorMessage));
          }
        });
      });
    },

    /**
     * 查看样例图片
     */
    onViewSample() {
      if (!this.props.sampleImages || this.props.sampleImages.length === 0) {
        my.showToast({
          content: '暂无样例图片',
          type: 'fail'
        });
        return;
      }
      
      this.setData({
        showSampleViewer: true,
        currentSampleIndex: 0
      });
    },

    /**
     * 关闭样例查看器
     */
    onCloseSampleViewer() {
      this.setData({
        showSampleViewer: false
      });
    },

    /**
     * 样例图片滑动事件
     */
    onSampleSwiperChange(e) {
      const { current } = e.detail;
      this.setData({
        currentSampleIndex: current
      });
    },

    /**
     * 上一张样例图片
     */
    onPrevSampleImage() {
      const { currentSampleIndex } = this.data;
      const sampleImages = this.props.sampleImages || [];
      const newIndex = currentSampleIndex > 0 ? currentSampleIndex - 1 : sampleImages.length - 1;
      this.setData({
        currentSampleIndex: newIndex
      });
    },

    /**
     * 下一张样例图片
     */
    onNextSampleImage() {
      const { currentSampleIndex } = this.data;
      const sampleImages = this.props.sampleImages || [];
      const newIndex = currentSampleIndex < sampleImages.length - 1 ? currentSampleIndex + 1 : 0;
      this.setData({
        currentSampleIndex: newIndex
      });
    }
  },
  didMount() {
    console.log(11, this.props.photoList)
  }
});

注意:组件是AI写的,可能有些bug。

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐