Ver código fonte

增加材料清单

2545307760@qq.com 9 meses atrás
pai
commit
2d1659f43c

+ 8 - 0
web/src/api/finance.js

@@ -173,3 +173,11 @@ export const createWorkingHours = (data) => {
     data
   })
 }
+
+export const queryProject = (data) => {
+  return service({
+    url: '/project/createWorkingHours',
+    method: 'post',
+    data
+  })
+}

+ 36 - 0
web/src/api/material.js

@@ -0,0 +1,36 @@
+import service from '@/utils/request'
+// 查询项目材料列表
+export const queryProjectMaterial = (data) => {
+    return service({
+        url: '/project/queryProjectMaterialsList',
+        method: 'GET',
+        params: data
+    })
+}
+
+// 新增项目材料
+export const createProjectMaterial = (data) => {
+    return service({
+        url: '/project/createProjectMaterials',
+        method: 'POST',
+        data: data
+    })
+}
+
+// 修改项目材料
+export const updateProjectMaterial = (data) => {
+    return service({
+        url: '/project/updateProjectMaterials',
+        method: 'PUT',
+        data: data
+    })
+}
+
+// 删除项目材料
+export const deleteProjectMaterial = (id) => {
+    return service({
+        url: '/project/deleteProjectMaterials?id=' + id,
+        method: 'delete'
+    })
+}
+

+ 5 - 5
web/src/utils/request.js

@@ -78,11 +78,11 @@ service.interceptors.response.use(
       }
       return response.data
     } else {
-      ElMessage({
-        showClose: true,
-        message: response.data.msg || decodeURI(response.headers.msg),
-        type: 'error'
-      })
+      // ElMessage({
+      //   showClose: true,
+      //   message: response.data.msg || decodeURI(response.headers.msg),
+      //   type: 'error'
+      // })
       return response.data.msg ? response.data : response
     }
   },

+ 27 - 78
web/src/view/finance/financeAnalysis/components/material.vue

@@ -1,84 +1,33 @@
 <template>
-  <div style="display: flex;">
-    <el-upload
-        action=""
-        :auto-upload="false"
-        :show-file-list="false"
-        :on-change="handleFileChange"
-        accept=".xlsx, .xls"
-    >
-      <el-button type="primary">导入Excel</el-button>
-    </el-upload>
-
-    <el-button type="success" @click="exportExcel" style="margin-left: 40px">导出Excel</el-button>
-  </div>
-  <div style="height: 20px"></div>
-  <el-table :data="fileList">
-    <el-table-column></el-table-column>
-  </el-table>
-  <el-drawer
-      v-model="detailShow"
-      title="材料详情"
-      direction="rtl"
-  >
-    <el-table :data="tableData" border style="width: 1800px" height="700px">
-      <el-table-column
-          v-for="column in tableColumns"
-          :key="column.prop"
-          :prop="column.prop"
-          :label="column.label"
-      />
-    </el-table>
-  </el-drawer>
-  <div style="height: 30px"></div>
+  <div>{{ id.currentCode }}</div>
 </template>
 
 <script setup>
