From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/components/UploadFile/src/UploadImgs.vue |  329 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 329 insertions(+), 0 deletions(-)

diff --git a/src/components/UploadFile/src/UploadImgs.vue b/src/components/UploadFile/src/UploadImgs.vue
new file mode 100644
index 0000000..d1386e4
--- /dev/null
+++ b/src/components/UploadFile/src/UploadImgs.vue
@@ -0,0 +1,329 @@
+<template>
+  <div class="upload-box">
+    <el-upload
+      v-model:file-list="fileList"
+      :accept="fileType.join(',')"
+      :action="uploadUrl"
+      :before-upload="beforeUpload"
+      :class="['upload', drag ? 'no-border' : '']"
+      :disabled="disabled"
+      :drag="drag"
+      :http-request="httpRequest"
+      :limit="limit"
+      :multiple="true"
+      :on-error="uploadError"
+      :on-exceed="handleExceed"
+      :on-success="uploadSuccess"
+      list-type="picture-card"
+    >
+      <div class="upload-empty">
+        <slot name="empty">
+          <Icon icon="ep:plus" />
+          <!-- <span>璇蜂笂浼犲浘鐗�</span> -->
+        </slot>
+      </div>
+      <template #file="{ file }">
+        <img :src="file.url" class="upload-image" />
+        <div class="upload-handle" @click.stop>
+          <div class="handle-icon" @click="imagePreview(file.url!)">
+            <Icon icon="ep:zoom-in" />
+            <span>鏌ョ湅</span>
+          </div>
+          <div v-if="!disabled" class="handle-icon" @click="handleRemove(file)">
+            <Icon icon="ep:delete" />
+            <span>鍒犻櫎</span>
+          </div>
+        </div>
+      </template>
+    </el-upload>
+    <div class="el-upload__tip">
+      <slot name="tip"></slot>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+import type { UploadFile, UploadProps, UploadUserFile } from 'element-plus'
+import { ElNotification } from 'element-plus'
+import { createImageViewer } from '@/components/ImageViewer'
+
+import { propTypes } from '@/utils/propTypes'
+import { useUpload } from '@/components/UploadFile/src/useUpload'
+
+defineOptions({ name: 'UploadImgs' })
+
+const message = useMessage() // 娑堟伅寮圭獥
+// 鏌ョ湅鍥剧墖
+const imagePreview = (imgUrl: string) => {
+  createImageViewer({
+    zIndex: 9999999,
+    urlList: [imgUrl]
+  })
+}
+
+type FileTypes =
+  | 'image/apng'
+  | 'image/bmp'
+  | 'image/gif'
+  | 'image/jpeg'
+  | 'image/pjpeg'
+  | 'image/png'
+  | 'image/svg+xml'
+  | 'image/tiff'
+  | 'image/webp'
+  | 'image/x-icon'
+
+const props = defineProps({
+  modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
+  drag: propTypes.bool.def(true), // 鏄惁鏀寔鎷栨嫿涓婁紶 ==> 闈炲繀浼狅紙榛樿涓� true锛�
+  disabled: propTypes.bool.def(false), // 鏄惁绂佺敤涓婁紶缁勪欢 ==> 闈炲繀浼狅紙榛樿涓� false锛�
+  limit: propTypes.number.def(5), // 鏈�澶у浘鐗囦笂浼犳暟 ==> 闈炲繀浼狅紙榛樿涓� 5寮狅級
+  fileSize: propTypes.number.def(5), // 鍥剧墖澶у皬闄愬埗 ==> 闈炲繀浼狅紙榛樿涓� 5M锛�
+  fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 鍥剧墖绫诲瀷闄愬埗 ==> 闈炲繀浼狅紙榛樿涓� ["image/jpeg", "image/png", "image/gif"]锛�
+  height: propTypes.string.def('150px'), // 缁勪欢楂樺害 ==> 闈炲繀浼狅紙榛樿涓� 150px锛�
+  width: propTypes.string.def('150px'), // 缁勪欢瀹藉害 ==> 闈炲繀浼狅紙榛樿涓� 150px锛�
+  borderradius: propTypes.string.def('8px'), // 缁勪欢杈规鍦嗚 ==> 闈炲繀浼狅紙榛樿涓� 8px锛�
+  directory: propTypes.string.def(undefined) // 涓婁紶鐩綍 ==> 闈炲繀浼狅紙榛樿涓� undefined锛�
+})
+
+const { uploadUrl, httpRequest } = useUpload(props.directory)
+
+const fileList = ref<UploadUserFile[]>([])
+const uploadNumber = ref<number>(0)
+const uploadList = ref<UploadUserFile[]>([])
+/**
+ * @description 鏂囦欢涓婁紶涔嬪墠鍒ゆ柇
+ * @param rawFile 涓婁紶鐨勬枃浠�
+ * */
+const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
+  const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
+  const imgType = props.fileType
+  const isValidType = imgType.includes(rawFile.type as FileTypes)
+  const isValidSize = imgSize
+
+  if (!isValidType)
+    ElNotification({
+      title: '娓╅Θ鎻愮ず',
+      message: '涓婁紶鍥剧墖涓嶇鍚堟墍闇�鐨勬牸寮忥紒',
+      type: 'warning'
+    })
+  if (!isValidSize)
+    ElNotification({
+      title: '娓╅Θ鎻愮ず',
+      message: `涓婁紶鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize}M锛乣,
+      type: 'warning'
+    })
+
+  // 鍙湁鍦ㄩ獙璇侀�氳繃鍚庢墠澧炲姞璁℃暟鍣�
+  if (isValidType && isValidSize) {
+    uploadNumber.value++
+  }
+
+  return isValidType && isValidSize
+}
+
+// 鍥剧墖涓婁紶鎴愬姛
+interface UploadEmits {
+  (e: 'update:modelValue', value: string[]): void
+}
+
+const emit = defineEmits<UploadEmits>()
+const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
+  message.success('涓婁紶鎴愬姛')
+  // 鍒犻櫎鑷韩
+  const index = fileList.value.findIndex((item) => item.response?.data === res.data)
+  fileList.value.splice(index, 1)
+  uploadList.value.push({ name: res.data, url: res.data })
+  if (uploadList.value.length == uploadNumber.value) {
+    fileList.value.push(...uploadList.value)
+    uploadList.value = []
+    uploadNumber.value = 0
+    emitUpdateModelValue()
+  }
+}
+
+// 鐩戝惉妯″瀷缁戝畾鍊煎彉鍔�
+watch(
+  () => props.modelValue,
+  (val: string | string[]) => {
+    if (!val) {
+      fileList.value = [] // fix锛氬鐞嗘帀缂撳瓨锛岃〃鍗曢噸缃悗涓婁紶缁勪欢鐨勫唴瀹瑰苟娌℃湁閲嶇疆
+      return
+    }
+
+    fileList.value = [] // 淇濋殰鏁版嵁涓虹┖
+    fileList.value.push(
+      ...(val as string[]).map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
+    )
+  },
+  { immediate: true, deep: true }
+)
+// 鍙戦�佸浘鐗囬摼鎺ュ垪琛ㄦ洿鏂�
+const emitUpdateModelValue = () => {
+  let result: string[] = fileList.value.map((file) => file.url!)
+  emit('update:modelValue', result)
+}
+// 鍒犻櫎鍥剧墖
+const handleRemove = (uploadFile: UploadFile) => {
+  fileList.value = fileList.value.filter(
+    (item) => item.url !== uploadFile.url || item.name !== uploadFile.name
+  )
+  emit(
+    'update:modelValue',
+    fileList.value.map((file) => file.url!)
+  )
+}
+
+// 鍥剧墖涓婁紶閿欒鎻愮ず
+const uploadError = () => {
+  ElNotification({
+    title: '娓╅Θ鎻愮ず',
+    message: '鍥剧墖涓婁紶澶辫触锛岃鎮ㄩ噸鏂颁笂浼狅紒',
+    type: 'error'
+  })
+  // 涓婁紶澶辫触鏃跺噺灏戣鏁板櫒锛岄伩鍏嶅悗缁笂浼犺闃诲
+  uploadNumber.value = Math.max(0, uploadNumber.value - 1)
+}
+
+// 鏂囦欢鏁拌秴鍑烘彁绀�
+const handleExceed = () => {
+  ElNotification({
+    title: '娓╅Θ鎻愮ず',
+    message: `褰撳墠鏈�澶氬彧鑳戒笂浼� ${props.limit} 寮犲浘鐗囷紝璇风Щ闄ゅ悗涓婁紶锛乣,
+    type: 'warning'
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+.is-error {
+  .upload {
+    :deep(.el-upload--picture-card),
+    :deep(.el-upload-dragger) {
+      border: 1px dashed var(--el-color-danger) !important;
+
+      &:hover {
+        border-color: var(--el-color-primary) !important;
+      }
+    }
+  }
+}
+
+:deep(.disabled) {
+  .el-upload--picture-card,
+  .el-upload-dragger {
+    cursor: not-allowed;
+    background: var(--el-disabled-bg-color) !important;
+    border: 1px dashed var(--el-border-color-darker);
+
+    &:hover {
+      border-color: var(--el-border-color-darker) !important;
+    }
+  }
+}
+
+.upload-box {
+  .no-border {
+    :deep(.el-upload--picture-card) {
+      border: none !important;
+    }
+  }
+
+  :deep(.upload) {
+    .el-upload-dragger {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 100%;
+      height: 100%;
+      padding: 0;
+      overflow: hidden;
+      border: 1px dashed var(--el-border-color-darker);
+      border-radius: v-bind(borderradius);
+
+      &:hover {
+        border: 1px dashed var(--el-color-primary);
+      }
+    }
+
+    .el-upload-dragger.is-dragover {
+      background-color: var(--el-color-primary-light-9);
+      border: 2px dashed var(--el-color-primary) !important;
+    }
+
+    .el-upload-list__item,
+    .el-upload--picture-card {
+      width: v-bind(width);
+      height: v-bind(height);
+      background-color: transparent;
+      border-radius: v-bind(borderradius);
+    }
+
+    .upload-image {
+      width: 100%;
+      height: 100%;
+      object-fit: contain;
+    }
+
+    .upload-handle {
+      position: absolute;
+      top: 0;
+      right: 0;
+      display: flex;
+      width: 100%;
+      height: 100%;
+      cursor: pointer;
+      background: rgb(0 0 0 / 60%);
+      opacity: 0;
+      box-sizing: border-box;
+      transition: var(--el-transition-duration-fast);
+      align-items: center;
+      justify-content: center;
+
+      .handle-icon {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        padding: 0 6%;
+        color: aliceblue;
+
+        .el-icon {
+          margin-bottom: 15%;
+          font-size: 140%;
+        }
+
+        span {
+          font-size: 100%;
+        }
+      }
+    }
+
+    .el-upload-list__item {
+      &:hover {
+        .upload-handle {
+          opacity: 1;
+        }
+      }
+    }
+
+    .upload-empty {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      font-size: 12px;
+      line-height: 30px;
+      color: var(--el-color-info);
+
+      .el-icon {
+        font-size: 28px;
+        color: var(--el-text-color-secondary);
+      }
+    }
+  }
+
+  .el-upload__tip {
+    line-height: 15px;
+    text-align: center;
+  }
+}
+</style>

--
Gitblit v1.8.0