Parcourir la source

新增 日常文件

xuwenhao il y a 8 mois
Parent
commit
c328bee564

+ 24 - 1
server/api/v1/admin/file.go

@@ -2,9 +2,13 @@ package admin
 
 import (
 	"github.com/gin-gonic/gin"
+	"log"
+	"net/http"
+	"os"
 	"server/dao"
 	"server/global"
 	"server/model/common/response"
+	"strings"
 )
 
 type FileApi struct{}
@@ -73,9 +77,28 @@ func (fa *FileApi) DeleteFileGenre(c *gin.Context) {
 func (fa *FileApi) FileDownload(c *gin.Context) {
 	path := c.Query("path")
 	name := c.Query("name")
+
+	// 验证或清理文件路径(这里只是一个简单的示例)
+	if strings.Contains(path, "..") {
+		response.FailWithMessage("无效的路径", c)
+		log.Println("检测到可能的路径遍历攻击尝试")
+		return
+	}
+
+	_, err := os.Stat(path)
+	if err != nil {
+		// 如果出现错误,可能是文件或目录不存在
+		if os.IsNotExist(err) {
+			c.JSON(http.StatusNotFound, gin.H{"error": "文件不存在"})
+			return
+		}
+		// 其他错误处理
+		c.AbortWithError(http.StatusInternalServerError, err)
+		return
+	}
+
 	c.Header("Content-Type", "application/octet-stream")          // 表示是文件流,唤起浏览器下载,一般设置了这个,就要设置文件名
 	c.Header("Content-Disposition", "attachment; filename="+name) // 用来指定下载下来的文件名
 	c.Header("Content-Transfer-Encoding", "binary")               // 表示传输过程中的编码形式,乱码问题可能就是因为它
 	c.File(path)
-	response.Ok(c)
 }

+ 1 - 0
server/api/v1/admin/finance.go

@@ -217,6 +217,7 @@ func (fa *FinanceApi) CreateDailyFile(c *gin.Context) {
 		dailFile := dao.DailyFile{
 			UserId: userId,
 			Name:   dst,
+			Genre:  genreId,
 			Path:   path,
 		}
 

+ 1 - 1
server/dao/dailyFile.go

@@ -32,7 +32,7 @@ func QueryDailyFileList(limit, offset, userId, genre int, name string) (dailyFil
 	if err != nil {
 		return
 	}
-	err = db.Order("id desc").Limit(limit).Offset(offset).Preload("ProjectState").Find(&dailyFiles).Error
+	err = db.Order("id desc").Limit(limit).Offset(offset).Preload("FileGenre").Find(&dailyFiles).Error
 	return dailyFiles, total, err
 }
 

+ 1 - 0
server/initialize/gorm.go

@@ -57,6 +57,7 @@ func RegisterTables() {
 		dao.ExpensesGenre{},
 		dao.DailyExpenses{},
 		dao.DailyFeeDetails{},
+		dao.DailyFile{},
 	)
 	if err != nil {
 		global.GVA_LOG.Error("register table failed", zap.Error(err))

+ 35 - 0
web/src/api/dailyFile.js

@@ -0,0 +1,35 @@
+import service from '@/utils/request'
+// 上传日常文件
+export const createDailyFile = (data) => {
+  return service({
+    url: '/finance/createDailyFile',
+    method: 'post',
+    data
+  })
+}
+// 查询日常文件列表
+export const queryDailyFileList = (data) => {
+  return service({
+    url: '/finance/queryDailyFileList',
+    method: 'post',
+    data
+  })
+}
+
+// 删除日常文件
+export const deleteDailyFile = (data) => {
+  return service({
+    url: '/finance/deleteDailyFile',
+    method: 'delete',
+    data
+  })
+}
+
+// 文件下载
+export const fileDownload = (data) => {
+  return service({
+    url: '/file/fileDownload?name=' + data.name + '&path=' + data.path,
+    method: 'get',
+    responseType: 'blob'
+  })
+}

+ 300 - 0
web/src/view/dailyFile/dailyFile.vue

@@ -0,0 +1,300 @@
+<template>
+  <el-container class="gva-table-box">
+    <el-header>
+      <el-form
+        inline
+        :model="searchData"
+      >
+        <el-form-item label="名称">
+          <el-input v-model="searchData.name" />
+        </el-form-item>
+        <el-form-item label="类型">
+          <el-select
+            v-model.number="searchData.genre"
+            placeholder="请选择"
+            style="width: 240px"
+          >
+            <el-option
+              v-for="item in fileTypes"
+              :key="item.ID"
+              :label="item.name"
+              :value="item.ID"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="GetFileList">查询</el-button>
+          <el-button @click="ResetFileList">重置</el-button>
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="fileDialog = true">文件上传</el-button>
+        </el-form-item>
+      </el-form>
+
+    </el-header>
+    <el-main>
+      <el-table
+        :data="filesData"
+        max-height="600"
+        height="600"
+      >
+        <el-table-column
+          prop="name"
+          label="名称"
+        />
+        <el-table-column
+          prop="fileGenre.name"
+          label="类型"
+        />
+        <el-table-column
+          label="上传时间"
+        >
+          <template #default="scope">
+            {{ dayjs(scope.row.CreatedAt).format("YYYY-MM-DD HH:mm:ss") }}
+          </template>
+        </el-table-column>
+        <el-table-column label="操作">
+          <template #default="scope">
+            <el-button
+              icon="Download"
+              type="primary"
+              link
+              @click="xiazai(scope.row)"
+            >
+              下载
+            </el-button>
+            <el-button
+              icon="delete"
+              type="primary"
+              link
+              @click="removeFile(scope.row)"
+            >
+              删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="gva-pagination">
+        <el-pagination
+          :current-page="searchData.pageInfo.page"
+          :page-size="searchData.pageInfo.pageSize"
+          :page-sizes="[10, 30, 50, 100]"
+          :total="total"
+          layout="total, sizes, prev, pager, next, jumper"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange"
+        />
+      </div>
+      <el-dialog
+        v-model="fileDialog"
+        title="文件上传"
+        width="800"
+        align-center
+      >
+        <div>
+          <el-select
+            v-model.number="filesType"
+            placeholder="请选择类型"
+            style="width: 240px"
+          >
+            <el-option
+              v-for="item in fileTypes"
+              :key="item.ID"
+              :label="item.name"
+              :value="item.ID"
+            />
+          </el-select>
+          <el-upload
+            ref="uploadRef"
+            class="upload-demo"
+            drag
+            multiple
+            :auto-upload="false"
+            :file-list="files"
+            :on-change="handleChange"
+            :on-remove="handleRemove"
+          >
+            <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+            <div class="el-upload__text">
+              在这里放下文件 或 <em>点击上传</em>
+            </div>
+            <template #tip>
+              文件不能大于500MB
+            </template>
+          </el-upload>
+        </div>
+        <template #footer>
+          <div class="dialog-footer">
+            <el-button @click="fileDialog = false">取消</el-button>
+            <el-button
+              type="primary"
+              @click="submitUpload"
+            >
+              确定上传
+            </el-button>
+          </div>
+        </template>
+      </el-dialog>
+    </el-main>
+  </el-container>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { useUserStore } from '@/pinia/modules/user'
+import { createDailyFile, deleteDailyFile, fileDownload, queryDailyFileList } from '@/api/dailyFile'
+import { getFileType } from '@/api/file'
+import { dayjs, ElMessage, ElMessageBox } from 'element-plus'
+
+const userStore = useUserStore()
+const user = userStore.userInfo
+
+const filesData = ref()
+
+const searchData = ref({
+  id: user.ID,
+  name: '',
+  genre: null,
+  pageInfo: {
+    page: 1,
+    pageSize: 10
+  },
+})
+
+const total = ref(0)
+
+// 分页
+const handleSizeChange = (val) => {
+  searchData.value.pageInfo.pageSize = val
+  queryData()
+}
+
+const handleCurrentChange = (val) => {
+  searchData.value.pageInfo.page = val
+  queryData()
+}
+
+const fileTypes = ref()
+
+const queryData = async() => {
+  await queryDailyFileList(searchData.value).then(res => {
+    filesData.value = res.data.list
+    total.value = res.data.total
+  })
+  await getFileType().then(res => {
+    fileTypes.value = res.data
+  })
+}
+
+// 查询
+const GetFileList = async() => {
+  await queryDailyFileList(searchData.value).then(res => {
+    filesData.value = res.data.list
+    total.value = res.data.total
+  })
+}
+
+const ResetFileList = async() => {
+  searchData.value = {
+    id: user.ID,
+    name: '',
+    genre: null,
+    pageInfo: {
+      page: 1,
+      pageSize: 10
+    },
+  }
+  await queryData()
+}
+
+// 文件上传
+const fileDialog = ref(false)
+
+const files = ref()
+
+const filesType = ref(1)
+
+const handleChange = (file, fileList) => {
+  files.value = fileList
+}
+// 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。function(file, fileList)
+const handleRemove = (file, fileList) => {
+  files.value = fileList
+}
+
+const submitUpload = async() => {
+  // 判断是否有文件再上传
+  if (filesType.value === 0) {
+    return ElMessage.warning('请选取文件类型后再上传')
+  }
+  if (files.value.length === 0) {
+    return ElMessage.warning('请选取文件后再上传')
+  }
+  // 下面的代码将创建一个空的FormData对象:
+  const formData = new FormData()
+  // 你可以使用FormData.append来添加键/值对到表单里面;
+  files.value.forEach((file) => {
+    formData.append('file', file.raw)
+  })
+  formData.append('id', user.ID)
+  formData.append('genre', filesType.value)
+  // 自定义的接口也可以用ajax或者自己封装的接口
+  await createDailyFile(formData).then(res => {
+    if (res.code === 0) {
+      ElMessage.success('上传成功')
+    }
+  })
+  await queryData()
+  fileDialog.value = false
+}
+
+// 文件下载
+const xiazai = async(val) => {
+  await fileDownload(val).then(res => {
+    console.log(res)
+    const link = document.createElement('a')
+    const href = window.URL.createObjectURL(res) // 创建下载的链接
+    link.href = href
+    link.download = val.name // 下载后文件名
+    document.body.appendChild(link)
+    link.click() // 点击下载
+    document.body.removeChild(link) // 下载完成移除元素
+    window.URL.revokeObjectURL(href) // 释放掉blob对象
+  })
+}
+
+// 文件删除
+const removeFile = async(val) => {
+  ElMessageBox.confirm(
+    '你确定进行删除操作吗?',
+    '删除',
+    {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
+  )
+    .then(async() => {
+      await deleteDailyFile(val).then(res => {
+        if (res.code === 0) {
+          ElMessage.success('删除成功')
+        }
+      })
+      await queryData()
+    })
+    .catch(() => {
+      ElMessage({
+        type: 'info',
+        message: '取消删除',
+      })
+    })
+}
+
+onMounted(() => {
+  queryData()
+})
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 0 - 80
web/src/view/department/department.vue

@@ -130,29 +130,6 @@
           </template>
         </el-table-column>
       </el-table>
-      <div>
-        <el-upload
-          ref="uploadRef"
-          class="upload-demo"
-          drag
-          multiple
-          :auto-upload="false"
-          :file-list="files"
-          :on-change="handleChange"
-          :on-remove="handleRemove"
-        >
-          <el-icon class="el-icon--upload"><upload-filled /></el-icon>
-          <div class="el-upload__text">
-            在这里放下文件 或 <em>点击上传</em>
-          </div>
-          <template #tip>
-            action="http://localhost:8080/api/department/filesUpload"
-          </template>
-        </el-upload>
-        <el-button @click="submitUpload">上传</el-button>
-        <a href="http://localhost:8080/api/department/xiazai">下载</a>
-        <el-button @click="xiazai">下载</el-button>
-      </div>
     </el-main>
     <!--    新增-->
     <el-dialog
@@ -298,68 +275,11 @@ import {
   updateDep,
   updateDepStatus,
   updateUserDep,
-  fileUpload,
-  xia
 } from '@/api/department'
 import CustomPic from '@/components/customPic/index.vue'
 import WarningBar from '@/components/warningBar/warningBar.vue'
 import { getAllUsers } from '@/api/user'
 import { ElMessage, ElMessageBox } from 'element-plus'
-import axios from 'axios'
-
-const files = ref()
-
-const handleChange = (file, fileList) => {
-  console.log(fileList)
-  files.value = fileList
-}
-// 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。function(file, fileList)
-const handleRemove = (file, fileList) => {
-  console.log(fileList)
-  files.value = fileList
-}
-
-const submitUpload = async() => {
-  // 判断是否有文件再上传
-  console.log(files.value)
-  if (files.value.length === 0) {
-    return ElMessage.warning('请选取文件后再上传')
-  }
-  // 下面的代码将创建一个空的FormData对象:
-  const formData = new FormData()
-  // 你可以使用FormData.append来添加键/值对到表单里面;
-  files.value.forEach((file) => {
-    formData.append('file', file.raw)
-  })
-  const css = ref({
-    name: 'cs',
-    age: 12
-  })
-  var s = JSON.stringify(css.value)
-  formData.append('cs', s.toString())
-  // 自定义的接口也可以用ajax或者自己封装的接口
-  await fileUpload(formData).then(res => {
-    console.log(res)
-  })
-}
-
-// 下载
-const xiazai = async() => {
-  await xia({ code: 'LCZM202407161229' }).then(res => {
-    console.log(res)
-    const url = window.URL.createObjectURL(res)
-    console.log(url)
-    const link = document.createElement('a')
-    link.href = url
-    link.setAttribute('download', 'LCZM202407161229' + '.zip') // 设置下载文件名
-    document.body.appendChild(link)
-    link.click()
-
-    // 清理
-    link.parentNode.removeChild(link)
-    window.URL.revokeObjectURL(url)
-  })
-}
 
 const depTableData = ref([])