imageUpload.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. <template>
  2. <view class="image-uploader">
  3. <up-upload
  4. :source-type="['camera', 'album']"
  5. :fileList="fileList"
  6. @afterRead="handleAfterRead"
  7. @delete="handleDelete"
  8. :maxCount="maxCount"
  9. :previewFullImage="true"
  10. :multiple="multiple"
  11. :disabled="disabled"
  12. :deletable="!disabled"
  13. >
  14. <view class="upload-btn" v-if="fileList.length < maxCount">
  15. <u-icon name="plus" size="40" color="#c0c4cc"></u-icon>
  16. </view>
  17. </up-upload>
  18. </view>
  19. </template>
  20. <script setup>
  21. import { ref, watch } from "vue";
  22. import { $upload, Toast } from "@/utils";
  23. import { t } from "@/locale";
  24. const props = defineProps({
  25. // 初始图片列表
  26. modelValue: {
  27. type: Array,
  28. default: () => [],
  29. },
  30. // 最大上传数量
  31. maxCount: {
  32. type: Number,
  33. default: 1,
  34. },
  35. // 是否多选
  36. multiple: {
  37. type: Boolean,
  38. default: false,
  39. },
  40. disabled: {
  41. type: Boolean,
  42. default: false,
  43. },
  44. // 上传按钮文字
  45. uploadText: {
  46. type: String,
  47. default: "上传图片",
  48. },
  49. // 上传接口地址
  50. uploadUrl: {
  51. type: String,
  52. default: "",
  53. },
  54. });
  55. const emit = defineEmits(["update:modelValue", "success", "delete", "error"]);
  56. const uToast = ref(null);
  57. const fileList = ref([]);
  58. // 监听父组件传入的值
  59. watch(
  60. () => props.modelValue,
  61. (newVal) => {
  62. fileList.value = newVal.map((item) => {
  63. return typeof item === "string" ? { url: item } : item;
  64. });
  65. },
  66. { immediate: true }
  67. );
  68. // 文件读取完成后处理
  69. const handleAfterRead = async (event) => {
  70. const files = event.file;
  71. try {
  72. const uploadFiles = Array.isArray(files) ? files : [files];
  73. const results = await Promise.all(
  74. uploadFiles.map((file) => {
  75. return $upload(file.url);
  76. })
  77. );
  78. const newFiles = results.map((res) => ({
  79. url: res.data.url,
  80. name: res.data.name,
  81. status: "success",
  82. }));
  83. fileList.value = [...fileList.value, ...newFiles];
  84. emit("update:modelValue", fileList.value);
  85. emit("success", newFiles);
  86. Toast(t("成功"));
  87. } catch (error) {
  88. emit("error", error);
  89. Toast(t("失败"));
  90. }
  91. };
  92. const handleDelete = (event) => {
  93. const { index } = event;
  94. const deletedFile = fileList.value[index];
  95. fileList.value.splice(index, 1);
  96. emit("update:modelValue", fileList.value);
  97. emit("delete", deletedFile);
  98. };
  99. defineExpose({
  100. clearFiles: () => {
  101. fileList.value = [];
  102. emit("update:modelValue", []);
  103. },
  104. });
  105. </script>
  106. <style lang="less" scoped>
  107. .image-uploader {
  108. padding: 10rpx;
  109. .upload-btn {
  110. display: flex;
  111. flex-direction: column;
  112. align-items: center;
  113. justify-content: center;
  114. width: 150rpx;
  115. height: 150rpx;
  116. border: 1px dashed #c0c4cc;
  117. border-radius: 8rpx;
  118. background-color: #f5f7fa;
  119. .upload-text {
  120. margin-top: 10rpx;
  121. font-size: 24rpx;
  122. color: #909399;
  123. }
  124. }
  125. }
  126. </style>