Forráskód Böngészése

增加材料清单

2545307760@qq.com 9 hónapja
szülő
commit
603b76b19f

+ 7 - 3
web/package.json

@@ -12,15 +12,17 @@
     "dependencies": {
         "@element-plus/icons-vue": "^2.1.0",
         "@qiun/ucharts": "2.5.0-20230101",
-        "@vue-office/docx": "^1.3.0",
-        "@vue-office/excel": "^1.4.5",
-        "@vue-office/pdf": "^1.5.3",
+        "@vue-office/docx": "^1.6.3",
+        "@vue-office/excel": "^1.7.14",
+        "@vue-office/pdf": "^1.6.5",
         "@vueuse/core": "^10.7.2",
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^5.1.12",
         "ant-design-vue": "~4.2.6",
         "axios": "^1.4.0",
         "core-js": "^3.31.1",
+        "docx": "^9.5.1",
+        "docx-preview": "^0.3.7",
         "echarts": "5.4.3",
         "element-plus": "^2.3.8",
         "file-saver": "^2.0.5",
@@ -28,6 +30,7 @@
         "js-cookie": "^3.0.5",
         "jsencrypt": "^3.3.2",
         "less": "^4.2.0",
+        "mammoth": "^1.11.0",
         "marked": "4.3.0",
         "mitt": "^3.0.1",
         "moment": "^2.30.1",
@@ -41,6 +44,7 @@
         "tailwindcss": "^3.3.3",
         "ucharts": "^0.0.0",
         "vue": "^3.4.21",
+        "vue-demi": "^0.14.10",
         "vue-router": "^4.3.2",
         "vuedraggable": "^4.1.0",
         "xlsx": "^0.18.5"

+ 134 - 96
web/src/view/projectManage/projectDetails/components/preview.vue

@@ -1,106 +1,144 @@
 <template>
   <div>
-    <input type="file" @change="handleFileUpload" />
-    <div v-if="previewUrl">
-      <!-- 图片预览 -->
-      <img v-if="isImage" :src="previewUrl" alt="预览" style="max-width: 100%; max-height: 500px;">
-
-      <!-- PDF预览 -->
-      <iframe
-          v-else-if="isPdf"
+    <div style="width: 100%; height: 700px">
+      <img
           :src="previewUrl"
-          width="100%"
-          height="800px"
-          frameborder="0"
-      ></iframe>
-
-      <!-- 文本预览 -->
-      <pre v-else-if="isText" style="white-space: pre-wrap;">{{ textContent }}</pre>
-
-      <!-- 通用预览(未知类型) -->
-      <div v-else>
-        <p>不支持直接预览此文件类型</p>
-        <a :href="previewUrl" download>点击下载</a>
-      </div>
+          style="width: 95%"
+          v-show="currentFileType === 'image'"
+      />
+      <el-table
+          :data="tableData"
+          height="650px"
+          v-show="currentFileType === 'excel'">
+        <el-table-column
+            v-for="(header, index) in headers"
+            :key="index"
+            :prop="`col_${index}`"
+            :label="header"
+        />
+      </el-table>
+      <div v-show="currentFileType === 'word'" v-html="wordHtml" />
     </div>
   </div>
 </template>
 
-<script>
-import { ref } from 'vue';
-
-export default {
-  setup() {
-    const previewUrl = ref(null);
-    const textContent = ref('');
-    const fileType = ref('');
-
-    // 文件类型判断
-    const detectFileType = (blob) => {
-      const type = blob.type;
-
-      // 图片类型检测
-      if (type.startsWith('image/')) {
-        return 'image';
-      }
-
-      // PDF检测
-      if (type === 'application/pdf') {
-        return 'pdf';
-      }
-
-      // 文本类型检测
-      if (type.includes('text/') ||
-          type === 'application/json' ||
-          type === 'application/javascript') {
-        return 'text';
-      }
-
-      // 可通过文件签名扩展更多类型检测
-      return 'unknown';
-    };
-
-    const handleFileUpload = async (e) => {
-      const file = e.target.files[0];
-      if (!file) return;
-
-      const blob = new Blob([file], { type: file.type });
-      const type = detectFileType(blob);
-
-      // 清理之前的URL
-      if (previewUrl.value) {
-        URL.revokeObjectURL(previewUrl.value);
-      }
-
-      // 生成新的预览URL
-      previewUrl.value = URL.createObjectURL(blob);
-      fileType.value = type;
-
-      // 处理文本内容
-      if (type === 'text') {
-        const text = await file.text();
-        textContent.value = text;
-      }
-
-      // 更新响应式变量
-      e.target.value = '';
+<script setup>
+import { ref, defineExpose, computed, reactive } from 'vue';
+import { fileDownload } from "@/api/dailyFile";
+const currentFileType = ref('')
+import * as XLSX from 'xlsx'
+import { saveAs } from 'file-saver'
+import * as mammoth from 'mammoth';
+const getFileExtension = (filename, toLowerCase = true) => {
+  if (typeof filename !== 'string') return '';
+
+  // 正则匹配:匹配最后一个点之后的有效扩展名
+  const match = filename.match(/\.([^.]+)$/);
+
+  return match ? (toLowerCase ? match[1].toLowerCase() : match[1]) : '';
+}
+
+const getFileType = (ext) => {
+  const extensionMap = {
+    jpg: 'image',
+    jpeg: 'image',
+    png: 'image',
+    gif: 'image',
+    webp: 'image',
+    pdf: 'pdf',
+    doc: 'word',
+    docx: 'word',
+    xls: 'excel',
+    xlsx: 'excel',
+    ppt: 'ppt',
+    pptx: 'ppt',
+    txt: 'text',
+    csv: 'csv',
+    json: 'json',
+    zip: 'archive',
+    rar: 'archive',
+    '7z': 'archive',
+    mp3: 'audio',
+    wav: 'audio',
+    mp4: 'video',
+    mov: 'video',
+    avi: 'video'
+  }
+  return extensionMap[ext] || 'unknown'
+}
+const getFile = (data) => {
+  const fileType = getFileType(getFileExtension(data.name))
+  currentFileType.value = fileType
+  fileDownload(data).then(res => {
+    if (fileType === 'image') {
+      previewImage(res.data)
+    } else if (fileType === 'excel') {
+      previewExcel(res.data)
+    } else if (fileType === 'word') {
+      previewOffice(res.data)
     }
-
-    // 组件卸载时清理内存
-    onUnmounted(() => {
-      if (previewUrl.value) {
-        URL.revokeObjectURL(previewUrl.value);
-      }
-    });
-
-    return {
-      previewUrl,
-      isImage: computed(() => fileType.value === 'image'),
-      isPdf: computed(() => fileType.value === 'pdf'),
-      isText: computed(() => fileType.value === 'text'),
-      textContent,
-      handleFileUpload
-    };
+  })
+}
+
+const previewUrl = ref('')
+// 图片预览.....................................................
+async function previewImage(data) {
+  try {
+    previewUrl.value = URL.createObjectURL(data)
+  } catch (err) {
+    throw new Error('图片预览失败', { cause: err })
+  }
+}
+// Excel 预览...................................................
+async function previewExcel(val) {
+  // console.log(val)
+  // console.log('val',new Uint8Array(val.arrayBuffer()))
+  try {
+    const data = new Uint8Array(await val.arrayBuffer())
+    const workbook = XLSX.read(data, { type: 'array' })
+    // 获取所有工作表名
+    sheets.splice(0, sheets.length, ...workbook.SheetNames)
+
+    if (sheets.length) {
+      activeSheet.value = sheets[0]
+      renderSheet(workbook.Sheets[sheets[0]])
+    }
+  } catch (err) {
+    throw new Error('Excel 文件解析失败', { cause: err })
   }
-};
+}
+const headers = ref([])
+const tableData = ref([])
+const sheets = reactive([])
+const activeSheet = ref('')
+// 渲染指定工作表................................................
+const wordHtml = ref('')
+const renderSheet = (worksheet) => {
+  // 提取表头(第一行)
+  const headerRow = XLSX.utils.sheet_to_json(worksheet, { header: 1 })[0] || []
+  headers.value = headerRow.map(header => header || '空列')
+
+  // 提取数据(跳过表头)
+  const jsonData = XLSX.utils.sheet_to_json(worksheet, { skipHeader: 1 })
+  // 转换为表格数据格式
+  tableData.value = jsonData.map(row => {
+    const rowData = {}
+    headers.value.forEach((header, index) => {
+      rowData[`col_${index}`] = row[header] || ''
+    })
+    return rowData
+  })
+}
+
+const previewOffice = async (val) => {
+  const arrayBuffer = await val.arrayBuffer()
+  console.log('arrayBuffer', arrayBuffer)
+  const { value } = await mammoth.extractRawText({ arrayBuffer })
+  console.log('value', value)
+  wordHtml.value = value;
+}
+
+defineExpose({
+  getFile
+})
 </script>

+ 83 - 0
web/src/view/projectManage/projectDetails/components/test.vue

@@ -0,0 +1,83 @@
+<template>
+  <div class="wrap" v-loading="loading">
+    <vue-office-docx
+        v-show="fileInfo.fileType === 'docx'"
+        :src="fileInfo.filePath" style="height: 100%"
+        @rendered="rendered" @error="HandlError">
+    </vue-office-docx>
+  </div>
+</template>
+
+<script setup>
+import { defineExpose, ref } from "vue"
+import VueOfficeDocx from "@vue-office/docx";
+import { fileDownload } from "@/api/dailyFile";
+const loading = ref(false)
+const currentFileType = ref('')
+const getFileExtension = (filename, toLowerCase = true) => {
+  if (typeof filename !== 'string') return '';
+
+  // 正则匹配:匹配最后一个点之后的有效扩展名
+  const match = filename.match(/\.([^.]+)$/);
+
+  return match ? (toLowerCase ? match[1].toLowerCase() : match[1]) : '';
+}
+
+const getFileType = (ext) => {
+  const extensionMap = {
+    jpg: 'image',
+    jpeg: 'image',
+    png: 'image',
+    gif: 'image',
+    webp: 'image',
+    pdf: 'pdf',
+    doc: 'word',
+    docx: 'word',
+    xls: 'excel',
+    xlsx: 'excel',
+    ppt: 'ppt',
+    pptx: 'ppt',
+    txt: 'text',
+    csv: 'csv',
+    json: 'json',
+    zip: 'archive',
+    rar: 'archive',
+    '7z': 'archive',
+    mp3: 'audio',
+    wav: 'audio',
+    mp4: 'video',
+    mov: 'video',
+    avi: 'video'
+  }
+  return extensionMap[ext] || 'unknown'
+}
+const getFile = (data) => {
+  const fileType = getFileType(getFileExtension(data.name))
+  currentFileType.value = fileType
+  fileDownload(data).then(res => {
+    if (fileType === 'image') {
+      previewImage(res.data)
+    } else if (fileType === 'excel') {
+      previewExcel(res.data)
+    } else if (fileType === 'word') {
+      previewWord(res.data)
+    }
+  })
+}
+
+const previewImage = (data) => {}
+
+const previewExcel = (data) => {}
+
+const previewWord = (data) => {
+  const blob = new Blob([response]);
+}
+
+defineExpose({
+  getFile
+})
+</script>
+
+<style scoped>
+
+</style>

+ 19 - 9
web/src/view/projectManage/projectDetails/projectDetails.vue

@@ -341,7 +341,7 @@
                         type="primary"
                         :icon="Monitor"
                         size="large"
-                        @click="projectPreview(scope.row)"
+                        @click="openPreview(scope.row)"
                     >
                       预览
                     </el-button>
@@ -673,10 +673,12 @@
         />
       </el-row>
     </el-dialog>
-<!--    <el-dialog-->
-<!--        v-model=""-->
-<!--        title="状态和类型编辑"-->
-<!--        width="40%"></el-dialog>-->
+    <el-dialog
+        v-model="previewShow"
+        title="文件预览"
+        width="60%">
+      <preview ref="previewRef"/>
+    </el-dialog>
   </div>
 </template>
 
@@ -692,6 +694,7 @@ import { editorData } from '@/pinia/project/project'
 import { fileDownload } from '@/api/dailyFile'
 import { useRoute, useRouter } from 'vue-router'
 import { useUserStore } from '@/pinia/modules/user'
+import preview from './components/preview.vue'
 // eslint-disable-next-line no-unused-vars
 const router = useRouter()
 const route = useRoute()
@@ -792,6 +795,9 @@ const currentState = ref('')
 const allowId = reactive([1, 6, 7])
 // 当前用户id
 const currentUserId = ref(0)
+
+const previewRef = ref(null)
+
 // 方法
 onMounted(() => {
   currentUserId.value = currentUser.userInfo.ID
@@ -806,11 +812,15 @@ onMounted(() => {
   })
 })
 
-const projectPreview = (data) => {
-  fileDownload(data).then(res => {
-    console.log('返回的文件', res)
-  })
+const previewShow = ref(false)
+
+const openPreview = (data) => {
+  previewShow.value = true
+  setTimeout(function (){
+    previewRef.value.getFile(data)
+  }, 500)
 }
+
 // 初始化项目数据
 const projectMessage = (code) => {
   getProjectMessage(code).then(res => {