瀏覽代碼

新增 上传下载

xuwenhao 8 月之前
父節點
當前提交
4505e68949

+ 2 - 0
server/api/v1/admin/enter.go

@@ -5,9 +5,11 @@ import "server/service"
 type ApiGroup struct {
 	DepartmentApi
 	ProjectApi
+	FileApi
 }
 
 var (
 	departmentService = service.ServiceGroupApp.AdminServiceGroup.DepartmentService
 	projectService    = service.ServiceGroupApp.AdminServiceGroup.ProjectService
+	fileService       = service.ServiceGroupApp.AdminServiceGroup.FileService
 )

+ 81 - 0
server/api/v1/admin/file.go

@@ -0,0 +1,81 @@
+package admin
+
+import (
+	"github.com/gin-gonic/gin"
+	"server/dao"
+	"server/global"
+	"server/model/common/response"
+)
+
+type FileApi struct{}
+
+func (fa *FileApi) QueryFileGenre(c *gin.Context) {
+	genre, err := fileService.QueryFileGenre()
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("QueryFileGenre ======= " + err.Error())
+		return
+	}
+	response.OkWithData(genre, c)
+}
+
+func (fa *FileApi) CreateFileGenre(c *gin.Context) {
+	var fileGenre dao.FileGenre
+	err := c.ShouldBindJSON(&fileGenre)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("CreateFileGenre ====== " + err.Error())
+		return
+	}
+	err = fileService.CreateFileGenre(fileGenre)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("CreateFileGenre ====== " + err.Error())
+		return
+	}
+	response.OkWithMessage("新增成功", c)
+}
+
+func (fa *FileApi) UpdateFileGenre(c *gin.Context) {
+	var fileGenre dao.FileGenre
+	err := c.ShouldBindJSON(&fileGenre)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("UpdateFileGenre ====== " + err.Error())
+		return
+	}
+	err = fileService.UpdateFileGenre(fileGenre)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("UpdateFileGenre ====== " + err.Error())
+		return
+	}
+	response.OkWithMessage("修改成功", c)
+}
+
+func (fa *FileApi) DeleteFileGenre(c *gin.Context) {
+	var fileGenre dao.FileGenre
+	err := c.ShouldBindJSON(&fileGenre)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("DeleteFileGenre ====== " + err.Error())
+		return
+	}
+	err = fileService.DeleteFileGenre(fileGenre)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("DeleteFileGenre ====== " + err.Error())
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+func (fa *FileApi) FileDownload(c *gin.Context) {
+	path := c.Query("path")
+	name := c.Query("name")
+	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)
+}

+ 227 - 12
server/api/v1/admin/project.go

