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/UploadImg.vue |  272 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 272 insertions(+), 0 deletions(-)

diff --git a/src/components/UploadFile/src/UploadImg.vue b/src/components/UploadFile/src/UploadImg.vue
new file mode 100644
index 0000000..66e9d0c
--- /dev/null
+++ b/src/components/UploadFile/src/UploadImg.vue
@@ -0,0 +1,272 @@
+<template>
+  <div class="upload-box">
+    <el-upload
+      :id="uuid"
+      :accept="fileType.join(',')"
+      :action="uploadUrl"
+      :before-upload="beforeUpload"
+      :class="['upload', drag ? 'no-border' : '']"
+      :disabled="disabled"
+      :drag="drag"
+      :http-request="httpRequest"
+      :multiple="false"
+      :on-error="uploadError"
+      :on-success="uploadSuccess"
+      :show-file-list="false"
+    >
+      <template v-if="modelValue">
+        <img :src="modelValue" class="upload-image" />
+        <div class="upload-handle" @click.stop>
+          <div v-if="!disabled" class="handle-icon" @click="editImg">
+            <Icon icon="ep:edit" />
+            <span v-if="showBtnText">{{ t('action.edit') }}</span>
+          </div>
+          <div class="handle-icon" @click="imagePreview(modelValue)">
+            <Icon icon="ep:zoom-in" />
+            <span v-if="showBtnText">{{ t('action.detail') }}</span>
+          </div>
+          <div v-if="showDelete && !disabled" class="handle-icon" @click="deleteImg">
+            <Icon icon="ep:delete" />
+            <span v-if="showBtnText">{{ t('action.del') }}</span>
+          </div>
+        </div>
+      </template>
+      <template v-else>
+        <div class="upload-empty">
+          <slot name="empty">
+            <Icon icon="ep:plus" />
+            <!-- <span>璇蜂笂浼犲浘鐗�</span> -->
+          </slot>
+        </div>
+      </template>
+    </el-upload>
+    <div class="el-upload__tip">
+      <slot name="tip"></slot>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import type { UploadProps } from 'element-plus'
+
+import { generateUUID } from '@/utils'
+import { propTypes } from '@/utils/propTypes'
+import { createImageViewer } from '@/components/ImageViewer'
+import { useUpload } from '@/components/UploadFile/src/useUpload'
+
+defineOptions({ name: 'UploadImg' })
+
+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.string.def(''),
+  drag: propTypes.bool.def(true), // 鏄惁鏀寔鎷栨嫿涓婁紶 ==> 闈炲繀浼狅紙榛樿涓� true锛�
+  disabled: propTypes.bool.def(false), // 鏄惁绂佺敤涓婁紶缁勪欢 ==> 闈炲繀浼狅紙榛樿涓� false锛�
+  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锛�
+  showDelete: propTypes.bool.def(true), // 鏄惁鏄剧ず鍒犻櫎鎸夐挳
+  showBtnText: propTypes.bool.def(true), // 鏄惁鏄剧ず鎸夐挳鏂囧瓧
+  directory: propTypes.string.def(undefined) // 涓婁紶鐩綍 ==> 闈炲繀浼狅紙榛樿涓� undefined锛�
+})
+const { t } = useI18n() // 鍥介檯鍖�
+const message = useMessage() // 娑堟伅寮圭獥
+// 鐢熸垚缁勪欢鍞竴id
+const uuid = ref('id-' + generateUUID())
+// 鏌ョ湅鍥剧墖
+const imagePreview = (imgUrl: string) => {
+  createImageViewer({
+    zIndex: 9999999,
+    urlList: [imgUrl]
+  })
+}
+
+const emit = defineEmits(['update:modelValue'])
+
+const deleteImg = () => {
+  emit('update:modelValue', '')
+}
+
+const { uploadUrl, httpRequest } = useUpload(props.directory)
+
+const editImg = () => {
+  const dom = document.querySelector(`#${uuid.value} .el-upload__input`)
+  dom && dom.dispatchEvent(new MouseEvent('click'))
+}
+
+const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
+  const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
+  const imgType = props.fileType
+  if (!imgType.includes(rawFile.type as FileTypes))
+    message.notifyWarning('涓婁紶鍥剧墖涓嶇鍚堟墍闇�鐨勬牸寮忥紒')
+  if (!imgSize) message.notifyWarning(`涓婁紶鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize}M锛乣)
+  return imgType.includes(rawFile.type as FileTypes) && imgSize
+}
+
+// 鍥剧墖涓婁紶鎴愬姛鎻愮ず
+const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
+  message.success('涓婁紶鎴愬姛')
+  emit('update:modelValue', res.data)
+}
+
+// 鍥剧墖涓婁紶閿欒鎻愮ず
+const uploadError = () => {
+  message.notifyError('鍥剧墖涓婁紶澶辫触锛岃鎮ㄩ噸鏂颁笂浼狅紒')
+}
+</script>
+<style lang="scss" scoped>
+.is-error {
+  .upload {
+    :deep(.el-upload),
+    :deep(.el-upload-dragger) {
+      border: 1px dashed var(--el-color-danger) !important;
+
+      &:hover {
+        border-color: var(--el-color-primary) !important;
+      }
+    }
+  }
+}
+
+:deep(.disabled) {
+  .el-upload,
+  .el-upload-dragger {
+    cursor: not-allowed !important;
+    background: var(--el-disabled-bg-color);
+    border: 1px dashed var(--el-border-color-darker) !important;
+
+    &:hover {
+      border: 1px dashed var(--el-border-color-darker) !important;
+    }
+  }
+}
+
+.upload-box {
+  .no-border {
+    :deep(.el-upload) {
+      border: none !important;
+    }
+  }
+
+  :deep(.upload) {
+    .el-upload {
+      position: relative;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: v-bind(width);
+      height: v-bind(height);
+      overflow: hidden;
+      border: 1px dashed var(--el-border-color-darker);
+      border-radius: v-bind(borderradius);
+      transition: var(--el-transition-duration-fast);
+
+      &:hover {
+        border-color: var(--el-color-primary);
+
+        .upload-handle {
+          opacity: 1;
+        }
+      }
+
+      .el-upload-dragger {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 100%;
+        height: 100%;
+        padding: 0;
+        overflow: hidden;
+        background-color: transparent;
+        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;
+      }
+
+      .upload-image {
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+      }
+
+      .upload-empty {
+        position: relative;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        font-size: 12px;
+        line-height: 30px;
+        color: var(--el-color-info);
+
+        .el-icon {
+          font-size: 28px;
+          color: var(--el-text-color-secondary);
+        }
+      }
+
+      .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: 40%;
+            font-size: 130%;
+            line-height: 130%;
+          }
+
+          span {
+            font-size: 85%;
+            line-height: 85%;
+          }
+        }
+      }
+    }
+  }
+
+  .el-upload__tip {
+    line-height: 18px;
+    text-align: center;
+  }
+}
+</style>

--
Gitblit v1.8.0