-import { ref } from 'vue';
-import { ElUpload, ElButton, ElMessage } from 'element-plus';
-import * as XLSX from 'xlsx';
-const fileList = reactive([])
-
-const loading = ref(false);
-const tableColumns = ref([]);
-const tableData = ref([]);
-const detailShow = ref(false)
-
-const handleFileChange = (file) => {
-  const reader = new FileReader();
-  reader.onload = (e) => {
-    try {
-      const data = new Uint8Array(e.target.result);
-      const workbook = XLSX.read(data, { type: 'array' });
-      const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
-      const jsonData = XLSX.utils.sheet_to_json(firstSheet);
-      // console.log('data', data)
-      // console.log('workbook', workbook)
-      // console.log('firstSheet', firstSheet)
-      // console.log('jsonData', jsonData)
-
-      if (jsonData.length === 0) throw new Error('Excel文件为空');
-
-      // 动态生成表头
-      tableColumns.value = Object.keys(jsonData[0]).map(key => ({
-        prop: key,
-        label: key.replace(/_/g, ' '),
-      }));
-      tableData.value = jsonData;
-      ElMessage.success('导入成功');
-    } catch (error) {
-      ElMessage.error(`解析失败: ${error.message}`);
-    }
-  };
-  reader.readAsArrayBuffer(file.raw);
-  reader.onloadstart = () => loading.value = true;
-  reader.onloadend = () => loading.value = false;
+import { codeOperate } from '@/pinia/code/code'
+import { queryProjectMaterial } from '@/api/material'
+import { defineExpose, reactive } from "vue";
+const id = codeOperate()
+console.log('当前code', id.currentCode)
+const condition = reactive({
+  pageInfo: {
+    page: 1,
+    pageSize: 100
+  },
+  code: "",
+  name: ""
+})
+const getProjectMaterial = (code) => {
+  condition.code = code
+  // queryProjectMaterial(condition).then(res => {
+  //   if (res.code === 0) {
+  //     console.log(res.data)
+  //   }
+  // })
 }
-
-const exportExcel = () => {
-  const worksheet = XLSX.utils.json_to_sheet(tableData.value)
-  const workbook = XLSX.utils.book_new()
-  XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
-  XLSX.writeFile(workbook, '导出数据.xlsx')
-};
+defineExpose({
+  getProjectMaterial
+})
 </script>
+
+<style scoped>
+</style>

+ 4 - 2
web/src/view/finance/financeAnalysis/financeAnalysis.vue

@@ -162,7 +162,7 @@
           <el-tab-pane
               label="材料清单"
               name="5">
-              <Material/>
+              <Material ref="materialOut"/>
           </el-tab-pane>
         </el-tabs>
       </el-col>
@@ -270,7 +270,6 @@ import { codeOperate } from '@/pinia/code/code'
 import { reimburseOperate } from '@/pinia/reimbursement/Reimbursement'
 import { hourOperate } from '@/pinia/hour/Hour'
 import { payData } from '@/pinia/cost/cost'
-// eslint-disable-next-line no-unused-vars
 import Payment from '@/view/finance/financeAnalysis/components/payment.vue'
 import expense from '@/assets/expenses.png'
 import { queryProjectMonthFee } from '@/api/cost'
@@ -392,6 +391,8 @@ const hourOut = ref()
 const sumOut = ref()
 
 const costOut = ref()
+
+const materialOut = ref(null)
 const getMainMessage = (code) => {
   queryProjectExpense(code).then(res => {
     if (res.code === 0) {
@@ -423,6 +424,7 @@ const getMainMessage = (code) => {
       pay.changeCostTotal(summaryData.projectFeeTotal)
       sumOut.value.querySum()
       costOut.value.outsideChangePage()
+      materialOut.value.getProjectMaterial(code)
     }
   })
   getProjectMessage(code).then(res => {

+ 106 - 0
web/src/view/projectManage/projectDetails/components/preview.vue

@@ -0,0 +1,106 @@
+<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"
+          :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>
+    </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 = '';
+    }
+
+    // 组件卸载时清理内存
+    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
+    };
+  }
+};
+</script>

+ 23 - 4
web/src/view/projectManage/projectDetails/projectDetails.vue

@@ -336,6 +336,15 @@
                   fixed="right"
                 >
                   <template #default="scope">
+                    <el-button
+                        text
+                        type="primary"
+                        :icon="Monitor"
+                        size="large"
+                        @click="projectPreview(scope.row)"
+                    >
+                      预览
+                    </el-button>
                     <el-button
                       text
                       type="primary"
@@ -664,6 +673,10 @@
         />
       </el-row>
     </el-dialog>
+<!--    <el-dialog-->
+<!--        v-model=""-->
+<!--        title="状态和类型编辑"-->
+<!--        width="40%"></el-dialog>-->
   </div>
 </template>
 
@@ -672,7 +685,7 @@ import { getProjectMessage, editProject } from '@/api/project'
 import { onMounted, reactive, ref } from 'vue'
 import { getAllUsers } from '@/api/user'
 import { queryFileList, createProjectFile, downloadProjectFile, deleteProjectFile, deleteProjectFiles } from '@/api/file'
-import { Download, Upload, Delete, Search, RefreshLeft, Edit } from '@element-plus/icons-vue'
+import { Download, Upload, Delete, Search, RefreshLeft, Edit, Monitor } from '@element-plus/icons-vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import OptionEdit from './components/optionEdit.vue'
 import { editorData } from '@/pinia/project/project'
@@ -793,6 +806,11 @@ onMounted(() => {
   })
 })
 
+const projectPreview = (data) => {
+  fileDownload(data).then(res => {
+    console.log('返回的文件', res)
+  })
+}
 // 初始化项目数据
 const projectMessage = (code) => {
   getProjectMessage(code).then(res => {
@@ -937,7 +955,7 @@ const downloadTips = () => {
       }
       downloadProjectFile(data).then(res => {
         const link = document.createElement('a')
-        const href = window.URL.createObjectURL(res) // 创建下载的链接
+        const href = window.URL.createObjectURL(res.data) // 创建下载的链接
         link.href = href
         link.download = route.query.code + '.zip' // 下载后文件名
         document.body.appendChild(link)
@@ -953,12 +971,12 @@ const downloadTips = () => {
       })
     })
 }
-
 // 单文件下载
 const oneDownload = async(val) => {
   await fileDownload(val).then(res => {
+    console.log('返回结果', res)
     const link = document.createElement('a')
-    const href = window.URL.createObjectURL(res) // 创建下载的链接
+    const href = window.URL.createObjectURL(res.data) // 创建下载的链接
     link.href = href
     link.download = val.name // 下载后文件名
     document.body.appendChild(link)
@@ -1078,3 +1096,4 @@ const openOptionEdit = () => {
   }
 }
 </style>
+