@@ -1,9 +1,14 @@
 package admin
 
 import (
+	"archive/zip"
+	"bytes"
 	"encoding/json"
 	"fmt"
 	"github.com/gin-gonic/gin"
+	"io"
+	"os"
+	"path/filepath"
 	"server/dao"
 	"server/global"
 	"server/model/common/request"
@@ -17,11 +22,17 @@ func (pa *ProjectApi) QueryProjectList(c *gin.Context) {
 	var search request.SearchProject
 	err := c.ShouldBindJSON(&search)
 	if err != nil {
-		response.FailWithMessage(err.Error(), c)
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("QueryProjectList ====== " + err.Error())
 		return
 	}
 
 	list, total, err := projectService.QueryProjectList(search)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("QueryProjectList ====== " + err.Error())
+		return
+	}
 
 	response.OkWithDetailed(response.PageResult{
 		List:     list,
@@ -39,12 +50,36 @@ func (pa *ProjectApi) QueryProjectByCode(c *gin.Context) {
 	}
 	project, err := projectService.QueryProjectByCode(code)
 	if err != nil {
-		response.FailWithMessage(err.Error(), c)
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("QueryProjectByCode ====== " + err.Error())
 		return
 	}
 	response.OkWithData(project, c)
 }
 
+func (pa *ProjectApi) QueryFileList(c *gin.Context) {
+	var info request.SearchProjectFileList
+	err := c.ShouldBindJSON(&info)
+	if err != nil {
+		response.FailWithMessage("错误", c)
+		global.GVA_LOG.Error("QueryFileList ====== " + err.Error())
+		return
+	}
+	list, total, err := projectService.QueryFileList(info)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("QueryFileList ====== " + err.Error())
+		return
+	}
+
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     info.PageInfo.Page,
+		PageSize: info.PageInfo.PageSize,
+	}, "获取成功", c)
+}
+
 func (pa *ProjectApi) QueryAllProject(c *gin.Context) {
 	projects, err := projectService.QueryAllProject()
 	if err != nil {
@@ -89,6 +124,39 @@ func (pa *ProjectApi) QueryCollections(c *gin.Context) {
 	response.OkWithData(collections, c)
 }
 
+func (pa *ProjectApi) QueryReimbursementList(c *gin.Context) {
+	var info request.SearchProjectList
+	err := c.ShouldBindJSON(&info)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("QueryReimbursementList ====== " + err.Error())
+		return
+	}
+	list, total, err := projectService.QueryReimbursementList(info)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("QueryReimbursementList ====== " + err.Error())
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     info.PageInfo.Page,
+		PageSize: info.PageInfo.PageSize,
+	}, "获取成功", c)
+}
+
+func (pa *ProjectApi) QueryReimbursement(c *gin.Context) {
+	code := c.Query("code")
+	reimbursement, err := projectService.QueryReimbursement(code)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("QueryReimbursement ====== " + err.Error())
+		return
+	}
+	response.OkWithData(reimbursement, c)
+}
+
 func (pa *ProjectApi) CreateProject(c *gin.Context) {
 	form, err := c.MultipartForm()
 	files := form.File["file"]
@@ -107,11 +175,10 @@ func (pa *ProjectApi) CreateProject(c *gin.Context) {
 	fmt.Println(files)
 	for _, file := range files {
 		dst := file.Filename
-		fmt.Println(file)
-		fmt.Println(file.Filename)
 		projectFile := dao.ProjectFile{
-			Name: dst,
-			Path: "./uploads/file/" + dst,
+			ProjectCode: project.Code,
+			Name:        dst,
+			Path:        "./uploads/file/" + project.Code + "/立项" + "/" + dst,
 		}
 
 		project.Files = append(project.Files, projectFile)
@@ -132,18 +199,48 @@ func (pa *ProjectApi) CreateProject(c *gin.Context) {
 }
 
 func (pa *ProjectApi) CreateProjectFile(c *gin.Context) {
-	var projectFile dao.ProjectFile
-	err := c.ShouldBindJSON(&projectFile)
+	form, err := c.MultipartForm()
 	if err != nil {
-		response.FailWithMessage(err.Error(), c)
+		response.FailWithMessage("失败", c)
 		return
 	}
-	err = projectService.CreateProjectFile(projectFile)
+	files := form.File["file"]
+
+	genre := c.PostForm("genre")
+	code := c.PostForm("code")
+	genreId, err := strconv.Atoi(genre)
 	if err != nil {
-		response.FailWithMessage(err.Error(), c)
+		response.FailWithMessage("错误", c)
+		global.GVA_LOG.Error("CreateProjectFile ====== " + err.Error())
 		return
 	}
-	response.OkWithMessage("创建成功", c)
+	fileGenre, err := fileService.QueryFileGenreById(genreId)
+	if err != nil {
+		response.FailWithMessage("错误", c)
+		global.GVA_LOG.Error("CreateProjectFile ====== " + err.Error())
+		return
+	}
+	for _, file := range files {
+		dst := file.Filename
+		projectFile := dao.ProjectFile{
+			ProjectCode: code,
+			Name:        dst,
+			Path:        "./uploads/file/" + code + "/" + fileGenre.Name + "/" + dst,
+		}
+
+		// gin 简单做了封装,拷贝了文件流
+		if err := c.SaveUploadedFile(file, "./uploads/file/"+dst); err != nil {
+			response.FailWithMessage("上传失败", c)
+			return
+		}
+		err = projectService.CreateProjectFile(projectFile)
+		if err != nil {
+			response.FailWithMessage("上传失败", c)
+			return
+		}
+	}
+
+	response.OkWithMessage("上传成功", c)
 }
 
 func (pa *ProjectApi) CreateWorkingHours(c *gin.Context) {
@@ -178,6 +275,23 @@ func (pa *ProjectApi) CreateCollection(c *gin.Context) {
 	response.OkWithMessage("创建成功", c)
 }
 
+func (pa *ProjectApi) CreateReimbursement(c *gin.Context) {
+	var reimbursement dao.Reimbursement
+	err := c.ShouldBindJSON(&reimbursement)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("CreateReimbursement ====== " + err.Error())
+		return
+	}
+	err = projectService.CreateReimbursement(reimbursement)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("CreateReimbursement ====== " + err.Error())
+		return
+	}
+	response.OkWithMessage("新建成功", c)
+}
+
 func (pa *ProjectApi) UpdateProject(c *gin.Context) {
 	var project dao.Project
 	err := c.ShouldBindJSON(&project)
@@ -224,6 +338,23 @@ func (pa *ProjectApi) UpdateCollection(c *gin.Context) {
 	response.OkWithMessage("修改成功", c)
 }
 
+func (pa *ProjectApi) UpdateReimbursement(c *gin.Context) {
+	var reimbursement dao.Reimbursement
+	err := c.ShouldBindJSON(&reimbursement)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("UpdateReimbursement ====== " + err.Error())
+		return
+	}
+	err = projectService.UpdateReimbursement(reimbursement)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("UpdateReimbursement ====== " + err.Error())
+		return
+	}
+	response.OkWithMessage("修改成功", c)
+}
+
 func (pa *ProjectApi) DeleteProject(c *gin.Context) {
 	code := c.Query("code")
 	if code == "" {
@@ -284,3 +415,87 @@ func (pa *ProjectApi) DeleteCollection(c *gin.Context) {
 	}
 	response.OkWithMessage("删除成功", c)
 }
+
+func (pa *ProjectApi) DeleteReimbursement(c *gin.Context) {
+	var reimbursement dao.Reimbursement
+	err := c.ShouldBindJSON(&reimbursement)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("DeleteReimbursement ====== " + err.Error())
+		return
+	}
+	err = projectService.DeleteReimbursement(reimbursement)
+	if err != nil {
+		response.FailWithMessage("失败", c)
+		global.GVA_LOG.Error("DeleteReimbursement ====== " + err.Error())
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+func (pa *ProjectApi) ProjectFilesDownload(c *gin.Context) {
+	var info request.SearchProjectFileList
+	err := c.ShouldBindJSON(&info)
+	if err != nil {
+		response.FailWithMessage("错误", c)
+		global.GVA_LOG.Error("ProjectFilesDownload ====== " + err.Error())
+		return
+	}
+	files, err := projectService.QueryFilesByNameOrGenre(info)
+	if err != nil {
+		response.FailWithMessage("错误", c)
+		global.GVA_LOG.Error("ProjectFilesDownload ====== " + err.Error())
+		return
+	}
+
+	zipBuffer := &bytes.Buffer{}
+	zipWriter := zip.NewWriter(zipBuffer)
+
+	for _, file := range files {
+		err := addFileToZip(zipWriter, file.Path)
+		if err != nil {
+			response.FailWithMessage("错误", c)
+			global.GVA_LOG.Error("ProjectFilesDownload ====== " + err.Error())
+			return
+		}
+	}
+
+	if err := zipWriter.Close(); err != nil {
+		response.FailWithMessage("错误", c)
+		global.GVA_LOG.Error("ProjectFilesDownload ====== " + err.Error())
+		return
+	}
+	c.Header("Content-Type", "application/zip")                               // 表示是文件流,唤起浏览器下载,一般设置了这个,就要设置文件名
+	c.Header("Content-Disposition", "attachment; filename="+info.Code+".zip") // 用来指定下载下来的文件名
+	c.Header("Content-Transfer-Encoding", "binary")                           // 表示传输过程中的编码形式,乱码问题可能就是因为它
+	c.Data(0, "application/zip", zipBuffer.Bytes())
+}
+
+// 添加文件道zip中
+func addFileToZip(w *zip.Writer, filename string) error {
+	file, err := os.Open(filename)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+
+	fileInfo, err := file.Stat()
+	if err != nil {
+		return err
+	}
+
+	header, err := zip.FileInfoHeader(fileInfo)
+	if err != nil {
+		return err
+	}
+
+	header.Name = filepath.Base(filename)
+
+	writer, err := w.CreateHeader(header)
+	if err != nil {
+		return err
+	}
+
+	_, err = io.Copy(writer, file)
+	return err
+}

+ 28 - 4
server/dao/feeDetails.go

@@ -7,8 +7,32 @@ import (
 
 type FeeDetails struct {
 	global.GVA_MODEL
-	Genre   string          `json:"genre" form:"genre" gorm:"comment:项目类型"`
-	Subject string          `json:"subject" form:"subject" gorm:"comment:科目"`
-	Remarks string          `json:"remarks" form:"remarks" gorm:"comment:备注"`
-	Price   decimal.Decimal `json:"price" form:"price" gorm:"comment:报销金额"`
+	ReimbursementId uint            `json:"reimbursementId" form:"reimbursementId" gorm:"comment:报销id"`
+	Genre           string          `json:"genre" form:"genre" gorm:"comment:项目类型"`
+	Subject         string          `json:"subject" form:"subject" gorm:"comment:科目"`
+	Remarks         string          `json:"remarks" form:"remarks" gorm:"comment:备注"`
+	Price           decimal.Decimal `json:"price" form:"price" gorm:"comment:报销金额"`
+}
+
+func (FeeDetails) TableName() string {
+	return "feeDetails"
+}
+
+// TODO:费用详情查询
+
+// QueryFeeDetails 查询费用详情
+func QueryFeeDetails(id int) (feeDetails []FeeDetails, err error) {
+	err = global.GVA_DB.Model(&FeeDetails{}).Where("reimbursement_id = ?", id).Find(&feeDetails).Error
+	return feeDetails, err
+}
+
+// TODO:费用详情新增
+
+// TODO:费用详情修改
+
+// TODO:费用详情删除
+
+// DeleteFeeDetails 删除费用详情
+func DeleteFeeDetails(id uint) error {
+	return global.GVA_DB.Unscoped().Where("reimbursement_id = ?", id).Delete(&FeeDetails{}).Error
 }

+ 50 - 0
server/dao/fileGenre.go

@@ -0,0 +1,50 @@
+package dao
+
+import (
+	"server/global"
+)
+
+type FileGenre struct {
+	global.GVA_MODEL
+	Name    string `json:"name" form:"name" gorm:"comment:文件类型名称"`
+	Remarks string `json:"remarks" form:"remarks" gorm:"comment:备注"`
+}
+
+func (FileGenre) TableName() string {
+	return "file_genre"
+}
+
+// TODO:文件类型查询
+
+// QueryFileGenre 查询项目类型
+func QueryFileGenre() (fileGenres []FileGenre, err error) {
+	err = global.GVA_DB.Model(&FileGenre{}).Find(&fileGenres).Error
+	return fileGenres, err
+}
+
+// QueryFileGenreById 查询项目类型 按id
+func QueryFileGenreById(id int) (fileGenre FileGenre, err error) {
+	err = global.GVA_DB.Model(&FileGenre{}).Where("id = ?", id).First(&fileGenre).Error
+	return fileGenre, err
+}
+
+// TODO:文件类型新增
+
+// CreateFileGenre 新增项目类型
+func (fg FileGenre) CreateFileGenre() error {
+	return global.GVA_DB.Create(&fg).Error
+}
+
+// TODO:文件类型修改
+
+// UpdateFileGenre 修改文件类型
+func (fg FileGenre) UpdateFileGenre() error {
+	return global.GVA_DB.Model(&fg).Updates(&fg).Error
+}
+
+// TODO:文件类型删除
+
+// DeleteFileGenre 删除文件类型
+func (fg FileGenre) DeleteFileGenre() error {
+	return global.GVA_DB.Unscoped().Delete(&fg).Error
+}

+ 2 - 2
server/dao/project.go

@@ -14,7 +14,7 @@ type Project struct {
 	Price        decimal.Decimal       `json:"price" form:"price" gorm:"type:decimal(10,2);not null"`
 	Level        int                   `json:"level" form:"level" gorm:"级别"`
 	Customer     string                `json:"customer" form:"customer" gorm:"相关客户"`
-	Illustrate   string                `json:"illustrate" form:"illustrate" gorm:"comment:说明"`
+	Illustrate   string                `json:"illustrate" form:"illustrate" gorm:"comment:说明;type:varchar(200)"`
 	State        int                   `json:"state" form:"state" gorm:"comment:状态"`
 	EndTime      string                `json:"endTime" form:"endTime" gorm:"comment:结束时间"`
 	Files        []ProjectFile         `json:"files" form:"files" gorm:"-"`
@@ -29,7 +29,7 @@ func (Project) TableName() string {
 // TODO:项目查询
 
 // QueryProjectList 查询项目列表
-func QueryProjectList(limit, offset, state int, name, time string) (project []*Project, total int64, err error) {
+func QueryProjectList(limit, offset, state int, name, time string) (project []Project, total int64, err error) {
 	// 创建db
 	db := global.GVA_DB.Model(&Project{})
 	// 如果有条件搜索 下方会自动创建搜索语句

+ 40 - 5
server/dao/projectFile.go

@@ -6,10 +6,11 @@ import (
 
 type ProjectFile struct {
 	global.GVA_MODEL
-	ProjectCode string `json:"projectCode" from:"projectCode" gorm:"comment:项目编号"`
-	Name        string `json:"name" from:"name" gorm:"comment:文件名称"`
-	Suffix      string `json:"suffix" from:"suffix" gorm:"comment:文件后缀"`
-	Path        string `json:"path" from:"path" gorm:"comment:路径"`
+	ProjectCode string    `json:"projectCode" from:"projectCode" gorm:"comment:项目编号"`
+	Name        string    `json:"name" from:"name" gorm:"comment:文件名称"`
+	Genre       int       `json:"genre" form:"genre" gorm:"comment:项目类型"`
+	FileGenre   FileGenre `json:"fileGenre" form:"fileGenre" gorm:"foreignKey:genre;references:id;"`
+	Path        string    `json:"path" from:"path" gorm:"comment:路径"`
 }
 
 func (ProjectFile) TableName() string {
@@ -20,7 +21,41 @@ func (ProjectFile) TableName() string {
 
 // QueryFilesByCode 查询项目文件 按项目code
 func QueryFilesByCode(code string) (files []ProjectFile, err error) {
-	err = global.GVA_DB.Model(&ProjectFile{}).Where("project_code = ?", code).Find(&files).Error
+	err = global.GVA_DB.Model(&ProjectFile{}).Where("project_code = ?", code).Preload("FileGenre").Find(&files).Error
+	return files, err
+}
+
+// QueryFileList 查询项目文件列表
+func QueryFileList(limit, offset, genre int, code, name string) (files []ProjectFile, total int64, err error) {
+	// 创建db
+	db := global.GVA_DB.Model(&ProjectFile{}).Where("project_code = ?", code)
+	// 如果有条件搜索 下方会自动创建搜索语句
+	if genre != 0 {
+		db = db.Where("genre = ?", genre)
+	}
+	if name != "" {
+		db = db.Where("name LIKE ?", "%"+name+"%")
+	}
+	err = db.Count(&total).Error
+	if err != nil {
+		return
+	}
+	err = db.Order("id desc").Limit(limit).Offset(offset).Find(&files).Error
+	return files, total, err
+}
+
+// QueryFilesByNameOrGenre 查询项目文件 按名称或类型
+func QueryFilesByNameOrGenre(genre int, code, name string) (files []ProjectFile, err error) {
+	// 创建db
+	db := global.GVA_DB.Model(&ProjectFile{}).Where("project_code = ?", code)
+	// 如果有条件搜索 下方会自动创建搜索语句
+	if genre != 0 {
+		db = db.Where("genre = ?", genre)
+	}
+	if name != "" {
+		db = db.Where("name LIKE ?", "%"+name+"%")
+	}
+	err = db.Order("id desc").Find(&files).Error
 	return files, err
 }
 

+ 3 - 3
server/dao/projectWorkingHours.go

@@ -8,8 +8,8 @@ import (
 type ProjectWorkingHours struct {
 	global.GVA_MODEL
 	ProjectCode string          `json:"projectCode" form:"projectCode" gorm:"comment:项目编号"`
-	People      int             `json:"people" form:"people" gorm:"comment:报工人"`
-	Days        int             `json:"days" form:"days" gorm:"not null;comment:天数"`
+	People      string          `json:"people" form:"people" gorm:"comment:报工人"`
+	Days        float64         `json:"days" form:"days" gorm:"type:double(5,1);not null;comment:天数"`
 	Price       decimal.Decimal `json:"price" form:"price" gorm:"type:decimal(10,2);not null;comment:人天单价"`
 }
 
@@ -26,7 +26,7 @@ func QueryWorkingHours(code string) (workingHours []ProjectWorkingHours, err err
 }
 
 // QueryWorkingHoursByCode 查询单个工时 按id
-func QueryWorkingHoursByCode(code string, people int) (workingHours ProjectWorkingHours, err error) {
+func QueryWorkingHoursByCode(code string, people string) (workingHours ProjectWorkingHours, err error) {
 	err = global.GVA_DB.Model(&ProjectWorkingHours{}).Where("code = ? and people = ?", code, people).First(&workingHours).Error
 	return workingHours, err
 }

+ 49 - 5
server/dao/reimbursement.go

@@ -2,6 +2,7 @@ package dao
 
 import (
 	"github.com/shopspring/decimal"
+	"gorm.io/gorm"
 	"server/global"
 )
 
@@ -23,8 +24,19 @@ func (Reimbursement) TableName() string {
 // TODO:报销查询
 
 // QueryReimbursementList 查询报销
-func QueryReimbursementList() {
-
+func QueryReimbursementList(limit, offset int, code, name string) (reimbursement []Reimbursement, total int64, err error) {
+	// 创建db
+	db := global.GVA_DB.Model(&Project{}).Where("project_code = ?", code)
+	// 如果有条件搜索 下方会自动创建搜索语句
+	if name != "" {
+		db = db.Where("name LIKE ?", "%"+name+"%")
+	}
+	err = db.Count(&total).Error
+	if err != nil {
+		return
+	}
+	err = db.Order("id desc").Limit(limit).Offset(offset).Find(&reimbursement).Error
+	return reimbursement, total, err
 }
 
 func QueryReimbursement(code string) (reimbursement []Reimbursement, err error) {
@@ -36,19 +48,51 @@ func QueryReimbursement(code string) (reimbursement []Reimbursement, err error)
 
 // CreateReimbursement 新增报销单
 func (r Reimbursement) CreateReimbursement() error {
-	return global.GVA_DB.Create(&r).Error
+	return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
+		txErr := tx.Create(&r).Error
+		if txErr != nil {
+			return txErr
+		}
+		for _, feeDetail := range r.FeeDetails {
+			txErr := tx.Create(&feeDetail).Error
+			if txErr != nil {
+				return txErr
+			}
+		}
+		return txErr
+	})
 }
 
 // TODO:报销修改
 
 // UpdateReimbursement 修改报销单
 func (r Reimbursement) UpdateReimbursement() error {
-	return global.GVA_DB.Model(&r).Updates(&r).Error
+	return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
+		txErr := tx.Unscoped().Where("reimbursement_id = ?", r.ID).Delete(&FeeDetails{}).Error
+		if txErr != nil {
+			return txErr
+		}
+		txErr = tx.Model(&r).Updates(&r).Error
+		if txErr != nil {
+			return txErr
+		}
+		return txErr
+	})
 }
 
 // TODO:报销删除
 
 // DeleteReimbursement 删除报销单
 func (r Reimbursement) DeleteReimbursement() error {
-	return global.GVA_DB.Unscoped().Delete(&r).Error
+	return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
+		txErr := tx.Unscoped().Where("reimbursement_id = ?", r.ID).Delete(&FeeDetails{}).Error
+		if txErr != nil {
+			return txErr
+		}
+		txErr = tx.Unscoped().Delete(&r).Error
+		if txErr != nil {
+			return txErr
+		}
+		return txErr
+	})
 }

+ 1 - 0
server/initialize/gorm.go

@@ -49,6 +49,7 @@ func RegisterTables() {
 		dao.Department{},
 		dao.Project{},
 		dao.ProjectFile{},
+		dao.FileGenre{},
 		dao.ProjectWorkingHours{},
 		dao.Collection{},
 	)

+ 1 - 0
server/initialize/router.go

@@ -89,6 +89,7 @@ func Routers() *gin.Engine {
 
 		adminRouter.InitDepartmentRouter(PublicGroup)
 		adminRouter.InitProjectRouter(PrivateGroup)
+		adminRouter.InitFileRouter(PrivateGroup)
 	}
 
 	global.GVA_LOG.Info("router register success")

+ 13 - 0
server/model/common/request/common.go

@@ -37,4 +37,17 @@ type SearchProject struct {
 	State    int      `json:"state" form:"state"`
 }
 
+type SearchProjectList struct {
+	PageInfo PageInfo `json:"pageInfo" form:"pageInfo"`
+	Code     string   `json:"code" form:"code"`
+	Name     string   `json:"name" form:"name"`
+}
+
+type SearchProjectFileList struct {
+	PageInfo PageInfo `json:"pageInfo" form:"pageInfo"`
+	Code     string   `json:"code" form:"code"`
+	Name     string   `json:"name" form:"name"`
+	Genre    int      `json:"genre" form:"genre"`
+}
+
 type Empty struct{}

+ 24 - 0
server/router/admin/file.go

@@ -0,0 +1,24 @@
+package admin
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "server/api/v1"
+	"server/middleware"
+)
+
+type FileRouter struct{}
+
+func (s *ProjectRouter) InitFileRouter(Router *gin.RouterGroup) {
+	fileRouter := Router.Group("file").Use(middleware.OperationRecord())
+	fileRouterWithoutRecord := Router.Group("file")
+	fileApi := v1.ApiGroupApp.AdminApiGroup.FileApi
+	{
+		fileRouter.POST("createFileGenre", fileApi.CreateFileGenre)
+		fileRouter.PUT("updateFileGenre", fileApi.UpdateFileGenre)
+		fileRouter.DELETE("deleteFileGenre", fileApi.DeleteFileGenre)
+	}
+	{
+		fileRouterWithoutRecord.GET("queryFileGenre", fileApi.QueryFileGenre)
+		fileRouterWithoutRecord.GET("fileDownload", fileApi.FileDownload)
+	}
+}

+ 7 - 0
server/router/admin/project.go

@@ -17,22 +17,29 @@ func (s *ProjectRouter) InitProjectRouter(Router *gin.RouterGroup) {
 		projectRouter.POST("createProjectFile", projectApi.CreateProjectFile)
 		projectRouter.POST("createWorkingHours", projectApi.CreateWorkingHours)
 		projectRouter.POST("createCollection", projectApi.CreateCollection)
+		projectRouter.POST("createReimbursement", projectApi.CreateReimbursement)
 
 		projectRouter.PUT("updateProject", projectApi.UpdateProject)
 		projectRouter.PUT("updateWorkingHours", projectApi.UpdateWorkingHours)
 		projectRouter.PUT("updateCollection", projectApi.UpdateCollection)
+		projectRouter.PUT("updateReimbursement", projectApi.UpdateReimbursement)
 
 		projectRouter.DELETE("deleteProject", projectApi.DeleteProject)
 		projectRouter.DELETE("deleteProjectFile", projectApi.DeleteProjectFile)
 		projectRouter.DELETE("deleteWorkingHours", projectApi.DeleteWorkingHours)
 		projectRouter.DELETE("deleteCollection", projectApi.DeleteCollection)
+		projectRouter.DELETE("deleteReimbursement", projectApi.DeleteReimbursement)
 	}
 	{
 		projectRouterWithoutRecord.POST("queryProjectList", projectApi.QueryProjectList)
 		projectRouterWithoutRecord.GET("queryProjectByCode", projectApi.QueryProjectByCode)
+		projectRouterWithoutRecord.POST("queryFileList", projectApi.QueryFileList)
 		projectRouterWithoutRecord.GET("queryAllProject", projectApi.QueryAllProject)
 		projectRouterWithoutRecord.GET("queryProjectsInfo", projectApi.QueryProjectsInfo)
 		projectRouterWithoutRecord.GET("queryWorkingHours", projectApi.QueryWorkingHours)
 		projectRouterWithoutRecord.GET("queryCollection", projectApi.QueryCollections)
+		projectRouterWithoutRecord.GET("queryReimbursementList", projectApi.QueryReimbursementList)
+		projectRouterWithoutRecord.GET("queryReimbursement", projectApi.QueryReimbursement)
+		projectRouterWithoutRecord.POST("projectFilesDownload", projectApi.ProjectFilesDownload)
 	}
 }

+ 1 - 0
server/service/admin/enter.go

@@ -3,4 +3,5 @@ package admin
 type ServiceGroup struct {
 	DepartmentService
 	ProjectService
+	FileService
 }

+ 25 - 0
server/service/admin/file.go

@@ -0,0 +1,25 @@
+package admin
+
+import "server/dao"
+
+type FileService struct{}
+
+func (fs *FileService) QueryFileGenre() ([]dao.FileGenre, error) {
+	return dao.QueryFileGenre()
+}
+
+func (fs *FileService) QueryFileGenreById(id int) (dao.FileGenre, error) {
+	return dao.QueryFileGenreById(id)
+}
+
+func (fs *FileService) CreateFileGenre(fileGenre dao.FileGenre) error {
+	return fileGenre.CreateFileGenre()
+}
+
+func (fs *FileService) UpdateFileGenre(fileGenre dao.FileGenre) error {
+	return fileGenre.UpdateFileGenre()
+}
+
+func (fs *FileService) DeleteFileGenre(fileGenre dao.FileGenre) error {
+	return fileGenre.DeleteFileGenre()
+}

+ 44 - 0
server/service/admin/project.go

@@ -29,6 +29,17 @@ func (ps *ProjectService) QueryProjectByCode(code string) (project dao.Project,
 	return project, err
 }
 
+func (ps *ProjectService) QueryFileList(info request.SearchProjectFileList) (list interface{}, total int64, err error) {
+	limit := info.PageInfo.PageSize
+	offset := info.PageInfo.PageSize * (info.PageInfo.Page - 1)
+	files, total, err := dao.QueryFileList(limit, offset, info.Genre, info.Code, info.Name)
+	return files, total, err
+}
+
+func (ps *ProjectService) QueryFilesByNameOrGenre(info request.SearchProjectFileList) ([]dao.ProjectFile, error) {
+	return dao.QueryFilesByNameOrGenre(info.Genre, info.Code, info.Name)
+}
+
 func (ps *ProjectService) QueryAllProject() ([]dao.Project, error) {
 	return dao.QueryAllProject()
 }
@@ -59,6 +70,27 @@ func (ps *ProjectService) QueryCollections(code string) ([]dao.Collection, error
 	return dao.QueryCollection(code)
 }
 
+func (ps *ProjectService) QueryReimbursementList(info request.SearchProjectList) (list interface{}, total int64, err error) {
+	limit := info.PageInfo.PageSize
+	offset := info.PageInfo.PageSize * (info.PageInfo.Page - 1)
+	return dao.QueryReimbursementList(limit, offset, info.Code, info.Name)
+}
+
+func (ps *ProjectService) QueryReimbursement(code string) ([]dao.Reimbursement, error) {
+	reimbursements, err := dao.QueryReimbursement(code)
+	if err != nil {
+		return nil, err
+	}
+	for i, reimbursement := range reimbursements {
+		details, err := dao.QueryFeeDetails(int(reimbursement.ID))
+		if err != nil {
+			return nil, err
+		}
+		reimbursements[i].FeeDetails = details
+	}
+	return reimbursements, err
+}
+
 func (ps *ProjectService) CreateProject(project dao.Project) error {
 	return project.CreateProject()
 }
@@ -90,6 +122,10 @@ func (ps *ProjectService) CreateCollection(collection dao.Collection) error {
 	return collection.CreateCollection()
 }
 
+func (ps *ProjectService) CreateReimbursement(reimbursement dao.Reimbursement) error {
+	return reimbursement.CreateReimbursement()
+}
+
 func (ps *ProjectService) UpdateProject(project dao.Project) error {
 	return project.UpdateProject()
 }
@@ -102,6 +138,10 @@ func (ps *ProjectService) UpdateCollection(collection dao.Collection) error {
 	return collection.UpdateCollection()
 }
 
+func (ps *ProjectService) UpdateReimbursement(reimbursement dao.Reimbursement) error {
+	return reimbursement.UpdateReimbursement()
+}
+
 func (ps *ProjectService) DeleteProject(code string) error {
 	return dao.DeleteProject(code)
 }
@@ -117,3 +157,7 @@ func (ps *ProjectService) DeleteWorkingHours(workingHours dao.ProjectWorkingHour
 func (ps *ProjectService) DeleteCollection(collection dao.Collection) error {
 	return collection.DeleteCollection()
 }
+
+func (ps *ProjectService) DeleteReimbursement(reimbursement dao.Reimbursement) error {
+	return reimbursement.DeleteReimbursement()
+}

+ 5 - 3
web/src/api/department.js

@@ -61,9 +61,11 @@ export const fileUpload = (data) => {
   })
 }
 
-export const xia = () => {
+export const xia = (data) => {
   return service({
-    url: '/department/xiazai',
-    method: 'get'
+    url: '/project/projectFilesDownload',
+    method: 'post',
+    responseType: 'blob',
+    data: data
   })
 }

+ 37 - 21
web/src/view/department/department.vue

@@ -345,30 +345,46 @@ const submitUpload = async() => {
 
 // 下载
 const xiazai = async() => {
-  try {
-    const response = await axios({
-      method: 'get',
-      url: 'http://localhost:8080/api/department/xiazai?api_key=' + Math.floor(Math.random() * 1000000),
-      responseType: 'blob'
-    })
-
-    const blob = new Blob([response.data], {
-      type: 'application/png' // 根据实际文件类型调整
-    })
-
-    const downloadUrl = window.URL.createObjectURL(blob)
-
+  await xia({ code: 'LCZM202407161229' }).then(res => {
+    console.log(res)
+    const url = window.URL.createObjectURL(new Blob([res.data]))
     const link = document.createElement('a')
-    link.href = downloadUrl
-    // link.download = response.headers[fileNameHeader] || 'default-filename.png' // 如果没有提供文件名,使用默认名
-    link.download = '04-原理篇.zip'
+    link.href = url
+    link.setAttribute('download', 'LCZM202407161229' + '.zip') // 设置下载文件名
     document.body.appendChild(link)
     link.click()
-    document.body.removeChild(link)
-    window.URL.revokeObjectURL(downloadUrl)
-  } catch (error) {
-    console.error('Download failed:', error)
-  }
+
+    // 清理
+    link.parentNode.removeChild(link)
+    window.URL.revokeObjectURL(url)
+  })
+  // try {
+  //   const response = await axios({
+  //     method: 'post',
+  //     url: 'http://localhost:8080/api/project/xiazai',
+  //     data: {
+  //       code: 'LCZH202407091055'
+  //     },
+  //     responseType: 'blob'
+  //   })
+  //
+  //   const blob = new Blob([response.data], {
+  //     type: 'application/png' // 根据实际文件类型调整
+  //   })
+  //
+  //   const downloadUrl = window.URL.createObjectURL(blob)
+  //
+  //   const link = document.createElement('a')
+  //   link.href = downloadUrl
+  //   // link.download = response.headers[fileNameHeader] || 'default-filename.png' // 如果没有提供文件名,使用默认名
+  //   link.download = '04-原理篇.zip'
+  //   document.body.appendChild(link)
+  //   link.click()
+  //   document.body.removeChild(link)
+  //   window.URL.revokeObjectURL(downloadUrl)
+  // } catch (error) {
+  //   console.error('Download failed:', error)
+  // }
 }
 
 const depTableData = ref([])