From b4c2cbc7825fe4732fb6333447fdada9567bcb97 Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期一, 12 十月 2026 17:48:36 +0800
Subject: [PATCH] 资源需求(标准物质相关)模块调整;新增统一附件组件并接入供应商管理与客户满意度;客户满意度表单改客户卡片下拉并关联已完成委托单;预览、图表、导航栏等组件调整
---
src/components/Preview/filePreview.vue | 275 ++++++++++++++++++++++++++++++++----------------------
1 files changed, 161 insertions(+), 114 deletions(-)
diff --git a/src/components/Preview/filePreview.vue b/src/components/Preview/filePreview.vue
index df8445e..e8ec772 100644
--- a/src/components/Preview/filePreview.vue
+++ b/src/components/Preview/filePreview.vue
@@ -1,53 +1,67 @@
<template>
<div>
- <div v-if="isImage">
- <img :src="imgUrl" alt="Image Preview" />
+ <!-- 鍥剧墖 -->
+ <div v-if="isImage" style="text-align: center">
+ <img :src="imgUrl" alt="鍥剧墖棰勮" />
</div>
- <div v-if="isPdf" style="height: 80vh;">
+ <!-- 瑙嗛 -->
+ <div v-else-if="isVideo" style="text-align: center">
+ <video :src="fileUrl" controls style="max-width: 100%; max-height: 85vh"></video>
+ </div>
+ <!-- 闊抽 -->
+ <div v-else-if="isAudio" style="text-align: center; padding-top: 10vh">
+ <audio :src="fileUrl" controls style="width: 60%"></audio>
+ </div>
+ <!-- PDF锛氫紶浜� OnlyOffice 閰嶇疆璧版枃妗f湇鍔★紝鍚﹀垯鐢ㄦ祻瑙堝櫒鍐呯疆闃呰鍣� -->
+ <div v-else-if="isPdf" style="height: 80vh;">
<onlyoffice v-if="useOnlyOffice" ref="onlyoffice" :options="option" style="width: 100%;height: 100%;" />
<iframe v-else-if="pdfUrl" :src="pdfUrl" title="PDF 棰勮" style="width: 100%;height: 100%;border: none;"></iframe>
<p v-else-if="pdfError">PDF 棰勮澶辫触锛�<a :href="fileUrl" style="color: var(--el-color-primary);" target="_blank">鐐规涓嬭浇鏌ョ湅</a></p>
<p v-else>姝e湪鍔犺浇 PDF鈥�</p>
</div>
- <div v-if="isDoc">
- <p v-if="!isDocShow">鏂囨。鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
- <a :href="fileUrl" v-if="!isDocShow">涓嬭浇鏂囦欢</a>
- <vue-office-docx v-else :src="fileUrl" style="height: 100vh;" @rendered="renderedHandler" @error="errorHandler" />
- </div>
- <div v-if="isXls">
- <p v-if="!isDocShow">鏂囨。鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
- <a :href="fileUrl" v-if="!isDocShow">涓嬭浇鏂囦欢</a>
- <vue-office-excel v-else :src="fileUrl" :options="excelOptions" style="height: 100vh;" @rendered="renderedHandler"
+ <!-- Word -->
+ <div v-else-if="isDoc">
+ <vue-office-docx v-if="isDocShow" :key="fileUrl" :src="fileUrl" style="height: 100vh;" @rendered="renderedHandler"
@error="errorHandler" />
+ <div v-else class="preview-fallback">
+ <p>Word 鏂囨。鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
+ <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
+ </div>
</div>
- <div v-if="isZipOrRar">
- <p>鍘嬬缉鏂囦欢鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
- <a :href="fileUrl">涓嬭浇鏂囦欢</a>
+ <!-- Excel -->
+ <div v-else-if="isXls">
+ <vue-office-excel v-if="isDocShow" :key="fileUrl" :src="fileUrl" :options="excelOptions" style="height: 100vh;"
+ @rendered="renderedHandler" @error="errorHandler" />
+ <div v-else class="preview-fallback">
+ <p>Excel 鏂囨。鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
+ <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
+ </div>
</div>
- <div v-if="isCsv">
- <p v-if="csvList.length == 0">CSV 鏂囦欢鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
- <a :href="fileUrl" v-if="csvList.length == 0">涓嬭浇鏂囦欢</a>
- <el-tabs type="border-card" v-if="csvList.length > 0" tab-position="bottom">
- <el-tab-pane :label="item.sheetName" v-for="(item, index) in csvList" :key="index">
- <el-table :data="item.tableData" height="75vh">
- <el-table-column :label="m.label" :prop="m.prop" v-for="(m, i) in item.column" :key="i" min-width="120px"
- show-overflow-tooltip>
- <template #header="scope">
- <div>
- <el-tooltip :content="m.label" placement="top">
- <div class="oneLine">
- <span>{{ m.label }}</span>
- </div>
- </el-tooltip>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </el-tab-pane>
- </el-tabs>
+ <!-- CSV 琛ㄦ牸棰勮 -->
+ <div v-else-if="isCsv">
+ <el-table v-if="csvRows.length" :data="csvRows" height="75vh">
+ <el-table-column :label="col" :prop="col" v-for="(col, i) in csvColumns" :key="i" min-width="120px"
+ show-overflow-tooltip />
+ </el-table>
+ <div v-else-if="csvError" class="preview-fallback">
+ <p>CSV 鏂囦欢瑙f瀽澶辫触锛岃涓嬭浇鏌ョ湅銆�</p>
+ <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
+ </div>
+ <p v-else>姝e湪鍔犺浇鈥�</p>
</div>
- <div v-if="!isSupported">
- <p>涓嶆敮鎸佺殑鏂囦欢鏍煎紡</p>
+ <!-- 鏂囨湰鏂囦欢 -->
+ <div v-else-if="isText">
+ <pre v-if="textLoaded" class="text-preview">{{ textContent }}</pre>
+ <div v-else-if="textError" class="preview-fallback">
+ <p>鏂囨湰鏂囦欢璇诲彇澶辫触锛岃涓嬭浇鏌ョ湅銆�</p>
+ <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
+ </div>
+ <p v-else>姝e湪鍔犺浇鈥�</p>
+ </div>
+ <!-- 鍏滃簳锛氬帇缂╁寘銆佺▼搴忔枃浠剁瓑娴忚鍣ㄦ棤娉曠洿鎺ユ樉绀虹殑鏍煎紡锛屼繚璇佷篃鍙笅杞� -->
+ <div v-else class="preview-fallback">
+ <p>璇ョ被鍨嬫枃浠讹紙{{ fileExt }}锛夋祻瑙堝櫒鏃犳硶鍦ㄧ嚎鏄剧ず锛岃涓嬭浇鍚庢煡鐪嬨��</p>
+ <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
</div>
</div>
</template>
@@ -85,22 +99,29 @@
data() {
return {
isDocShow: true,
- csvList: [], //csv鏂囦欢鏁版嵁
+ csvColumns: [],
+ csvRows: [],
+ csvError: false,
+ textContent: '',
+ textLoaded: false,
+ textError: false,
pdfUrl: '', //pdf鏈湴棰勮鍦板潃
pdfError: false
}
},
computed: {
- // 鍘熷疄鐜版妸 isImage / isXls / isCsv 鍐欐垚浜嗐�岃鏂囦欢鍚庣紑 + 椤烘墜鏀瑰埆鐨勫搷搴斿紡鐘舵�併�嶇殑 computed锛�
- // isImage 閲屽啓 imgUrl銆乮sXls 閲屽啓 options.xls銆乮sCsv 閲岀洿鎺ュ彂璇锋眰銆�
- // computed 閲屽啓鐘舵�佸睘浜庡壇浣滅敤锛屽湪 Vue3 涓細鍦ㄦ覆鏌撴湡瑙﹀彂鐘舵�佸彉鏇达紝寰堝鏄撴紨鍙樻垚閫掑綊鏇存柊銆�
- // 杩欓噷鎷嗘垚绾绠楀睘鎬э紝鍓綔鐢ㄧ粺涓�浜ょ粰涓嬮潰閭d釜 watch銆�
isImage() {
return /\.(jpg|jpeg|png|gif)$/i.test(this.fileUrl)
},
imgUrl() {
- // 鍘� isImage 鏃犺鐪熷亣閮戒細鍐� imgUrl = fileUrl锛堝懡涓浘鐗囨椂鎶� word 鎹㈡垚 img锛�
+ // 鍥剧墖瀛樺湪 /img 闈欐�佽矾寰勪笅锛岃皟鐢ㄦ柟浼犵殑鍙兘鏄� /word 鍦板潃锛岃繖閲岀粺涓�鎹㈡垚 /img
return this.isImage ? this.fileUrl.replaceAll('word', 'img') : ''
+ },
+ isVideo() {
+ return /\.(mp4|webm|ogv|ogg)$/i.test(this.fileUrl)
+ },
+ isAudio() {
+ return /\.(mp3|wav|m4a|aac|flac|oga)$/i.test(this.fileUrl)
},
isPdf() {
return /\.pdf$/i.test(this.fileUrl)
@@ -110,6 +131,12 @@
},
isXls() {
return /\.(xls|xlsx)$/i.test(this.fileUrl)
+ },
+ isCsv() {
+ return /\.csv$/i.test(this.fileUrl)
+ },
+ isText() {
+ return /\.(txt|log|json|xml|md|ini|conf|cfg|yml|yaml|sql|properties)$/i.test(this.fileUrl)
},
// vue-office 鐨� excel 缁勪欢鐢� xls 鏍囧織鍖哄垎 xlsx / xls
excelOptions() {
@@ -121,20 +148,15 @@
heightOffset: 10, //鍦ㄩ粯璁ゆ覆鏌撶殑鍒楄〃楂樺害涓婂啀鍔� Npx楂�
beforeTransformData: workbookData => {
return workbookData
- }, //搴曞眰閫氳繃exceljs鑾峰彇excel鏂囦欢鍐呭锛岄�氳繃璇ラ挬瀛愬嚱鏁帮紝鍙互瀵硅幏鍙栫殑excel鏂囦欢鍐呭杩涜淇敼锛屾瘮濡傛煇涓崟鍏冩牸鐨勬暟鎹樉绀轰笉姝g‘锛屽彲浠ュ湪姝よ嚜琛屼慨鏀规瘡涓崟鍏冩牸鐨剉alue鍊笺��
+ }, //搴曞眰閫氳繃exceljs鑾峰彇excel鏂囦欢鍐呭锛岄�氳繃璇ラ挬瀛愬嚱鏁帮紝鍙互瀵硅幏鍙栧埌鐨別xcel鏂囦欢鍐呭杩涜淇敼锛屾瘮濡傛煇涓崟鍏冩牸鐨勬暟鎹樉绀轰笉姝g‘锛屽彲浠ュ湪姝よ嚜琛屼慨鏀规瘡涓崟鍏冩牸鐨剉alue鍊笺��
transformData: workbookData => {
return workbookData
} //灏嗚幏鍙栧埌鐨別xcel鏁版嵁杩涜澶勭悊涔嬪悗涓旀覆鏌撳埌椤甸潰涔嬪墠锛屽彲閫氳繃transformData瀵瑰嵆灏嗘覆鏌撶殑鏁版嵁鍙婃牱寮忚繘琛屼慨鏀癸紝姝ゆ椂姣忎釜鍗曞厓鏍肩殑text鍊煎氨鏄嵆灏嗘覆鏌撳埌椤甸潰涓婄殑鍐呭
}
},
- isZipOrRar() {
- return /\.(zip|rar)$/i.test(this.fileUrl)
- },
- isCsv() {
- return /\.csv$/i.test(this.fileUrl)
- },
- isSupported() {
- return this.isImage || this.isPdf || this.isDoc || this.isZipOrRar || this.isCsv || this.isXls
+ fileExt() {
+ const match = this.fileUrl.match(/\.([a-zA-Z0-9]+)$/)
+ return match ? match[1].toUpperCase() : '鏈煡'
},
// 璋冪敤鏂逛紶浜� OnlyOffice 鐨勯厤缃墠璧版枃妗f湇鍔★紝鍚﹀垯鐢ㄦ祻瑙堝櫒鍐呯疆闃呰鍣�
useOnlyOffice() {
@@ -144,10 +166,9 @@
watch: {
fileUrl: {
handler() {
+ this.resetState()
this.loadPdf()
- if (this.isCsv) {
- this.loadCSVData()
- }
+ this.loadTextContent()
},
immediate: true
}
@@ -156,6 +177,16 @@
this.releasePdfUrl()
},
methods: {
+ // 鍒囨崲鏂囦欢鏃舵竻鎺変笂涓�涓枃浠剁殑棰勮鐘舵�侊紝閬垮厤娌跨敤鏃х粨鏋滐紙姣斿涓婃 Word 娓叉煋澶辫触锛岃繖娆¤繕琚崱鍦ㄤ笅杞藉厹搴曢〉锛�
+ resetState() {
+ this.isDocShow = true
+ this.csvColumns = []
+ this.csvRows = []
+ this.csvError = false
+ this.textContent = ''
+ this.textLoaded = false
+ this.textError = false
+ },
releasePdfUrl() {
if (this.pdfUrl) {
URL.revokeObjectURL(this.pdfUrl)
@@ -178,51 +209,44 @@
this.pdfError = true
}
},
- renderedHandler() {
- console.log('娓叉煋瀹屾垚')
- this.isDocShow = true
- this.resetStyle()
- },
- errorHandler() {
- console.log('娓叉煋澶辫触')
- this.isDocShow = false
- },
- async loadCSVData() {
- // 鍘熷疄鐜颁緷璧� this.$axios / this.$api锛岃�岃繖涓や釜鍏ㄥ眬鍙橀噺鍦ㄩ」鐩噷浠庢湭娉ㄥ唽杩�
- // 锛坢ain.js 娌℃湁鎸傝浇锛屼篃娌℃湁 api 鑱氬悎瀵硅薄锛夛紝涓�鏃﹁繘鍏� CSV 鍒嗘敮灏辨姏 TypeError銆�
- // 鍦ㄦ嬁鍒扮湡瀹炴帴鍙d箣鍓嶅厛鏄惧紡闄嶇骇涓恒�屼笅杞芥煡鐪嬨�嶏紝涓庣敤鎴峰疄闄呯湅鍒扮殑缁撴灉涓�鑷淬��
- if (!this.$axios || !this.$api?.insOrderPlan?.preview) {
- console.warn('[filePreview] 鏈厤缃� $axios/$api锛孋SV 杞〃鏍间笉鍙敤锛屽凡闄嶇骇涓轰笅杞芥煡鐪�')
- this.csvList = []
+ // CSV 瑙f瀽鎴愯〃鏍笺�佹枃鏈枃浠剁洿鎺ュ睍绀猴紱鍐呭甯歌涓� GBK 缂栫爜锛孶TF-8 瑙e嚭涔辩爜鏃舵崲 GBK 鍐嶈В涓�娆�
+ async loadTextContent() {
+ if (!this.isCsv && !this.isText) {
return
}
- this.$axios
- .post(this.$api.insOrderPlan.preview, {
- id: this.currentFile.id
- })
- .then(res => {
- let arr = res.data
- arr = arr.map(m => {
- const obj = {
- sheetName: m.sheetName,
- tableData: [],
- column: []
- }
- obj.tableData = this.formatCSVToTable(m.content.replaceAll('null', ' '))
- // .replaceAll('MIN','=MIN').replaceAll('MAX','=MAX').replaceAll('AVERAGE','=AVERAGE')
- for (const item in obj.tableData[0]) {
- obj.column.push({
- label: item,
- prop: item
- })
- }
- return obj
- })
- this.csvList = arr
- })
- .catch(err => {
- console.log(err)
- })
+ try {
+ const { data } = await axios.get(this.fileUrl, { responseType: 'arraybuffer' })
+ let text = new TextDecoder('utf-8').decode(data)
+ // 瑙g爜缁撴灉閲屽嚭鐜� U+FFFD 鏇挎崲绗︼紝璇存槑鍘熸枃涓嶆槸 UTF-8锛屽父瑙佹槸 GBK 缂栫爜鐨勪腑鏂囨枃浠�
+ if (text.indexOf(String.fromCharCode(0xFFFD)) !== -1) {
+ try {
+ text = new TextDecoder('gbk').decode(data)
+ } catch (e) {
+ // 涓埆娴忚鍣ㄤ笉鏀寔 gbk 瑙g爜锛屼繚鐣� UTF-8 缁撴灉
+ }
+ }
+ if (this.isCsv) {
+ const rows = this.formatCSVToTable(text.replaceAll('\r\n', '\n').replaceAll('\r', '\n'))
+ if (rows.length) {
+ this.csvColumns = Object.keys(rows[0])
+ this.csvRows = rows.slice(0, 5000)
+ } else {
+ this.csvError = true
+ }
+ } else {
+ this.textContent = text.length > 1000000
+ ? text.slice(0, 1000000) + '\n\n鈥︹�︼紙鏂囦欢杩囧ぇ锛屼粎棰勮鍓嶄竴鐧句竾瀛楋紝瀹屾暣鍐呭璇蜂笅杞芥煡鐪嬶級'
+ : (text || '锛堟枃浠跺唴瀹逛负绌猴級')
+ this.textLoaded = true
+ }
+ } catch (error) {
+ console.error(error)
+ if (this.isCsv) {
+ this.csvError = true
+ } else {
+ this.textError = true
+ }
+ }
},
formatCSVToTable(str) {
const result = []
@@ -239,19 +263,10 @@
} else {
const obj = {}
for (let index = 0; index < arrHeader.length; index++) {
- // 缁勮琛ㄦ牸鏁版嵁
- const name = String(arrHeader[index])
+ const name = String(arrHeader[index]).trim()
if (!arrHeader[index]) continue
if (!obj[name]) {
- try {
- if (kv[index]) {
- obj[name] = String(kv[index])
- } else {
- obj[name] = ''
- }
- } catch (err) {
- obj[name] = ''
- }
+ obj[name] = kv[index] ? String(kv[index]) : ''
}
}
result.push(obj)
@@ -260,6 +275,21 @@
}
}
return result
+ },
+ downloadFile() {
+ const name = this.currentFile && this.currentFile.fileName
+ ? this.currentFile.fileName
+ : this.fileUrl.split('/').pop()
+ this.$download.saveAs(this.fileUrl, name)
+ },
+ renderedHandler() {
+ console.log('娓叉煋瀹屾垚')
+ this.isDocShow = true
+ this.resetStyle()
+ },
+ errorHandler() {
+ console.log('娓叉煋澶辫触')
+ this.isDocShow = false
},
resetStyle() {
// 娉細鍘熷疄鐜版槸 style.replace(/pt/g, 'px')锛屼細鎶婃牱寮忛噷浠绘剰浣嶇疆鐨� pt 閮芥崲鎺�
@@ -282,9 +312,26 @@
max-width: 100%;
}
-.oneLine {
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
+.text-preview {
+ white-space: pre-wrap;
+ word-break: break-all;
+ max-height: 85vh;
+ overflow: auto;
+ padding: 12px;
+ margin: 0;
+ background: #f8f9fa;
+ font-family: Consolas, Monaco, monospace;
+ font-size: 13px;
+ line-height: 1.6;
+}
+
+.preview-fallback {
+ text-align: center;
+ padding-top: 15vh;
+ color: #606266;
+}
+
+.preview-fallback p {
+ margin-bottom: 16px;
}
</style>
--
Gitblit v1.9.3