From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/views/ai/knowledge/document/form/UploadStep.vue | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 273 insertions(+), 0 deletions(-)
diff --git a/src/views/ai/knowledge/document/form/UploadStep.vue b/src/views/ai/knowledge/document/form/UploadStep.vue
new file mode 100644
index 0000000..5a4d700
--- /dev/null
+++ b/src/views/ai/knowledge/document/form/UploadStep.vue
@@ -0,0 +1,273 @@
+<template>
+ <el-form ref="formRef" :model="modelData" label-width="0" class="mt-20px">
+ <el-form-item class="mb-20px">
+ <div class="w-full">
+ <div
+ class="w-full border-2 border-dashed border-[#dcdfe6] rounded-md p-20px text-center hover:border-[#409eff]"
+ >
+ <el-upload
+ ref="uploadRef"
+ class="upload-demo"
+ drag
+ :action="uploadUrl"
+ :auto-upload="true"
+ :on-success="handleUploadSuccess"
+ :on-error="handleUploadError"
+ :on-change="handleFileChange"
+ :on-remove="handleFileRemove"
+ :before-upload="beforeUpload"
+ :http-request="httpRequest"
+ :file-list="fileList"
+ :multiple="true"
+ :show-file-list="false"
+ :accept="acceptedFileTypes"
+ >
+ <div class="flex flex-col items-center justify-center py-20px">
+ <Icon icon="ep:upload-filled" class="text-[48px] text-[#c0c4cc] mb-10px" />
+ <div class="el-upload__text text-[16px] text-[#606266]">
+ 鎷栨嫿鏂囦欢鑷虫锛屾垨鑰�
+ <em class="text-[#409eff] not-italic cursor-pointer">閫夋嫨鏂囦欢</em>
+ </div>
+ <div class="el-upload__tip mt-10px text-[#909399] text-[12px]">
+ 宸叉敮鎸� {{ supportedFileTypes.join('銆�') }}锛屾瘡涓枃浠朵笉瓒呰繃 {{ maxFileSize }} MB銆�
+ </div>
+ </div>
+ </el-upload>
+ </div>
+
+ <div
+ v-if="modelData.list && modelData.list.length > 0"
+ class="mt-15px grid grid-cols-1 gap-2"
+ >
+ <div
+ v-for="(file, index) in modelData.list"
+ :key="index"
+ class="flex justify-between items-center py-4px px-12px border-l-4 border-l-[#409eff] rounded-sm shadow-sm hover:bg-[#ecf5ff] transition-all duration-300"
+ >
+ <div class="flex items-center">
+ <Icon icon="ep:document" class="mr-8px text-[#409eff]" />
+ <span class="text-[13px] text-[#303133] break-all">{{ file.name }}</span>
+ </div>
+ <el-button type="danger" link @click="removeFile(index)" class="ml-2">
+ <Icon icon="ep:delete" />
+ </el-button>
+ </div>
+ </div>
+ </div>
+ </el-form-item>
+
+ <!-- 娣诲姞涓嬩竴姝ユ寜閽� -->
+ <el-form-item>
+ <div class="flex justify-end w-full">
+ <el-button type="primary" @click="handleNextStep" :disabled="!isAllUploaded">
+ 涓嬩竴姝�
+ </el-button>
+ </div>
+ </el-form-item>
+ </el-form>
+</template>
+
+<script lang="ts" setup>
+import { PropType, ref, computed, inject, getCurrentInstance, onMounted } from 'vue'
+import { useMessage } from '@/hooks/web/useMessage'
+import { useUpload } from '@/components/UploadFile/src/useUpload'
+import { generateAcceptedFileTypes } from '@/utils'
+import { Icon } from '@/components/Icon'
+
+const props = defineProps({
+ modelValue: {
+ type: Object as PropType<any>,
+ required: true
+ }
+})
+
+const emit = defineEmits(['update:modelValue'])
+
+const formRef = ref() // 琛ㄥ崟寮曠敤
+const uploadRef = ref() // 涓婁紶缁勪欢寮曠敤
+const parent = inject('parent', null) // 鑾峰彇鐖剁粍浠跺疄渚�
+const { uploadUrl, httpRequest } = useUpload() // 浣跨敤涓婁紶缁勪欢鐨勯挬瀛�
+const message = useMessage() // 娑堟伅寮圭獥
+const fileList = ref([]) // 鏂囦欢鍒楄〃
+const uploadingCount = ref(0) // 涓婁紶涓殑鏂囦欢鏁伴噺
+
+// 鏀寔鐨勬枃浠剁被鍨嬪拰澶у皬闄愬埗
+const supportedFileTypes = [
+ 'TXT',
+ 'MARKDOWN',
+ 'MDX',
+ 'PDF',
+ 'HTML',
+ 'XLSX',
+ 'XLS',
+ 'DOC',
+ 'DOCX',
+ 'CSV',
+ 'EML',
+ 'MSG',
+ 'PPTX',
+ 'XML',
+ 'EPUB',
+ 'PPT',
+ 'MD',
+ 'HTM'
+]
+const allowedExtensions = supportedFileTypes.map((ext) => ext.toLowerCase()) // 灏忓啓鐨勬墿灞曞悕鍒楄〃
+const maxFileSize = 15 // 鏈�澶ф枃浠跺ぇ灏�(MB)
+
+// 鏋勫缓 accept 灞炴�у�硷紝鐢ㄤ簬闄愬埗鏂囦欢閫夋嫨瀵硅瘽妗嗕腑鍙鐨勬枃浠剁被鍨�
+const acceptedFileTypes = computed(() => generateAcceptedFileTypes(supportedFileTypes))
+
+/** 琛ㄥ崟鏁版嵁 */
+const modelData = computed({
+ get: () => {
+ return props.modelValue
+ },
+ set: (val) => emit('update:modelValue', val)
+})
+
+/** 纭繚 list 灞炴�у瓨鍦� */
+const ensureListExists = () => {
+ if (!props.modelValue.list) {
+ emit('update:modelValue', {
+ ...props.modelValue,
+ list: []
+ })
+ }
+}
+
+/** 鏄惁鎵�鏈夋枃浠堕兘宸蹭笂浼犲畬鎴� */
+const isAllUploaded = computed(() => {
+ return modelData.value.list && modelData.value.list.length > 0 && uploadingCount.value === 0
+})
+
+/**
+ * 涓婁紶鍓嶆鏌ユ枃浠剁被鍨嬪拰澶у皬
+ *
+ * @param file 寰呬笂浼犵殑鏂囦欢
+ * @returns 鏄惁鍏佽涓婁紶
+ */
+const beforeUpload = (file) => {
+ // 1.1 妫�鏌ユ枃浠舵墿灞曞悕
+ const fileName = file.name.toLowerCase()
+ const fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1)
+ if (!allowedExtensions.includes(fileExtension)) {
+ message.error('涓嶆敮鎸佺殑鏂囦欢绫诲瀷锛�')
+ return false
+ }
+ // 1.2 妫�鏌ユ枃浠跺ぇ灏�
+ if (!(file.size / 1024 / 1024 < maxFileSize)) {
+ message.error(`鏂囦欢澶у皬涓嶈兘瓒呰繃 ${maxFileSize} MB锛乣)
+ return false
+ }
+
+ // 2. 澧炲姞涓婁紶涓殑鏂囦欢璁℃暟
+ uploadingCount.value++
+ return true
+}
+
+/**
+ * 鏂囦欢涓婁紶鎴愬姛澶勭悊
+ *
+ * @param response 涓婁紶鍝嶅簲
+ * @param file 涓婁紶鐨勬枃浠�
+ */
+const handleUploadSuccess = (response, file) => {
+ // 娣诲姞鍒版枃浠跺垪琛�
+ if (response && response.data) {
+ ensureListExists()
+ emit('update:modelValue', {
+ ...props.modelValue,
+ list: [
+ ...props.modelValue.list,
+ {
+ name: file.name,
+ url: response.data
+ }
+ ]
+ })
+ } else {
+ message.error(`鏂囦欢 ${file.name} 涓婁紶澶辫触`)
+ }
+
+ // 鍑忓皯涓婁紶涓殑鏂囦欢璁℃暟
+ uploadingCount.value = Math.max(0, uploadingCount.value - 1)
+}
+
+/**
+ * 鏂囦欢涓婁紶澶辫触澶勭悊
+ *
+ * @param error 閿欒淇℃伅
+ * @param file 涓婁紶鐨勬枃浠�
+ */
+const handleUploadError = (error, file) => {
+ message.error(`鏂囦欢 ${file.name} 涓婁紶澶辫触: ${error}`)
+ // 鍑忓皯涓婁紶涓殑鏂囦欢璁℃暟
+ uploadingCount.value = Math.max(0, uploadingCount.value - 1)
+}
+
+/**
+ * 鏂囦欢鍙樻洿澶勭悊
+ *
+ * @param file 鍙樻洿鐨勬枃浠�
+ */
+const handleFileChange = (file) => {
+ if (file.status === 'success' || file.status === 'fail') {
+ uploadingCount.value = Math.max(0, uploadingCount.value - 1)
+ }
+}
+
+/**
+ * 鏂囦欢绉婚櫎澶勭悊
+ *
+ * @param file 琚Щ闄ょ殑鏂囦欢
+ */
+const handleFileRemove = (file) => {
+ if (file.status === 'uploading') {
+ uploadingCount.value = Math.max(0, uploadingCount.value - 1)
+ }
+}
+
+/**
+ * 浠庡垪琛ㄤ腑绉婚櫎鏂囦欢
+ *
+ * @param index 瑕佺Щ闄ょ殑鏂囦欢绱㈠紩
+ */
+const removeFile = (index: number) => {
+ // 浠庡垪琛ㄤ腑绉婚櫎鏂囦欢
+ const newList = [...props.modelValue.list]
+ newList.splice(index, 1)
+ // 鏇存柊琛ㄥ崟鏁版嵁
+ emit('update:modelValue', {
+ ...props.modelValue,
+ list: newList
+ })
+}
+
+/** 涓嬩竴姝ユ寜閽鐞� */
+const handleNextStep = () => {
+ // 1.1 妫�鏌ユ槸鍚︽湁鏂囦欢涓婁紶
+ if (!modelData.value.list || modelData.value.list.length === 0) {
+ message.warning('璇蜂笂浼犺嚦灏戜竴涓枃浠�')
+ return
+ }
+ // 1.2 妫�鏌ユ槸鍚︽湁鏂囦欢姝e湪涓婁紶
+ if (uploadingCount.value > 0) {
+ message.warning('璇风瓑寰呮墍鏈夋枃浠朵笂浼犲畬鎴�')
+ return
+ }
+
+ // 2. 鑾峰彇鐖剁粍浠剁殑goToNextStep鏂规硶
+ const parentEl = parent || getCurrentInstance()?.parent
+ if (parentEl && typeof parentEl.exposed?.goToNextStep === 'function') {
+ parentEl.exposed.goToNextStep()
+ }
+}
+
+/** 鍒濆鍖� */
+onMounted(() => {
+ ensureListExists()
+})
+</script>
+
+<style lang="scss" scoped></style>
--
Gitblit v1.8.0