mirror of https://gitee.com/godoos/godoos.git
17 changed files with 1364 additions and 574 deletions
@ -1,71 +1,158 @@ |
|||||
<script setup> |
|
||||
import { ref } from "vue"; |
|
||||
import { useChatStore } from "@/stores/chat"; |
|
||||
const store = useChatStore() |
|
||||
|
|
||||
</script> |
|
||||
|
|
||||
<template> |
<template> |
||||
<div v-for="item in store.chatList"> |
<div v-if="store.chatList.length > 0" v-for="item in store.chatList" :key="item.id"> |
||||
<div class="list-item" @click="store.changeChatList(item)" |
<div |
||||
:style="{ backgroundColor: item.id === store.targetUserId ? '#C4C4C4' : '' }"> |
class="list-item" |
||||
|
@click="store.changeChatList(item.id)" |
||||
|
:style="{ |
||||
|
backgroundColor: item.id === store.targetUserId ? '#C4C4C4' : '', |
||||
|
}" |
||||
|
> |
||||
<el-row> |
<el-row> |
||||
<el-col :span="6"> |
<el-col :span="6"> |
||||
<el-avatar shape="square" :size="40" class="avatar" :src="item.avatar" /> |
<el-avatar |
||||
|
shape="square" |
||||
|
:size="40" |
||||
|
class="avatar" |
||||
|
:src="item.avatar" |
||||
|
/> |
||||
</el-col> |
</el-col> |
||||
<el-col :span="18"> |
<el-col :span="18" class="preview"> |
||||
<el-row> |
<el-row class="preview-content"> |
||||
<el-col :span="18"> |
<el-col :span="18" class="preview-left"> |
||||
<div class="previewName">{{ item.name }}</div> |
<div class="previewName">{{ item.nickname }}</div> |
||||
|
<div class="previewChat"> |
||||
|
<span v-if="item.previewType === 0">{{ item.previewMessage }}</span> |
||||
|
<span v-if="item.previewType === 1"> |
||||
|
{{ |
||||
|
item.targetUserId === id |
||||
|
? "你" |
||||
|
: '"' + item.nickname + '"' |
||||
|
}}撤回了一条消息 |
||||
|
</span> |
||||
|
</div> |
||||
</el-col> |
</el-col> |
||||
<el-col :span="6"> |
<el-col :span="6" class="preview-right"> |
||||
<div class="previewTime">{{ item.previewTimeFormat }}</div> |
<div class="previewTime"> |
||||
|
{{ item.previewTimeFormat }} |
||||
|
</div> |
||||
</el-col> |
</el-col> |
||||
</el-row> |
</el-row> |
||||
<el-row> |
|
||||
<div v-if="item.previewType === 0" class="previewChat">{{ item.previewMessage }}</div> |
|
||||
<div v-if="item.previewType === 1" class="previewChat">{{ item.userId === id ? "你" : "\"" + item.name + |
|
||||
"\""}}撤回了一条消息</div> |
|
||||
</el-row> |
|
||||
</el-col> |
</el-col> |
||||
</el-row> |
</el-row> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
|
<div v-else class="emptyChat"> |
||||
|
<el-icon :size="60" class="chat-icon"> |
||||
|
<ChatSquare /> |
||||
|
</el-icon> |
||||
|
<p class="empty-message">暂无数据</p> |
||||
|
</div> |
||||
</template> |
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { useChatStore } from "@/stores/chat"; |
||||
|
import { ref } from "vue"; |
||||
|
|
||||
|
const store = useChatStore(); |
||||
|
const id = ref("1"); |
||||
|
|
||||
|
</script> |
||||
|
|
||||
<style scoped> |
<style scoped> |
||||
.list-item { |
.list-item { |
||||
width: 100%; |
width: 100%; |
||||
height: 60px; |
height: 60px; |
||||
|
display: flex; |
||||
} |
} |
||||
|
|
||||
.list-item:hover { |
.list-item:hover { |
||||
background-color: #D0D0D0; |
background-color: #d0d0d0; |
||||
} |
} |
||||
|
|
||||
.avatar { |
.avatar { |
||||
margin: 10px |
margin: 10px; |
||||
|
} |
||||
|
|
||||
|
.preview { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.preview-content { |
||||
|
height: 100%; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.preview-left { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
height: 100%; |
||||
} |
} |
||||
|
|
||||
.previewName { |
.previewName { |
||||
margin-top: 10px; |
margin-left: 10px; |
||||
font-size: 15px; |
font-size: 12px; |
||||
font-family: Arial, sans-serif; |
font-family: Arial, sans-serif; |
||||
line-height: 1.5; |
line-height: 1.5; |
||||
|
overflow: hidden; /* 隐藏超出部分 */ |
||||
|
text-overflow: ellipsis; /* 显示为省略号 */ |
||||
|
white-space: nowrap; /* 不换行 */ |
||||
|
min-width: 100px; /* 最小宽度 */ |
||||
|
max-width: 100%; /* 最大宽度 */ |
||||
} |
} |
||||
|
|
||||
.previewChat { |
.previewChat { |
||||
font-size: 14px; |
height: 20px; |
||||
|
margin-left: 10px; |
||||
|
font-size: 10px; |
||||
font-family: Arial, sans-serif; |
font-family: Arial, sans-serif; |
||||
color: #999999; |
color: #999999; |
||||
|
overflow: hidden; /* 隐藏超出部分 */ |
||||
|
text-overflow: ellipsis; /* 显示为省略号 */ |
||||
|
white-space: nowrap; /* 不换行 */ |
||||
|
min-width: 90px; /* 最小宽度 */ |
||||
|
max-width: 100%; /* 最大宽度 */ |
||||
|
} |
||||
|
|
||||
|
.preview-right { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 100%; |
||||
} |
} |
||||
|
|
||||
.previewTime { |
.previewTime { |
||||
float: right; |
|
||||
margin-top: 10px; |
|
||||
margin-right: 10px; |
|
||||
font-size: 12px; |
font-size: 12px; |
||||
font-family: Arial, sans-serif; |
font-family: Arial, sans-serif; |
||||
color: #999999; |
color: #999999; |
||||
|
min-width: 30px; /* 最小宽度 */ |
||||
|
max-width: 100%; /* 最大宽度 */ |
||||
|
} |
||||
|
|
||||
|
.emptyChat { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 100%; |
||||
|
margin: 120px auto; |
||||
|
text-align: center; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.chat-icon { |
||||
|
color: #0078d4; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.empty-message { |
||||
|
font-size: 16px; |
||||
|
color: #666666; |
||||
} |
} |
||||
</style> |
</style> |
||||
|
@ -0,0 +1,79 @@ |
|||||
|
<template> |
||||
|
<div |
||||
|
v-if="store.targetUserId == null" |
||||
|
class="no-message-container" |
||||
|
> |
||||
|
<el-icon |
||||
|
:size="180" |
||||
|
color="#0078d7" |
||||
|
> |
||||
|
<ChatDotSquare /> |
||||
|
</el-icon> |
||||
|
<p>欢迎使用GodoOS</p> |
||||
|
</div> |
||||
|
<div |
||||
|
v-else |
||||
|
class="user-info-container" |
||||
|
> |
||||
|
<div class="user-details"> |
||||
|
<p>昵称: {{ store.targetUserInfo.nickname }}</p> |
||||
|
<p>邮箱: {{ store.targetUserInfo.email }}</p> |
||||
|
<p>电话: {{ store.targetUserInfo.phone }}</p> |
||||
|
<p>描述: {{ store.targetUserInfo.desc }}</p> |
||||
|
<p>工号: {{ store.targetUserInfo.jobNumber }}</p> |
||||
|
<p>工作地点: {{ store.targetUserInfo.workPlace }}</p> |
||||
|
<p>入职日期: {{ store.targetUserInfo.hiredDate }}</p> |
||||
|
</div> |
||||
|
<el-button |
||||
|
type="primary" |
||||
|
@click="sendMessage(store.targetUserId)" |
||||
|
>发送消息</el-button |
||||
|
> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { useChatStore } from "@/stores/chat"; |
||||
|
|
||||
|
const store = useChatStore(); |
||||
|
|
||||
|
const sendMessage = (id:number) => { |
||||
|
store.currentNavId = 0; |
||||
|
store.updateConversationList(id) |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.no-message-container { |
||||
|
height: 100%; |
||||
|
margin: 0px auto; |
||||
|
text-align: center; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.user-info-container { |
||||
|
height: 100%; |
||||
|
margin: 0px auto; |
||||
|
text-align: center; |
||||
|
font-size: 14px; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.user-details { |
||||
|
margin-bottom: 20px; |
||||
|
text-align: left; |
||||
|
max-width: 300px; |
||||
|
} |
||||
|
|
||||
|
.user-details p { |
||||
|
margin: 5px 0; |
||||
|
} |
||||
|
|
||||
|
.el-button { |
||||
|
width: 100%; |
||||
|
max-width: 300px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,106 @@ |
|||||
|
package files |
||||
|
|
||||
|
import ( |
||||
|
"encoding/base64" |
||||
|
"encoding/json" |
||||
|
"godo/libs" |
||||
|
"net/http" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
// 带加密读
|
||||
|
func HandleReadFile(w http.ResponseWriter, r *http.Request) { |
||||
|
|
||||
|
path := r.URL.Query().Get("path") |
||||
|
fPwd := r.Header.Get("fPwd") |
||||
|
hasPwd := IsHavePwd(fPwd) |
||||
|
|
||||
|
// 获取salt值
|
||||
|
salt := GetSalt(r) |
||||
|
|
||||
|
// 校验文件路径
|
||||
|
if err := validateFilePath(path); err != nil { |
||||
|
libs.HTTPError(w, http.StatusBadRequest, err.Error()) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 有密码校验密码
|
||||
|
if hasPwd { |
||||
|
if !CheckFilePwd(fPwd, salt) { |
||||
|
libs.HTTPError(w, http.StatusBadRequest, "密码错误") |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 获取文件路径
|
||||
|
basePath, err := libs.GetOsDir() |
||||
|
if err != nil { |
||||
|
libs.HTTPError(w, http.StatusInternalServerError, err.Error()) |
||||
|
return |
||||
|
} |
||||
|
// 读取内容
|
||||
|
fileContent, err := ReadFile(basePath, path) |
||||
|
if err != nil { |
||||
|
libs.HTTPError(w, http.StatusNotFound, err.Error()) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 解密
|
||||
|
data, err := libs.DecryptData(fileContent, libs.EncryptionKey) |
||||
|
if err != nil { |
||||
|
libs.HTTPError(w, http.StatusInternalServerError, err.Error()) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
content := string(data) |
||||
|
// 检查文件内容是否以"link::"开头
|
||||
|
if !strings.HasPrefix(content, "link::") { |
||||
|
content = base64.StdEncoding.EncodeToString(data) |
||||
|
} |
||||
|
|
||||
|
// 初始响应
|
||||
|
res := libs.APIResponse{Code: 0, Message: "success", Data: content} |
||||
|
|
||||
|
json.NewEncoder(w).Encode(res) |
||||
|
} |
||||
|
|
||||
|
// 设置文件密码
|
||||
|
func HandleSetFilePwd(w http.ResponseWriter, r *http.Request) { |
||||
|
fPwd := r.Header.Get("filepPwd") |
||||
|
salt := r.Header.Get("salt") |
||||
|
// 服务端再hash加密
|
||||
|
hashPwd := libs.HashPassword(fPwd, salt) |
||||
|
|
||||
|
// 服务端存储
|
||||
|
req := libs.ReqBody{ |
||||
|
Name: "filePwd", |
||||
|
Value: hashPwd, |
||||
|
} |
||||
|
libs.SetConfig(req) |
||||
|
|
||||
|
// salt值存储
|
||||
|
reqSalt := libs.ReqBody{ |
||||
|
Name: "salt", |
||||
|
Value: salt, |
||||
|
} |
||||
|
libs.SetConfig(reqSalt) |
||||
|
res := libs.APIResponse{Message: "密码设置成功"} |
||||
|
json.NewEncoder(w).Encode(res) |
||||
|
} |
||||
|
|
||||
|
// 更改文件密码
|
||||
|
func HandleChangeFilePwd(w http.ResponseWriter, r *http.Request) { |
||||
|
filePwd := r.Header.Get("filePwd") |
||||
|
salt := r.Header.Get("salt") |
||||
|
if filePwd == "" || salt == "" { |
||||
|
libs.ErrorMsg(w, "密码为空") |
||||
|
return |
||||
|
} |
||||
|
newPwd := libs.HashPassword(filePwd, salt) |
||||
|
pwdReq := libs.ReqBody{ |
||||
|
Name: "filePwd", |
||||
|
Value: newPwd, |
||||
|
} |
||||
|
libs.SetConfig(pwdReq) |
||||
|
libs.SuccessMsg(w, "success", "The file password change success!") |
||||
|
} |
Loading…
Reference in new issue