uniapp上传图片、文件、视频,可二次更改

<script setup> import { ref } from 'vue'; import { onLoad } from '@dcloudio/uni-app'; import request from '@/utils/request'; import { getFiles, getRan...
uniapp上传图片、文件、视频,可二次更改
uniapp上传图片、文件、视频,可二次更改
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import request from '@/utils/request';
import { getFiles, getRandomFile, getFileArray } from '@/utils/yangUtils';
const imageSuffix = ['png', 'jpg', 'jpeg'];
const videoSuffix = ['mp4', 'avi', 'mov'];
const imageNum = ref(0);
const videoNum = ref(0);

// 获取父级传入的数据
const props = defineProps(["modelValue", "limit", "message", "isDelete", "isAdd"]);

// 定义 emits
const emit = defineEmits(['update:modelValue']);

// const listData = ref(["https://yxdjpw.oss-cn-beijing.aliyuncs.com/uploadDefault/20260417192542-d3ea46.png", "https://yxdjpw.oss-cn-beijing.aliyuncs.com/uploadDefault/20260417194449-bfb1ba.mp4"]);
const listData = ref([])

const onSelectFile = async () => {
  let result;
  // #ifdef APP || APP-PLUS || MP-WEIXIN || MP-TOUTIAO || MP-LARK || MP-JD || MP-HARMONY || MP-XHS
  result = await uni.chooseMedia({
    count: props.limit || 9,
    mediaType: ['image', 'video'],
    sourceType: ['album', 'camera'],
    maxDuration: '30s'
  })
  // #endif
  // #ifdef WEB || H5
  result = await uni.chooseFile({
    count: props.limit || 9,
    type: 'all',
  })
  // #endif
  let arr = [];

  console.log(result);

  for (let filePath of result.tempFiles) {
    // 判断文件是否超出10M
    if (filePath.size > 10 * 1024 * 1024) {
      uni.showModal({
        title: '提示',
        content: '文件大小不能超过10M!',
        showCancel: true,
      })
      return;
    }

    if (imageSuffix.includes(filePath.name.replaceAll('"', '').split('.').pop()?.toLowerCase())) {
      imageNum.value++;
    } else if (videoSuffix.includes(filePath.name.replaceAll('"', '').split('.').pop()?.toLowerCase())) {
      // 判断之前有没有上传过视频或者图片
      if (imageNum.value >= 1 || videoNum.value >= 1) {
        uni.showModal({
          title: '提示',
          content: '图片和视频不能同时上传,并且视频只能上传一个!',
          showCancel: true,
        })
        return;
      }

      videoNum.value++;
    }
    const data = await request("/upload/uploadFile", filePath.path, "post");
    arr.push(getFileArray(data)[0]);
  }
  listData.value = listData.value.concat(arr);
  // 将数据返回出去
  emit("update:modelValue", listData.value.join(","));
}

// 删除
const onDelete = (index) => {
  // 1. 先拿到要删除的项(必须在 splice 之前拿!)
  const deletedItem = listData.value[index];
  // 2. 判断类型,更新计数
  const ext = deletedItem.split('.').pop()?.toLowerCase();
  if (imageSuffix.includes(ext)) {
    imageNum.value--;
  } else if (videoSuffix.includes(ext)) {
    videoNum.value--;
  }
  // 3. 再删除元素
  listData.value.splice(index, 1);
  // 4. 更新双向绑定
  emit("update:modelValue", listData.value.join(","));
}

// 查看
const onView = (item) => {

}

// 类型判断
const isSuffix = () => {
  return listData.value.some(item => {
    // 获取后缀(转小写,避免大小写问题)
    const ext = item.split('.').pop()?.toLowerCase()
    return videoSuffix.includes(ext)
  })
}
</script>

<template>
  <view>
    <view class="header">
      <view v-if="props.message" class="message">{{ props.message }}</view>
      <view class="numCount">{{ `${listData.length}/${props.limit || 9}` }}</view>
    </view>
    <view class="image-grid" :class="isSuffix() ? 'grid-video' : ''">
      <template v-for="(item, index) in listData" :key="index">
        <view class="grid-item grid-item-content" :class="isSuffix() ? 'grid-item-video' : ''" @click="onView(item)">
          <image v-if="imageSuffix.includes(item.split('.').pop())" class="img" :src="item" mode="aspectFill" />
          <video v-else-if="videoSuffix.includes(item.split('.').pop())" class="video" :src="item" />
          <view class="grid-item-delete" v-if="(isAdd ?? true)" @click="onDelete(index)"><uni-icons class="icon-delete"
              type="closeempty" color="#D3D4D6" size="24" /></view>
        </view>
      </template>
      <view class="grid-item icon-add" v-if="(isAdd ?? true) && !isSuffix() && listData.length < (props.limit ?? 9)"
        @click="onSelectFile()">
        <uni-icons type="plusempty" size="60" color="#F1F1F1"></uni-icons>
      </view>
    </view>
  </view>
</template>

<style lang="scss" scoped>
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20rpx;
}

