diff --git a/backend/defConfig/configTemp.yaml b/backend/defConfig/configTemp.yaml index e4a00f2..3338148 100644 --- a/backend/defConfig/configTemp.yaml +++ b/backend/defConfig/configTemp.yaml @@ -24,12 +24,12 @@ user: file: maxSize: 52428800 pahts: - avatar: "./data/avatar/" - image: "./data/upload/image/" - video: "./data/upload/video/" - music: "./data/upload/music/" - pdf: "./data/upload/pdf/" - other: "./data/upload/other/" + avatar: "data/static/avatar/" + image: "data/upload/image/" + video: "data/upload/video/" + music: "data/upload/music/" + pdf: "data/upload/pdf/" + other: "data/upload/other/" allowImageMime: image/jpeg: ".jpeg" diff --git a/backend/defConfig/errorCodes.json b/backend/defConfig/errorCodes.json index b0c88c6..79c05d2 100644 --- a/backend/defConfig/errorCodes.json +++ b/backend/defConfig/errorCodes.json @@ -9,6 +9,11 @@ "userEmailFormatError":-43, "userCookieError":-44, "userCookieNotFund":-44, - "userCookieExpired":-44 + "userCookieExpired":-44, + "file_mime_err":-51, + "file_size_err":-52, + "file_name_err":-53, + "file_get_err":-54 + } \ No newline at end of file diff --git a/backend/main.go b/backend/main.go index eca47a8..77a70bf 100644 --- a/backend/main.go +++ b/backend/main.go @@ -68,6 +68,17 @@ func main() { models.ConfigAllInit() routers.ApiInit() + //创建必要目录 + for _, path := range models.ConfigsFile.Pahts { + fmt.Println(path) + err := os.MkdirAll(path, 0755) + if err != nil { + fmt.Printf("创建文件夹失败: %v\n", err) + panic("创建文件夹失败") + + } + } + //启动gin服务 r := gin.Default() diff --git a/backend/models/configs.go b/backend/models/configs.go index d80bb5e..5b26106 100644 --- a/backend/models/configs.go +++ b/backend/models/configs.go @@ -4,8 +4,6 @@ import "github.com/mitchellh/mapstructure" var Configs map[string]interface{} -//mime信息转换位拓展名 - type ConfigsWeb_ struct { Host string `mapstructure:"host"` Port string `mapstructure:"port"` diff --git a/backend/models/file.go b/backend/models/file.go index d30c0f8..9fc23be 100644 --- a/backend/models/file.go +++ b/backend/models/file.go @@ -1,6 +1,13 @@ package models -import "os" +import ( + "crypto" + "encoding/hex" + "io" + "mime/multipart" + "net/http" + "os" +) // 判断文件是否存在 func FileExists(path string) bool { @@ -10,3 +17,42 @@ func FileExists(path string) bool { } return true } + +// 计算文件的哈希 +func SHA256HashFile(file_head *multipart.FileHeader) (string, error) { + // 打开文件 + file, err := file_head.Open() + if err != nil { + return "foen error", err + } + defer file.Close() + + hasher := crypto.SHA256.New() + + // 从文件流中读取并计算哈希 + _, err = io.Copy(hasher, file) + if err != nil { + return "", err + } + + hashBytes := hasher.Sum(nil) + return hex.EncodeToString(hashBytes), nil + +} + +// 获取文件mime +func GetFileMime(file_head *multipart.FileHeader) (string, error) { + file, err := file_head.Open() + if err != nil { + return "foen error", err + } + defer file.Close() + + // 读取前512字节用于MIME检测 + buffer := make([]byte, 512) + io.ReadFull(file, buffer) + mimeType := http.DetectContentType(buffer) + + return mimeType, nil + +} diff --git a/backend/routers/api.go b/backend/routers/api.go index a1278c3..52e0319 100644 --- a/backend/routers/api.go +++ b/backend/routers/api.go @@ -50,6 +50,7 @@ func SeparateData(ctx *gin.Context) (map[string]interface{}, string) { func ApiRoot(r *gin.RouterGroup) { + ApiStatic(r.Group("/static")) ApiUser(r.Group("/users")) r.GET("/", func(ctx *gin.Context) { diff --git a/backend/routers/apiStatic.go b/backend/routers/apiStatic.go new file mode 100644 index 0000000..09b82d3 --- /dev/null +++ b/backend/routers/apiStatic.go @@ -0,0 +1,25 @@ +package routers + +import ( + "ops/models" + "path" + + "github.com/gin-gonic/gin" +) + +//处理api的静态内容 + +func ApiStatic(r *gin.RouterGroup) { + r.GET("/avatar/:filename", func(ctx *gin.Context) { + filename := ctx.Param("filename") + dst := path.Join(models.ConfigsFile.Pahts["avatar"], filename) + if models.FileExists(dst) { + ctx.File(dst) + } else { + //找不到文件 + ctx.String(404, "file not found") + + } + + }) +} diff --git a/backend/routers/apiUsers.go b/backend/routers/apiUsers.go index c9fe0b4..f5d6337 100644 --- a/backend/routers/apiUsers.go +++ b/backend/routers/apiUsers.go @@ -1,8 +1,10 @@ package routers import ( + "errors" "fmt" "ops/models" + "path" "strconv" "time" @@ -80,6 +82,33 @@ type From_user_changepass struct { Newpass string `json:"newpass"` } +func AuthenticationAuthorityFromCookie(c string) (*models.TabUser_, error) { + + if c != "" { + cookie := models.TabCookie_{ + Value: c, + } + if models.DB.Where(&cookie).First(&cookie).Error == nil { + //找到cookie,验证cookie有效性,以及更新cookie + if models.CheckCookiesAndUpdate(&cookie) { + //cookie有效 + //载入user + user := models.TabUser_{ + ID: cookie.UserID, + } + models.DB.Where(&user).First(&user) + return &user, nil + } else { + return nil, errors.New("cookie 过期") + } + } else { + return nil, errors.New("cookie Not Fund") + } + } else { + return nil, errors.New("cookie 参数错误") + } +} + func AuthenticationAuthority(ctx *gin.Context) (bool, models.TabUser_, map[string]interface{}) { var user models.TabUser_ @@ -187,8 +216,92 @@ func ApiUser(r *gin.RouterGroup) { //修改用户头像 r.POST("/updateAvatar", func(ctx *gin.Context) { + cookie := ctx.PostForm("cookie") + user, err := AuthenticationAuthorityFromCookie(cookie) + if err == nil { + file, err := ctx.FormFile("file") + if err == nil { + if file.Filename != "" { + //限制文件大小 + if file.Size > 512 { + //头像裁剪过限制1M应该差不多 + if file.Size < 1048576 { + + //判断mime + mimeType, err := models.GetFileMime(file) + if err == nil { + + file_extname := models.ConfigsFile.AllowImageMime[mimeType] + if file_extname != "" { + + //haxi文件 + + file_hashi_name, err := models.SHA256HashFile(file) + if err == nil { + + dst := path.Join(models.ConfigsFile.Pahts["avatar"], file_hashi_name+file_extname) + + var is_save_ok = false + //判断文件是否存在避免重复保存 + if models.FileExists(dst) { + //fmt.Println("文件存在") + is_save_ok = true + ReturnJson(ctx, "apiOK", nil) + } else { + //fmt.Println("文件no存在") + ferr := ctx.SaveUploadedFile(file, dst) + if ferr == nil { + //文件保存成功 + //fmt.Print("save_ok") + is_save_ok = true + ReturnJson(ctx, "apiOK", nil) + } else { + fmt.Print(ferr) + ReturnJson(ctx, "postErr", nil) + } + + } + if is_save_ok { + //修改数据库内容 + var user_info_fund models.TabUserInfo_ + user_info_fund.UserID = user.ID + + var user_update_avatar models.TabUserInfo_ + user_update_avatar.AvatarPath = file_hashi_name + file_extname + + models.DB.Where(&user_info_fund).Updates(&user_update_avatar) + + } + + } else { + ReturnJson(ctx, "postErr", nil) + } + + } else { + ReturnJson(ctx, "file_mime_err", nil) + } + + } else { + ReturnJson(ctx, "postErr", nil) + } + + } else { + ReturnJson(ctx, "file_size_err", nil) + } + } else { + ReturnJson(ctx, "file_size_err", nil) + } + } else { + ReturnJson(ctx, "file_name_err", nil) + } + } else { + ReturnJson(ctx, "file_get_err", nil) + } + + } else { + ReturnJson(ctx, "userCookieError", nil) + } - ReturnJson(ctx, "jsonErr", nil) }) //更新用户info diff --git a/backend/routers/return.go b/backend/routers/return.go index 11f5830..efd890a 100644 --- a/backend/routers/return.go +++ b/backend/routers/return.go @@ -22,3 +22,13 @@ func ReturnJson(ctx *gin.Context, errMsg string, data map[string]interface{}) { //ctx.Abort() } + +func Return_file(ctx *gin.Context, file_path string, preview bool) { + if preview { + ctx.File(file_path) + } else { + //需要从数据库拉取原始文件名 + //ctx.FileAttachment(file_info.Path, file_info.Name) + } + +} diff --git a/frontent/ops_vue_js/src/i18n/en.json b/frontent/ops_vue_js/src/i18n/en.json index feae2a0..8ced4a7 100644 --- a/frontent/ops_vue_js/src/i18n/en.json +++ b/frontent/ops_vue_js/src/i18n/en.json @@ -1,4 +1,10 @@ { + "errorpage": { + "404_title": "404 Resource Not Found", + "404_msg_title": "Oops… You just found an error page", + "404_msg": "We are sorry but the page you are looking for was not found", + "404_back_home": "Take me home" + }, "appname": { "home": "Home", "login": "Login", @@ -106,9 +112,9 @@ }, "settings": { "cancel": "Cancel", - "basic_information":"Basic Information", - "contact_information":"Contact Information", - "security_settings":"Security Settings", + "basic_information": "Basic Information", + "contact_information": "Contact Information", + "security_settings": "Security Settings", "account_settings": "Account Settings", "my_account": "My Account", "profile_information": "Profile Information", diff --git a/frontent/ops_vue_js/src/i18n/zh-CN.json b/frontent/ops_vue_js/src/i18n/zh-CN.json index a1e2efe..84075c6 100644 --- a/frontent/ops_vue_js/src/i18n/zh-CN.json +++ b/frontent/ops_vue_js/src/i18n/zh-CN.json @@ -1,60 +1,66 @@ { + "errorpage": { + "404_title": "404 资源未找到", + "404_msg_title": "抱歉…您刚刚发现了一个错误页面", + "404_msg": "您所查找的页面不存在", + "404_back_home": "返回首页" + }, "appname": { "home": "主页", "login": "登录", "forgot_password": "忘记密码", "register": "注册", - "schedule":"日程", - "purchase":"采购", - "warehouse":"仓库" + "schedule": "日程", + "purchase": "采购", + "warehouse": "仓库" }, - "cropper": { + "cropper": { "select_image": "选择图片", "select_File": "选择文件", "crop_image": "裁剪图片", "cancel": "取消", - "closs": "关闭" - }, - "purchase":{ - "purchase_list":"采购列表", - "item_name":"物品名称", - "purpose":"用途", - "unit":"单位", - "quantity":"数量", - "unit_price":"单价", - "total_price":"总价", - "created_at":"创建日期", - "updated_at":"更新日期", - "status":"状态", - "completed":"已完成", - "pending":"待处理", - "show":"显示", - "entries":"个物件", - "search":"搜索", - "add_part":"添加订单", - "exp_report":"生成报告" + "closs": "关闭" + }, + "purchase": { + "purchase_list": "采购列表", + "item_name": "物品名称", + "purpose": "用途", + "unit": "单位", + "quantity": "数量", + "unit_price": "单价", + "total_price": "总价", + "created_at": "创建日期", + "updated_at": "更新日期", + "status": "状态", + "completed": "已完成", + "pending": "待处理", + "show": "显示", + "entries": "个物件", + "search": "搜索", + "add_part": "添加订单", + "exp_report": "生成报告" }, "schedule": { - "my_schedule":"我的日程", - "add_event":"添加事件", - "event_title":"事件标题", - "event_date":"事件日期", - "event_time":"事件时间", - "event_description":"事件描述", - "save_event":"保存事件", - "no_events":"没有找到事件", - "delete_event":"删除事件", - "edit_event":"编辑事件", - "tody":"今天", - "week":"本周", - "month":"本月", - "previous_month":"上月", - "next_month":"下月", - "previous_year":"上年", - "next_year":"下年" + "my_schedule": "我的日程", + "add_event": "添加事件", + "event_title": "事件标题", + "event_date": "事件日期", + "event_time": "事件时间", + "event_description": "事件描述", + "save_event": "保存事件", + "no_events": "没有找到事件", + "delete_event": "删除事件", + "edit_event": "编辑事件", + "tody": "今天", + "week": "本周", + "month": "本月", + "previous_month": "上月", + "next_month": "下月", + "previous_year": "上年", + "next_year": "下年" }, "message": { - "functionality_not_yet_developed":"功能未开发", + "functionality_not_yet_developed": "功能未开发", "hello": "你好", "welcome": "欢迎", "dark_mode": "深色模式", @@ -95,20 +101,20 @@ "user_settings": "个人资料", "preferences": "偏好设置", "administrator": "管理员", - "select_date":"选择日期", - "save_ok":"保存成功", - "change_ok":"更改成功", - "type_old_pass":"输入旧密码", - "type_new_pass":"输入新密码", - "type_cof_pass":"确认新密码", - "old_pass_incorrect":"旧密码不正确", - "confirm_password_incorrect":"确认密码不正确" + "select_date": "选择日期", + "save_ok": "保存成功", + "change_ok": "更改成功", + "type_old_pass": "输入旧密码", + "type_new_pass": "输入新密码", + "type_cof_pass": "确认新密码", + "old_pass_incorrect": "旧密码不正确", + "confirm_password_incorrect": "确认密码不正确" }, "settings": { "cancel": "取消", - "basic_information":"基本信息", - "contact_information":"联系信息", - "security_settings":"安全设置", + "basic_information": "基本信息", + "contact_information": "联系信息", + "security_settings": "安全设置", "account_settings": "个人设置", "my_account": "我的账户", "profile_information": "个人信息", @@ -122,7 +128,7 @@ "female": "女", "birthday": "生日", "admin": "管理员", - "website_settings":"网站设置", + "website_settings": "网站设置", "site_name": "网站名称", "logo_settings": "Logo设置", "site_description": "网站描述", diff --git a/frontent/ops_vue_js/src/router/index.js b/frontent/ops_vue_js/src/router/index.js index 09c875a..87e8e6a 100644 --- a/frontent/ops_vue_js/src/router/index.js +++ b/frontent/ops_vue_js/src/router/index.js @@ -13,6 +13,11 @@ const router = createRouter({ name: "home", component: HomeView, }, + { + path: "/404", + name: "404", + component: () => import("../views/404.vue"), + }, { path: "/settings/account", name: "settings account", @@ -61,7 +66,7 @@ const router = createRouter({ name: "admin", component: () => import("../views/adminView.vue"), }, - + { path: "/schedule", name: "schedule", @@ -70,13 +75,18 @@ const router = createRouter({ { path: "/purchase", name: "purchase", - component: () => import("../views/purchaseView.vue"), + component: () => import("../views/purchase/purchase.vue"), }, - { + { + path: "/purchase/addorder", + name: "purchase", + component: () => import("../views/purchase/addorder.vue"), + }, + { path: "/warehouse", name: "warehouse", component: () => import("../views/warehouse.vue"), - } + }, ], }); diff --git a/frontent/ops_vue_js/src/stores/user.js b/frontent/ops_vue_js/src/stores/user.js index c92bb62..b7a28a2 100644 --- a/frontent/ops_vue_js/src/stores/user.js +++ b/frontent/ops_vue_js/src/stores/user.js @@ -41,7 +41,7 @@ export const useUserStore = defineStore("user", () => { const getUserAvatarPath = () => { if (userInfo.value != null) { if (userInfo.value.AvatarPath != "") { - return userInfo.value.AvatarPath; + return "/api/static/avatar/"+userInfo.value.AvatarPath; } } return "/ava.svg"; diff --git a/frontent/ops_vue_js/src/views/404.vue b/frontent/ops_vue_js/src/views/404.vue new file mode 100644 index 0000000..f5ff451 --- /dev/null +++ b/frontent/ops_vue_js/src/views/404.vue @@ -0,0 +1,59 @@ + + + + + + + 404 + {{ t("errorpage.404_msg_title") }} + + {{ t("errorpage.404_msg") }} + + + + + + + + + + + {{ t("errorpage.404_back_home") }} + + + + + + + diff --git a/frontent/ops_vue_js/src/views/purchase/addorder.vue b/frontent/ops_vue_js/src/views/purchase/addorder.vue new file mode 100644 index 0000000..cfbf8a9 --- /dev/null +++ b/frontent/ops_vue_js/src/views/purchase/addorder.vue @@ -0,0 +1,21 @@ + + + +add_part + \ No newline at end of file diff --git a/frontent/ops_vue_js/src/views/purchaseView.vue b/frontent/ops_vue_js/src/views/purchase/purchase.vue similarity index 73% rename from frontent/ops_vue_js/src/views/purchaseView.vue rename to frontent/ops_vue_js/src/views/purchase/purchase.vue index 5adad5a..402b330 100644 --- a/frontent/ops_vue_js/src/views/purchaseView.vue +++ b/frontent/ops_vue_js/src/views/purchase/purchase.vue @@ -17,17 +17,15 @@ watch(locale, () => { - - {{ t('purchase.purchase_list') }} + {{ t("purchase.purchase_list") }} - - {{ t('purchase.show') }} + {{ t("purchase.show") }} { aria-label="Invoices count" /> - {{ t('purchase.entries') }} + {{ t("purchase.entries") }} - {{ t('purchase.add_part') }} - {{ t('purchase.exp_report') }} + + {{ t("purchase.add_part") }} + + + {{ t("purchase.exp_report") }} - - {{ t('purchase.search') }} + {{ t("purchase.search") }} { aria-label="Search invoice" /> - @@ -89,46 +88,39 @@ watch(locale, () => { - {{ t('purchase.item_name') }} - {{ t('purchase.purpose') }} - {{ t('purchase.unit') }} - {{ t('purchase.quantity') }} - {{ t('purchase.unit_price') }} - {{ t('purchase.total_price') }} - {{ t('purchase.created_at') }} - {{ t('purchase.updated_at') }} - {{ t('purchase.status') }} + {{ t("purchase.item_name") }} + {{ t("purchase.purpose") }} + {{ t("purchase.unit") }} + {{ t("purchase.quantity") }} + {{ t("purchase.unit_price") }} + {{ t("purchase.total_price") }} + {{ t("purchase.created_at") }} + {{ t("purchase.updated_at") }} + {{ t("purchase.status") }} - - - - 001 - - 办公室用纸 - - 办公用品 - 包 - 10 - 15.00 - 150.00 - 2024-06-01 - 2024-06-05 - - 已完成 - - - - + + + + 001 + 办公室用纸 + 办公用品 + 包 + 10 + 15.00 + 150.00 + 2024-06-01 + 2024-06-05 + 已完成 + -
{{ t("errorpage.404_msg_title") }}
+ {{ t("errorpage.404_msg") }} +