From 80ca024e9ae633df0dc9f4e8f533f33b526afb3d Mon Sep 17 00:00:00 2001 From: p-honggang.li <p-honggang.li@pcitc.com> Date: 星期一, 08 九月 2025 16:24:38 +0800 Subject: [PATCH] 修复文件上传获取不到用户信息的BUG --- src/views/tradeManage/upload/index.vue | 924 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 804 insertions(+), 120 deletions(-) diff --git a/src/views/tradeManage/upload/index.vue b/src/views/tradeManage/upload/index.vue index 344e497..3f34ba1 100644 --- a/src/views/tradeManage/upload/index.vue +++ b/src/views/tradeManage/upload/index.vue @@ -163,6 +163,7 @@ :on-exceed="onExceed" :on-remove="handleRemove" :show-file-list="false" + :before-upload="beforeUpload" > <el-button type="primary">閫夋嫨鏂囦欢</el-button> </el-upload> @@ -190,16 +191,47 @@ {{ formatFileSize(row.size) }} </template> </el-table-column> - <el-table-column label="鎿嶄綔" width="100"> + <el-table-column label="鎿嶄綔" width="280"> <template #default="{ row, $index }"> - <el-button - type="text" - size="small" - class="delete-btn" - @click="handleRemove(row, fileList)" - > - 鍒犻櫎 - </el-button> + <div class="file-actions"> + <el-button + type="text" + size="small" + class="upload-btn" + @click="handleUpload(row)" + v-if="!row.status || row.status === 'ready'" + :loading="row.uploading" + > + {{ row.uploading ? '涓婁紶涓�' : '涓婁紶' }} + </el-button> + <el-button + type="text" + size="small" + class="preview-btn" + @click="handlePreview(row)" + v-if="isPreviewable(row)" + :disabled="!isFileUploaded(row)" + > + 棰勮 + </el-button> + <el-button + type="text" + size="small" + class="download-btn" + @click="handleDownload(row)" + :disabled="!isFileUploaded(row)" + > + 涓嬭浇 + </el-button> + <el-button + type="text" + size="small" + class="delete-btn" + @click="handleRemove(row, fileList)" + > + 鍒犻櫎 + </el-button> + </div> </template> </el-table-column> </el-table> @@ -218,11 +250,18 @@ import { onMounted, reactive, ref, computed, type CSSProperties } from 'vue' import { useRoute, useRouter } from 'vue-router' import { Document, User, Goods, List } from '@element-plus/icons-vue' -import { fetchOrderDetail, uploadTradeFile } from '@/api/tradeManage' -import { ElMessage } from 'element-plus' +import { uploadTradeFile } from '@/api/tradeManage' +import orderApi from '@/api/orderApi' +import { ElMessage, ElMessageBox } from 'element-plus' +import { useUserInfo } from '@/stores/modules/userInfo' +import createAxios from '@/utils/axios' +import productApi from "@/api/productApi"; +import sysUserService from "@/api/sysUser"; +import {queryUserDetail} from "@/api/userInfo"; const route = useRoute() const router = useRouter() +const userStore = useUserInfo() const detail = reactive<any>({ items: [] }) const fileList = ref<any[]>([]) const orderTableWrapRef = ref<HTMLElement | null>(null) @@ -247,116 +286,369 @@ return [...detail.items, summaryRow] }) +// 鐘舵�佹槧灏勶紙鍚庣涓枃 -> 鍓嶇鏋氫妇锛� +const statusServerToUi: Record<string, string> = { + '寰呬笂浼犳枃浠�': 'WAIT_UPLOAD', + '寰呮巿鏉�': 'WAIT_AUTHORIZE', + '寰呬氦鏄撶‘璁�': 'WAIT_CONFIRM', + '宸插畬鎴�': 'COMPLETED', + '宸茶瘎浠�': 'EVALUATED', +} + +const formatDateTime = (val?: string) => (val ? val.replace('T', ' ').slice(0, 19) : '') + +const normalizePriceType = (val?: string): 'points' | 'currency' | 'agreement' | 'free' => { + if (!val) return 'currency' + const s = String(val) + if (/(绉垎|points)/i.test(s)) return 'points' + if (/(鍗忚|agreement)/i.test(s)) return 'agreement' + if (/(鍏嶈垂|free)/i.test(s)) return 'free' + return 'currency' +} + onMounted(async () => { - // 浣跨敤鍓嶇妯℃嫙鏁版嵁浠ヤ究寮�鍙� UI锛堜笉鏀瑰姩鍚庣鏈嶅姟锛� - const mockDetail = { - orderNo: '4348442557619205545', - resourceTypeName: '杞欢浜у搧', - status: 'WAIT_UPLOAD', - statusName: '寰呬笂浼犳枃浠�', - applyTime: '2025-05-21 10:00:00', - unitName: '涓氦鏂硅繙寤鸿鏈夐檺鍏徃', - userName: '寮犻潤', - userAccount: 'L20159922', - userDept: '淇℃伅涓績', - userPhone: '13800000000', - productName: '涓氦鏂硅繙鏅鸿兘瀹炴祴瀹為噺绠$悊绯荤粺', - supplier: '涓氦鏂硅繙绉戞妧鏈夐檺鍏徃', - industry: '寤虹瓚宸ョ▼', - projectUnit: '鍦熷缓宸ョ▼', - productType: '杞欢搴�', - productDesc: - '闈㈠悜宸ョ▼椤圭洰鐨勮川閲忓疄娴嬪疄閲忔暟瀛楀寲绠$悊绯荤粺锛屾敮鎸佹爣鍑嗗寲閲囬泦銆佽嚜鍔ㄧ粺璁′笌杩囩▼绠℃帶銆�', - items: [ - { - id: '1', - name: '浼佷笟绉佹湁SaaS鐗堣鍙�', - saleType: '涔版柇', - accountCount: 50, - customerTarget: '浼佷笟', - concurrentNodes: 50, - pricePoint: 50000, - priceCash: 0, - quantity: 1, - period: 0, - }, - { - id: '2', - name: '浼佷笟绉佹湁SaaS鐗圤TA鍗囩骇鏈嶅姟', - saleType: 'OTA鏈嶅姟', - accountCount: 50, - customerTarget: '浼佷笟', - concurrentNodes: 50, - pricePoint: 0, - priceCash: 7500, - quantity: 1, - period: 1, - }, - { - id: '3', - name: '浼佷笟绉佹湁SaaS鐗堢敤鎴峰閲忓寘', - saleType: '绉佹湁澧為噺鍖�', - accountCount: 100, - customerTarget: '浼佷笟', - concurrentNodes: 100, - pricePoint: 0, - priceCash: 0, - priceProtocol: true, - quantity: 1, - period: 1, - }, - { - id: '4', - name: '涓汉鍏湁SaaS鐗堣鍙�', - saleType: '绉佹湁澧為噺鍖�', - accountCount: 50, - customerTarget: '涓汉', - concurrentNodes: 50, - pricePoint: 0, - priceCash: 0, - quantity: 1, - period: 0, - }, - ], - pointTotal: '50,000', - cashTotal: '7,500', + const orderId = String(route.params.id || '') + if (!orderId) { + ElMessage.error('璁㈠崟ID涓嶈兘涓虹┖') + return } - Object.assign(detail, mockDetail) - - // 娣诲姞妯℃嫙鏂囦欢鏁版嵁鐢ㄤ簬灞曠ず - fileList.value = [ - { - name: '绛惧瓧鐩栫珷鏂囦欢.pdf', - size: 2621440, // 2.5MB - uid: '1', - status: 'success' - }, - { - name: 'API Keys.txt', - size: 354, // 354 Bytes - uid: '2', - status: 'success' + // 鑾峰彇鐢ㄦ埛淇℃伅 + if (!userStore.getUserId) { + try { + const res: any = await queryUserDetail() + if (res?.code === 200 && res.data) { + userStore.updateUserDetail(res.data) + } else { + ElMessage.error(res?.msg || '鏃犳硶鑾峰彇鐢ㄦ埛淇℃伅锛岃鍏堢櫥褰�') + return + } + } catch (e) { + console.error('鑾峰彇鐢ㄦ埛璇︽儏澶辫触:', e) + ElMessage.error('鑾峰彇鐢ㄦ埛淇℃伅澶辫触锛岃绋嶅悗閲嶈瘯') + return } - ] - - // 娉ㄩ噴鎺夊師鏈夌殑API璋冪敤锛屼娇鐢ㄦā鎷熸暟鎹� - // const { data } = (await fetchOrderDetail({ id: route.params.id })) as any - // Object.assign(detail, data || {}) + } + + try { + const res = (await orderApi.getOrderDetail(orderId)) as any + const data = res?.data || {} + + const statusName: string = data.orderStatus || '' + const uiStatus = statusServerToUi[statusName] || 'INFO' + + // 鏍规嵁浜у搧id鑾峰彇浜у搧淇℃伅锛屾洿鏂板ご閮ㄥ睍绀� + try { + if (data.productId) { + const detailRes: any = await productApi.getProductById({ id: data.productId }) + if (detailRes?.code === 200 && detailRes.data) { + // 鐢ㄤ骇鍝佽鎯呰ˉ鍏ㄥご淇℃伅 + data.productName = detailRes.data.name || data.productName + data.providerName = detailRes.data.submissionUnit || data.providerName + data.industry = detailRes.data.industrialChainName || data.industry + data.productDesc = detailRes.data.describe || data.productDesc + data.projectUnit = detailRes.data.importantAreaName || data.productDesc + data.productType = detailRes.data.typeName || data.productDesc + } + } + } catch (e) { + // 蹇界暐浜у搧璇︽儏澶辫触锛屼笉闃诲璁㈠崟璇︽儏 + } + + // 鑾峰彇鐢ㄦ埛淇℃伅 + try { + const userRes: any = await sysUserService.getUserdetail({ userId: data.userId }) + if (userRes?.code === 200 && userRes.data) { + // 鐢ㄤ骇鍝佽鎯呰ˉ鍏ㄥご淇℃伅 + data.unitName = userRes.data.unitName || data.unitName + data.userName = userRes.data.name || data.userName + data.userDept = userRes.data.departmentName || data.userDept + data.userPhone = userRes.data.phone || data.userPhone + data.userAccount = userRes.data.username || data.userAccount + } + }catch (e){ + + } + + // 鏄犲皠璁㈠崟璇︽儏澶撮儴淇℃伅 + const head = { + orderNo: data.orderId, + resourceTypeName: '杞欢浜у搧', + status: uiStatus, + statusName, + applyTime: formatDateTime(data.applyTime), + unitName: data.unitName || '-', + userName: data.userName || '-', + userAccount: data.userAccount || '-', + userDept: data.userDept || '-', + userPhone: data.userPhone || '-', + productName: data.productName || '-', + supplier: data.providerName || '-', + industry: data.industry || '-', + projectUnit: data.projectUnit || '-', + productType: data.productType || '-', + productDesc: data.productDesc || '-', + } + + // 鏄庣粏椤规槧灏� + const items: any[] = Array.isArray(data.orderDetails) + ? data.orderDetails.map((d: any, idx: number) => { + const pt = normalizePriceType(d.priceType) + return { + id: String(d.id ?? idx + 1), + name: d.suiteName, + saleType: d.salesForm, + accountCount: d.accountLimit, + customerTarget: d.customerType, + concurrentNodes: d.concurrentNodes, + pricePoint: pt === 'points' ? Number(d.unitPrice || 0) : 0, + priceCash: pt === 'currency' ? Number(d.unitPrice || 0) : 0, + priceProtocol: pt === 'agreement', + quantity: Number(d.quantity || 0), + period: Number(d.duration || 0), + } + }) + : [] + + // 姹囨�伙紙绠�鍗曠浉鍔狅細鍗曚环*鏁伴噺锛� + const pointTotalNum = items.reduce((sum, it) => sum + Number(it.pricePoint || 0) * Number(it.quantity || 0), 0) + const cashTotalNum = items.reduce((sum, it) => sum + Number(it.priceCash || 0) * Number(it.quantity || 0), 0) + + Object.assign(detail, head, { + items, + pointTotal: pointTotalNum.toLocaleString(), + cashTotal: cashTotalNum.toLocaleString(), + }) + + // 濡傛灉鏈夊凡涓婁紶鐨勬枃浠讹紝鏄剧ず鍦ㄦ枃浠跺垪琛ㄤ腑 + if (data.attachments && Array.isArray(data.attachments)) { + fileList.value = data.attachments.map((file: any) => ({ + name: file.fileName, + size: file.fileSize, + uid: file.id, + status: 'success', + url: file.fileUrl + })) + } + } catch (error) { + console.error('鑾峰彇璁㈠崟璇︽儏澶辫触:', error) + ElMessage.error('鑾峰彇璁㈠崟璇︽儏澶辫触') + } }) const onExceed = () => ElMessage.warning('鏈�澶氶�夋嫨5涓枃浠�') + +// 鏂囦欢涓婁紶鍓嶇殑楠岃瘉 +const beforeUpload = (file: File) => { + // 妫�鏌ユ枃浠跺ぇ灏忥紙闄愬埗涓�100MB锛� + const maxSize = 500 * 1024 * 1024 + if (file.size > maxSize) { + ElMessage.error('鏂囦欢澶у皬涓嶈兘瓒呰繃500MB') + return false + } + + // 妫�鏌ユ枃浠跺悕闀垮害 + if (file.name.length > 100) { + ElMessage.error('鏂囦欢鍚嶇О涓嶈兘瓒呰繃100瀛楃') + return false + } + + // 璁剧疆鏂囦欢鍒濆鐘舵�� + const fileObj = { + name: file.name, + size: file.size, + type: file.type, + raw: file, + status: 'ready', // 鍒濆鐘舵�佷负鍑嗗涓婁紶 + uploading: false, + uploaded: false + } + + // 灏嗘枃浠舵坊鍔犲埌鏂囦欢鍒楄〃 + fileList.value.push(fileObj) + + return false // 闃绘鑷姩涓婁紶锛屾敼涓烘墜鍔ㄤ笂浼� +} + +// 涓婁紶鍗曚釜鏂囦欢鍒版湇鍔″櫒 +const uploadSingleFile = async (file: File) => { + const formData = new FormData() + formData.append('file', file) + formData.append('folder', 'order-attachments') + + try { + console.log('寮�濮嬩笂浼犳枃浠�:', file.name, '澶у皬:', file.size) + + const response = await createAxios({ + url: '/admin/file/upload', + method: 'POST', + headers: { + 'Content-Type': 'multipart/form-data' + }, + data: formData + }) + + console.log('鏂囦欢涓婁紶鍝嶅簲:', response) + + // 妫�鏌ュ搷搴旀牸寮� - 鏍规嵁瀹為檯杩斿洖鏍煎紡璋冩暣 + const responseData = response as any + + // 鏍规嵁瀹為檯鍝嶅簲鏍煎紡锛岀洿鎺ユ鏌� responseData.code + if (responseData && responseData.code === 200) { + console.log('鏂囦欢涓婁紶鎴愬姛锛岃繑鍥炴暟鎹�:', responseData.data) + return responseData.data // 杩斿洖鏂囦欢URL + } else if (responseData && responseData.data && responseData.data.code === 200) { + // 澶囩敤妫�鏌ワ細濡傛灉鍝嶅簲琚寘瑁呭湪 data 涓� + console.log('鏂囦欢涓婁紶鎴愬姛锛岃繑鍥炴暟鎹�:', responseData.data.data) + return responseData.data.data // 杩斿洖鏂囦欢URL + } else { + // 澶勭悊閿欒鎯呭喌 + const errorMsg = responseData?.msg || responseData?.data?.msg || responseData?.message || responseData?.data?.message || '鏂囦欢涓婁紶澶辫触' + console.error('鏂囦欢涓婁紶澶辫触锛岄敊璇俊鎭�:', errorMsg) + throw new Error(errorMsg) + } + } catch (error) { + console.error('鏂囦欢涓婁紶寮傚父:', error) + throw error + } +} + +// 淇濆瓨鏂囦欢淇℃伅鍒版暟鎹簱 +const saveFileInfo = async (fileData: any) => { + try { + console.log('寮�濮嬩繚瀛樻枃浠朵俊鎭�:', fileData) + + // 浣跨敤FormData鏍煎紡锛屽洜涓哄悗绔娇鐢ˊRequestParam鎺ユ敹鍙傛暟 + const formData = new FormData() + formData.append('orderId', fileData.orderId) + formData.append('fileName', fileData.fileName) + formData.append('originalName', fileData.originalName) + formData.append('fileType', fileData.fileType) + formData.append('fileSize', fileData.fileSize.toString()) + formData.append('fileUrl', fileData.fileUrl) + formData.append('bucketName', fileData.bucketName) + formData.append('objectName', fileData.objectName) + formData.append('uploadUserId', fileData.uploadUserId.toString()) + formData.append('uploadUserName', fileData.uploadUserName) + formData.append('attachmentType', fileData.attachmentType) + formData.append('description', fileData.description) + + console.log('鍑嗗鍙戦�佺殑鏂囦欢淇℃伅:', { + orderId: fileData.orderId, + fileName: fileData.fileName, + fileSize: fileData.fileSize, + fileUrl: fileData.fileUrl, + uploadUserId: fileData.uploadUserId, + uploadUserName: fileData.uploadUserName + }) + + const response = await createAxios({ + url: '/admin/api/order/attachment/upload', + method: 'POST', + headers: { + 'Content-Type': 'multipart/form-data' + }, + data: formData + }) + + console.log('淇濆瓨鏂囦欢淇℃伅鍝嶅簲:', response) + + // 浣跨敤涓庢枃浠朵笂浼犵浉鍚岀殑鍝嶅簲鍒ゆ柇閫昏緫 + const responseData = response as any + + if (responseData && responseData.code === 200) { + console.log('鏂囦欢淇℃伅淇濆瓨鎴愬姛锛岃繑鍥炵殑闄勪欢ID:', responseData.data) + const attachmentId = responseData.data + + // 楠岃瘉attachmentId鏄惁涓烘湁鏁堢殑鏁板瓧 + if (typeof attachmentId === 'number' && attachmentId > 0) { + return attachmentId + } else { + console.error('杩斿洖鐨勯檮浠禝D涓嶆槸鏈夋晥鏁板瓧:', attachmentId, typeof attachmentId) + throw new Error(`鏃犳晥鐨勯檮浠禝D: ${attachmentId}`) + } + } else if (responseData && responseData.data && responseData.data.code === 200) { + // 澶囩敤妫�鏌ワ細濡傛灉鍝嶅簲琚寘瑁呭湪 data 涓� + console.log('鏂囦欢淇℃伅淇濆瓨鎴愬姛锛堝鐢ㄦ鏌ワ級锛岃繑鍥炵殑闄勪欢ID:', responseData.data.data) + const attachmentId = responseData.data.data + + // 楠岃瘉attachmentId鏄惁涓烘湁鏁堢殑鏁板瓧 + if (typeof attachmentId === 'number' && attachmentId > 0) { + return attachmentId + } else { + console.error('杩斿洖鐨勯檮浠禝D涓嶆槸鏈夋晥鏁板瓧锛堝鐢ㄦ鏌ワ級:', attachmentId, typeof attachmentId) + throw new Error(`鏃犳晥鐨勯檮浠禝D: ${attachmentId}`) + } + } else { + // 澶勭悊閿欒鎯呭喌 + const errorMsg = responseData?.msg || responseData?.data?.msg || responseData?.message || responseData?.data?.message || '淇濆瓨鏂囦欢淇℃伅澶辫触' + console.error('鏂囦欢淇℃伅淇濆瓨澶辫触:', errorMsg) + throw new Error(errorMsg) + } + } catch (error) { + console.error('淇濆瓨鏂囦欢淇℃伅寮傚父:', error) + throw error + } +} + + + const goBack = () => router.back() + +// 鎻愪氦鏂囦欢骞舵洿鏂拌鍗曠姸鎬� const submit = async () => { - // 妯℃嫙鎻愪氦鎴愬姛鍝嶅簲 - const mockResponse = { code: 200 } - - // 娉ㄩ噴鎺夊師鏈夌殑API璋冪敤锛屼娇鐢ㄦā鎷熸暟鎹� - // const { code } = (await uploadTradeFile({ id: route.params.id, files: fileList.value })) as any - - if (mockResponse.code === 200) { + if (fileList.value.length === 0) { + ElMessage.warning('璇疯嚦灏戜笂浼犱竴涓枃浠�') + return + } + + // 妫�鏌ユ槸鍚︽湁鏈笂浼犵殑鏂囦欢 + const unuploadedFiles = fileList.value.filter(file => !file.uploaded && !file.url) + if (unuploadedFiles.length > 0) { + ElMessage.warning('璇峰厛涓婁紶鎵�鏈夋枃浠�') + return + } + + try { + const orderId = String(route.params.id || '') + const userId = userStore.getUserId + const userName = userStore.username || userStore.name || '鏈煡鐢ㄦ埛' + + // 涓婁紶鎵�鏈夋湭涓婁紶鐨勬枃浠� + const uploadPromises = fileList.value + .filter(fileItem => fileItem.raw && !fileItem.uploaded) + .map(async (fileItem) => { + const fileUrl = await uploadSingleFile(fileItem.raw) + + // 淇濆瓨鏂囦欢淇℃伅鍒版暟鎹簱 + const attachmentData = { + orderId: orderId, + fileName: fileItem.name, + originalName: fileItem.name, + fileType: fileItem.type || 'application/octet-stream', + fileSize: fileItem.size, + fileUrl: fileUrl, + bucketName: 'dev', + objectName: fileUrl.split('/').pop(), + uploadUserId: userId, + uploadUserName: userName, + attachmentType: '鍏朵粬', + description: '浜ゆ槗鏂囦欢' + } + + await saveFileInfo(attachmentData) + }) + + await Promise.all(uploadPromises) + + // 鏇存柊璁㈠崟鐘舵�佽繘鍏ヤ笅涓�涓姸鎬� + await orderApi.updateOrderStatusToNext(orderId) + ElMessage.success('鎻愪氦鎴愬姛') router.back() + } catch (error) { + console.error('鎻愪氦澶辫触:', error) + ElMessage.error(error instanceof Error ? error.message : '鎻愪氦澶辫触') } } @@ -456,13 +748,365 @@ // 鏂囦欢鍒楄〃琛ㄦ牸琛ㄤ綋鏂囧瓧澶у皬 const fileTableCellStyle: CSSProperties = { fontSize: '12px' }; +// 鍒ゆ柇鏂囦欢鏄惁鍙瑙� +const isPreviewable = (file: any) => { + // 棣栧厛妫�鏌IME绫诲瀷 + const previewableTypes = [ + 'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/bmp', 'image/webp', + 'text/plain', 'text/html', 'text/css', 'text/javascript', + 'application/pdf', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // .docx + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', // .pptx + ] + + // 濡傛灉MIME绫诲瀷鍖归厤锛岀洿鎺ヨ繑鍥瀟rue + if (previewableTypes.includes(file.type || '')) { + return true + } + + // 濡傛灉MIME绫诲瀷涓虹┖鎴栦笉鍖归厤锛屾牴鎹枃浠舵墿灞曞悕鍒ゆ柇 + const fileName = file.name || '' + const fileExtension = fileName.toLowerCase().split('.').pop() + + const previewableExtensions = [ + 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', + 'txt', 'html', 'htm', 'css', 'js', + 'pdf', + 'docx', 'xlsx', 'pptx' + ] + + return previewableExtensions.includes(fileExtension) +} + +// 鏂囦欢棰勮 +const handlePreview = async (file: any) => { + if (!file.url) { + ElMessage.warning('鏂囦欢閾炬帴涓嶅瓨鍦�') + return + } + + // 鑾峰彇鏂囦欢鎵╁睍鍚� + const fileName = file.name || '' + const fileExtension = fileName.toLowerCase().split('.').pop() + + let previewUrl = file.url + + // 濡傛灉鏂囦欢瀛樺偍鍦∕inIO锛屼紭鍏堜娇鐢ㄩ瑙圲RL + if (file.url.includes('order-attachments')) { + try { + // 棣栧厛灏濊瘯鑾峰彇棰勮URL + const previewResponse = await createAxios({ + url: `/admin/file/preview`, + method: 'GET', + params: { + fileName: file.url + } + }) + + console.log('棰勮URL鍝嶅簲:', previewResponse) + + // 妫�鏌ュ搷搴旀牸寮� + const responseData = previewResponse as any + if (responseData && responseData.code === 200 && responseData.data) { + // previewUrl = responseData.data.replaceAll("192.168.20.52", import.meta.env.) + console.log('浣跨敤棰勮URL:', previewUrl) + } else { + console.log('棰勮URL鑾峰彇澶辫触锛屼娇鐢ㄤ笅杞芥柟寮�') + // 濡傛灉棰勮URL鑾峰彇澶辫触锛屽洖閫�鍒颁笅杞芥柟寮� + const response = await createAxios({ + url: `/admin/file/download`, + method: 'GET', + responseType: 'blob', + params: { + fileName: file.url, + originalName: file.name + } + }) + + // 鍒涘缓棰勮URL + const blob = new Blob([response as any]) + previewUrl = window.URL.createObjectURL(blob) + console.log('浣跨敤Blob URL:', previewUrl) + } + } catch (error) { + console.error('鑾峰彇鏂囦欢澶辫触:', error) + ElMessage.error('鑾峰彇鏂囦欢澶辫触锛屾棤娉曢瑙�') + return + } + } + + // 鍥剧墖鏂囦欢鐩存帴鍦ㄦ柊绐楀彛鎵撳紑 + if ((file.type && file.type.startsWith('image/')) || + ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(fileExtension)) { + console.log('棰勮鍥剧墖鏂囦欢:', previewUrl) + window.open(previewUrl, '_blank') + return + } + + // PDF鏂囦欢鍦ㄦ柊绐楀彛鎵撳紑 + if (file.type === 'application/pdf' || fileExtension === 'pdf') { + console.log('棰勮PDF鏂囦欢:', previewUrl) + window.open(previewUrl, '_blank') + return + } + + // 鏂囨湰鏂囦欢鍦ㄦ柊绐楀彛鎵撳紑 + if ((file.type && file.type.startsWith('text/')) || + ['txt', 'html', 'htm', 'css', 'js'].includes(fileExtension)) { + console.log('棰勮鏂囨湰鏂囦欢:', previewUrl) + window.open(previewUrl, '_blank') + return + } + + // Office鏂囨。鍜屽叾浠栨枃浠剁被鍨嬶紝灏濊瘯鍦ㄦ柊绐楀彛鎵撳紑 + try { + console.log('棰勮鍏朵粬鏂囦欢:', previewUrl) + window.open(previewUrl, '_blank') + } catch (error) { + console.error('棰勮澶辫触:', error) + ElMessage.error('棰勮澶辫触锛岃涓嬭浇鍚庢煡鐪�') + } +} + +// 鍒ゆ柇鏂囦欢鏄惁宸蹭笂浼犳垚鍔� +const isFileUploaded = (file: any) => { + // 鏂囦欢鏈塽rl涓旂姸鎬佷负success琛ㄧず宸蹭笂浼犳垚鍔� + return file.url && (file.status === 'success' || file.uploaded) +} + +// 鍗曚釜鏂囦欢涓婁紶 +const handleUpload = async (file: any) => { + if (!file.raw) { + ElMessage.warning('鏂囦欢鏁版嵁涓嶅瓨鍦�') + return + } + + // 璁剧疆涓婁紶鐘舵�� + file.uploading = true + + try { + // 涓婁紶鏂囦欢鍒版湇鍔″櫒 + const fileUrl = await uploadSingleFile(file.raw) + + // 淇濆瓨鏂囦欢淇℃伅鍒版暟鎹簱 + const orderId = String(route.params.id || '') + const userId = userStore.getUserId + const userName = userStore.username || userStore.name || '鏈煡鐢ㄦ埛' + + const attachmentData = { + orderId: orderId, + fileName: file.name, + originalName: file.name, + fileType: file.type || 'application/octet-stream', + fileSize: file.size, + fileUrl: fileUrl, + bucketName: 'dev', + objectName: fileUrl.split('/').pop(), + uploadUserId: userId, + uploadUserName: userName, + attachmentType: '鍏朵粬', + description: '浜ゆ槗鏂囦欢' + } + + const attachmentId = await saveFileInfo(attachmentData) + + // 鏇存柊鏂囦欢鐘舵�� + file.url = fileUrl + file.uid = attachmentId // 璁剧疆姝g‘鐨勯檮浠禝D + file.status = 'success' + file.uploaded = true + file.uploading = false + + ElMessage.success('鏂囦欢涓婁紶鎴愬姛') + } catch (error) { + console.error('鏂囦欢涓婁紶澶辫触:', error) + file.uploading = false + ElMessage.error(error instanceof Error ? error.message : '鏂囦欢涓婁紶澶辫触') + } +} + +// 鏂囦欢涓嬭浇 +const handleDownload = async (file: any) => { + if (!file.url) { + ElMessage.warning('鏂囦欢閾炬帴涓嶅瓨鍦�') + return + } + + console.log('寮�濮嬩笅杞芥枃浠�:', file.name, 'URL:', file.url) + + try { + // 濡傛灉鏂囦欢瀛樺偍鍦∕inIO锛屼娇鐢ㄥ悗绔洿鎺ヤ笅杞紸PI + if (file.url.includes('order-attachments')) { + console.log('浣跨敤MinIO涓嬭浇API') + + // 浣跨敤axios閫氳繃浠g悊璁块棶鍚庣API + const response = await createAxios({ + url: `/admin/file/download`, + method: 'GET', + responseType: 'blob', + params: { + fileName: file.url, + originalName: file.name + } + }) + + console.log('涓嬭浇鍝嶅簲:', response) + + // 鍒涘缓涓嬭浇閾炬帴 + const blob = new Blob([response as any]) + const downloadUrl = window.URL.createObjectURL(blob) + + console.log('鍒涘缓涓嬭浇閾炬帴:', downloadUrl) + + const link = document.createElement('a') + link.href = downloadUrl + link.download = file.name || 'download' + link.target = '_blank' + link.rel = 'noopener noreferrer' + + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + + // 娓呯悊URL瀵硅薄 + window.URL.revokeObjectURL(downloadUrl) + + ElMessage.success('鏂囦欢涓嬭浇鎴愬姛') + } else { + console.log('浣跨敤鐩存帴URL涓嬭浇') + // 鍏朵粬鎯呭喌鐩存帴浣跨敤鍘烾RL + const link = document.createElement('a') + link.href = file.url + link.download = file.name || 'download' + link.target = '_blank' + link.rel = 'noopener noreferrer' + + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + + ElMessage.success('寮�濮嬩笅杞芥枃浠�') + } + } catch (error) { + console.error('涓嬭浇澶辫触:', error) + ElMessage.error('涓嬭浇澶辫触锛岃閲嶈瘯') + } +} + +// 鍒犻櫎MinIO鏂囦欢 +const deleteMinioFile = async (fileName: string) => { + try { + console.log('寮�濮嬪垹闄inIO鏂囦欢:', fileName) + + const response = await createAxios({ + url: `/admin/file/delete`, + method: 'DELETE', + params: { + fileName: fileName + } + }) + + console.log('鍒犻櫎MinIO鏂囦欢鍝嶅簲:', response) + + const responseData = response as any + if (responseData && responseData.code === 200) { + console.log('MinIO鏂囦欢鍒犻櫎鎴愬姛') + return true + } else { + const errorMsg = responseData?.msg || responseData?.data?.msg || responseData?.message || responseData?.data?.message || '鍒犻櫎MinIO鏂囦欢澶辫触' + console.error('鍒犻櫎MinIO鏂囦欢澶辫触:', errorMsg) + throw new Error(errorMsg) + } + } catch (error) { + console.error('鍒犻櫎MinIO鏂囦欢寮傚父:', error) + throw error + } +} + +// 鍒犻櫎鏁版嵁搴撻檮浠惰褰� +const deleteAttachmentRecord = async (attachmentId: number) => { + try { + console.log('寮�濮嬪垹闄ゆ暟鎹簱闄勪欢璁板綍:', attachmentId) + + const response = await createAxios({ + url: `/admin/api/order/attachment/delete/${attachmentId}`, + method: 'DELETE' + }) + + console.log('鍒犻櫎鏁版嵁搴撻檮浠惰褰曞搷搴�:', response) + + const responseData = response as any + if (responseData && responseData.code === 200) { + console.log('鏁版嵁搴撻檮浠惰褰曞垹闄ゆ垚鍔�') + return true + } else { + const errorMsg = responseData?.msg || responseData?.data?.msg || responseData?.message || responseData?.data?.message || '鍒犻櫎鏁版嵁搴撻檮浠惰褰曞け璐�' + console.error('鍒犻櫎鏁版嵁搴撻檮浠惰褰曞け璐�:', errorMsg) + throw new Error(errorMsg) + } + } catch (error) { + console.error('鍒犻櫎鏁版嵁搴撻檮浠惰褰曞紓甯�:', error) + throw error + } +} + // 鏂囦欢鍒楄〃绉婚櫎鏂囦欢 -const handleRemove = (file: any, uploadFiles: any) => { - // 浠庢枃浠跺垪琛ㄤ腑绉婚櫎鎸囧畾鏂囦欢 - const index = fileList.value.findIndex(item => item.uid === file.uid) - if (index > -1) { - fileList.value.splice(index, 1) - ElMessage.success('鏂囦欢宸插垹闄�') +const handleRemove = async (file: any, uploadFiles: any) => { + try { + // 鏄剧ず纭瀵硅瘽妗� + await ElMessageBox.confirm( + `纭畾瑕佸垹闄ゆ枃浠� "${file.name}" 鍚楋紵`, + '鍒犻櫎纭', + { + confirmButtonText: '纭畾', + cancelButtonText: '鍙栨秷', + type: 'warning', + center: true + } + ) + + // 鐢ㄦ埛纭鍚庢墽琛屽垹闄ゆ搷浣� + // 濡傛灉鏄凡涓婁紶鐨勬枃浠讹紙鏈塽id涓斾负鏁板瓧锛岃〃绀烘暟鎹簱璁板綍ID锛� + if (file.uid && !isNaN(file.uid) && file.url) { + console.log('鍒犻櫎宸蹭笂浼犵殑鏂囦欢:', file.name, '闄勪欢ID:', file.uid) + + // 1. 鍒犻櫎MinIO涓殑鏂囦欢 + if (file.url.includes('order-attachments')) { + await deleteMinioFile(file.url) + } + + // 2. 鍒犻櫎鏁版嵁搴撲腑鐨勯檮浠惰褰� + await deleteAttachmentRecord(file.uid) + + // 3. 浠庢枃浠跺垪琛ㄤ腑绉婚櫎 + const index = fileList.value.findIndex(item => item.uid === file.uid) + if (index > -1) { + fileList.value.splice(index, 1) + } + + ElMessage.success('鏂囦欢鍒犻櫎鎴愬姛') + } else { + // 濡傛灉鏄湭涓婁紶鐨勬枃浠讹紙鍙湁raw鏂囦欢瀵硅薄锛� + console.log('鍒犻櫎鏈笂浼犵殑鏂囦欢:', file.name) + + // 鐩存帴浠庢枃浠跺垪琛ㄤ腑绉婚櫎 + const index = fileList.value.findIndex(item => item.name === file.name && item.size === file.size) + if (index > -1) { + fileList.value.splice(index, 1) + } + + ElMessage.success('鏂囦欢宸插垹闄�') + } + } catch (error) { + // 濡傛灉鐢ㄦ埛鍙栨秷鍒犻櫎锛宔rror涓�'cancel'锛屼笉闇�瑕佹樉绀洪敊璇秷鎭� + if (error === 'cancel') { + console.log('鐢ㄦ埛鍙栨秷鍒犻櫎鎿嶄綔') + return + } + + console.error('鍒犻櫎鏂囦欢澶辫触:', error) + ElMessage.error(error instanceof Error ? error.message : '鍒犻櫎鏂囦欢澶辫触') } } </script> @@ -649,10 +1293,50 @@ color: #409eff; } } - .delete-btn { - color: #f56c6c; - &:hover { - text-decoration: underline; + .file-actions { + display: flex; + gap: 8px; + align-items: center; + justify-content: center; + + .upload-btn { + color: #e6a23c; + &:hover { + text-decoration: underline; + } + &:disabled { + color: #c0c4cc; + cursor: not-allowed; + } + } + + .preview-btn { + color: #409eff; + &:hover { + text-decoration: underline; + } + &:disabled { + color: #c0c4cc; + cursor: not-allowed; + } + } + + .download-btn { + color: #67c23a; + &:hover { + text-decoration: underline; + } + &:disabled { + color: #c0c4cc; + cursor: not-allowed; + } + } + + .delete-btn { + color: #f56c6c; + &:hover { + text-decoration: underline; + } } } } -- Gitblit v1.8.0