.image-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* 一行3个,自动均分 */
  gap: 20rpx;
  /* 格子间距 */
  box-sizing: border-box;
}

.grid-item {
  width: 240rpx;
  height: 240rpx;
}

.grid-video {
  grid-template-columns: repeat(1, 1fr) !important;
}

.grid-item-video {
  width: 100%;
  height: 400rpx;
}

.numCount {
  display: flex;
  flex-direction: row-reverse;
  font-size: 26rpx;
}

.img,
.video {
  width: 100%;
  height: 100%;
}

.grid-item-content {
  position: relative;
}

.icon-add {
  background: #FFFFFF;
  line-height: 240rpx;
  text-align: center;
  border: 1rpx solid #EEEEEE;
  border-radius: 6rpx;
}

.grid-item-delete {
  position: absolute;
  top: 0rpx;
  right: 0rpx;
  z-index: 1;
}
</style>

方法解析

文件后端返回的是
/api/upload/xxx.png

  1. getFiles 将图片拼接成正常能访问的 比如http://域名.com/api/upload/xxx.png
  2. getRandomFile 是随机访问
  3. getFileArray 是拼接成数组
  4. limit → 最多上传数量 默认为9
  5. message → 提示 比如请上传视频 可不填
  6. isDelete → 是否可以删除重新上传 默认为true
  7. isAdd → 是否可以新增,比如有些地方可以直接回显 比如产品的图片,但是你不想让它显示新增的框 可以为false 默认为true

// 文件加载
export const getFiles = (url) => {
  if (!url) {
    return "";
  }
  if (url.startsWith("http://") || url.startsWith("https://")) {
    return cleanString(url);
  } else {
    return baseURL + cleanString(url);
  }
};

// 随机加载文件
export const getRandomFile = (url) => {
  if (!url) {
    return "";
  }
  const urls = url.split(",");
  const randomIndex = Math.floor(Math.random() * urls.length);
  const selectedUrl = urls[randomIndex];

  if (
    cleanString(selectedUrl).startsWith("http://") ||
    cleanString(selectedUrl).startsWith("https://")
  ) {
    return cleanString(selectedUrl);
  } else {
    return baseURL + cleanString(selectedUrl);
  }
};

export const getFileArray = (url) => {
  if (!url) {
    return [];
  }
  let baseUrl = [];
  url.split(",").forEach((v) => {
    if (
      cleanString(v).startsWith("http://") ||
      cleanString(v).startsWith("https://")
    ) {
      baseUrl.push(cleanString(v));
    } else {
      baseUrl.push(baseURL + cleanString(v));
    }
  });
  console.log(baseUrl);
  return baseUrl;
};

请求就是 uni.request的请求 你们按照你们自己的改

当前只支持H5 你们需要的话 我会再更新

如果上传了视频就直接独占一行 并且不能上传其他视频和图片 仿照的是朋友圈

其他页面调用就是 比如文件名叫uploadFile.vue吧

import UpLoadFile from '@/components/uploadFile.vue'

// 页面代码
<UpLoadFile v-model="form.media"  />

第一次封装 还有些问题 我会持续更新的

1 个帖子 - 1 位参与者

阅读完整话题

来源: linux.do查看原文