mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
新增复制按钮图标来复制 ChatGPT 回复内容。Golang 后端实现为每个用户订阅聊天角色功能
This commit is contained in:
parent
3bf83cd48c
commit
50ff591dbb
@ -393,6 +393,17 @@ func (s *Server) GetChatHistoryHandle(c *gin.Context) {
|
||||
}
|
||||
|
||||
session := s.ChatSession[sessionId]
|
||||
user, err := GetUser(session.Username)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Invalid args"})
|
||||
return
|
||||
}
|
||||
|
||||
if v, ok := user.ChatRoles[data.Role]; !ok || v != 1 {
|
||||
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "No permission to access the history of role " + data.Role})
|
||||
return
|
||||
}
|
||||
|
||||
history, err := GetChatHistory(session.Username, data.Role)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "No history message"})
|
||||
|
@ -2,16 +2,18 @@ package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"openai/types"
|
||||
"openai/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (s *Server) TestHandle(c *gin.Context) {
|
||||
roles := types.GetDefaultChatRole()
|
||||
for _, v := range roles {
|
||||
PutChatRole(v)
|
||||
_ = PutChatRole(v)
|
||||
}
|
||||
c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Data: GetChatRoles()})
|
||||
|
||||
@ -81,10 +83,11 @@ func (s *Server) SetDebugHandle(c *gin.Context) {
|
||||
// AddUserHandle 添加 Username
|
||||
func (s *Server) AddUserHandle(c *gin.Context) {
|
||||
var data struct {
|
||||
Name string `json:"name"`
|
||||
MaxCalls int `json:"max_calls"`
|
||||
EnableHistory bool `json:"enable_history"`
|
||||
Term int `json:"term"` // 有效期
|
||||
Name string `json:"name"`
|
||||
MaxCalls int `json:"max_calls"`
|
||||
EnableHistory bool `json:"enable_history"`
|
||||
Term int `json:"term"` // 有效期
|
||||
ChatRoles []string `json:"chat_roles"` // 订阅角色
|
||||
}
|
||||
err := json.NewDecoder(c.Request.Body).Decode(&data)
|
||||
if err != nil {
|
||||
@ -105,12 +108,28 @@ func (s *Server) AddUserHandle(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var chatRoles = make(map[string]int)
|
||||
if len(data.ChatRoles) > 0 {
|
||||
if data.ChatRoles[0] == "all" { // 所有的角色
|
||||
roles := GetChatRoles()
|
||||
for key := range roles {
|
||||
chatRoles[key] = 1
|
||||
}
|
||||
} else {
|
||||
for _, key := range data.ChatRoles {
|
||||
chatRoles[key] = 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
user := types.User{
|
||||
Name: data.Name,
|
||||
MaxCalls: data.MaxCalls,
|
||||
RemainingCalls: data.MaxCalls,
|
||||
EnableHistory: data.EnableHistory,
|
||||
Term: data.Term,
|
||||
ChatRoles: chatRoles,
|
||||
Status: true}
|
||||
err = PutUser(user)
|
||||
if err != nil {
|
||||
@ -124,10 +143,11 @@ func (s *Server) AddUserHandle(c *gin.Context) {
|
||||
// BatchAddUserHandle 批量生成 Username
|
||||
func (s *Server) BatchAddUserHandle(c *gin.Context) {
|
||||
var data struct {
|
||||
Number int `json:"number"`
|
||||
MaxCalls int `json:"max_calls"`
|
||||
EnableHistory bool `json:"enable_history"`
|
||||
Term int `json:"term"`
|
||||
Number int `json:"number"`
|
||||
MaxCalls int `json:"max_calls"`
|
||||
EnableHistory bool `json:"enable_history"`
|
||||
Term int `json:"term"`
|
||||
ChatRoles []string `json:"chat_roles"`
|
||||
}
|
||||
err := json.NewDecoder(c.Request.Body).Decode(&data)
|
||||
if err != nil || data.MaxCalls <= 0 {
|
||||
@ -135,6 +155,21 @@ func (s *Server) BatchAddUserHandle(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var chatRoles = make(map[string]int)
|
||||
if len(data.ChatRoles) > 0 {
|
||||
if data.ChatRoles[0] == "all" { // 所有的角色
|
||||
roles := GetChatRoles()
|
||||
for key := range roles {
|
||||
chatRoles[key] = 1
|
||||
}
|
||||
} else {
|
||||
for _, key := range data.ChatRoles {
|
||||
chatRoles[key] = 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var users = make([]types.User, 0)
|
||||
for i := 0; i < data.Number; i++ {
|
||||
name := utils.RandString(12)
|
||||
@ -148,6 +183,7 @@ func (s *Server) BatchAddUserHandle(c *gin.Context) {
|
||||
RemainingCalls: data.MaxCalls,
|
||||
EnableHistory: data.EnableHistory,
|
||||
Term: data.Term,
|
||||
ChatRoles: chatRoles,
|
||||
Status: true}
|
||||
err = PutUser(user)
|
||||
if err == nil {
|
||||
@ -204,6 +240,24 @@ func (s *Server) SetUserHandle(c *gin.Context) {
|
||||
if v, ok := data["api_key"]; ok {
|
||||
user.ApiKey = v.(string)
|
||||
}
|
||||
if v, ok := data["chat_roles"]; ok {
|
||||
if roles, ok := v.([]interface{}); ok {
|
||||
chatRoles := make(map[string]int)
|
||||
if roles[0] == "all" {
|
||||
roles := GetChatRoles()
|
||||
for key := range roles {
|
||||
chatRoles[key] = 1
|
||||
}
|
||||
} else {
|
||||
for _, key := range roles {
|
||||
key := strings.TrimSpace(fmt.Sprintf("%v", key))
|
||||
chatRoles[key] = 1
|
||||
}
|
||||
}
|
||||
user.ChatRoles = chatRoles
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
err = PutUser(*user)
|
||||
if err != nil {
|
||||
@ -299,12 +353,24 @@ func (s *Server) ListApiKeysHandle(c *gin.Context) {
|
||||
|
||||
// GetChatRoleListHandle 获取聊天角色列表
|
||||
func (s *Server) GetChatRoleListHandle(c *gin.Context) {
|
||||
sessionId := c.GetHeader(types.TokenName)
|
||||
session := s.ChatSession[sessionId]
|
||||
user, err := GetUser(session.Username)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Hacker Access!!!"})
|
||||
return
|
||||
}
|
||||
var rolesOrder = []string{"gpt", "teacher", "translator", "english_trainer", "weekly_report", "girl_friend",
|
||||
"kong_zi", "lu_xun", "steve_jobs", "elon_musk", "red_book", "dou_yin", "programmer",
|
||||
"seller", "good_comment", "psychiatrist", "artist"}
|
||||
var res = make([]interface{}, 0)
|
||||
var roles = GetChatRoles()
|
||||
for _, k := range rolesOrder {
|
||||
// 确认当前用户是否订阅了当前角色
|
||||
if v, ok := user.ChatRoles[k]; !ok || v != 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if v, ok := roles[k]; ok && v.Enable {
|
||||
res = append(res, struct {
|
||||
Key string `json:"key"`
|
||||
|
@ -14,15 +14,16 @@ type Config struct {
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
MaxCalls int `json:"max_calls"` // 最多调用次数,如果为 0 则表示不限制
|
||||
RemainingCalls int `json:"remaining_calls"` // 剩余调用次数
|
||||
EnableHistory bool `json:"enable_history"` // 是否启用聊天记录
|
||||
Status bool `json:"status"` // 当前状态
|
||||
Term int `json:"term" default:"30"` // 会员有效期,单位:天
|
||||
ActiveTime int64 `json:"active_time"` // 激活时间
|
||||
ExpiredTime int64 `json:"expired_time"` // 到期时间
|
||||
ApiKey string `json:"api_key"` // OpenAI API KEY
|
||||
Name string `json:"name"`
|
||||
MaxCalls int `json:"max_calls"` // 最多调用次数,如果为 0 则表示不限制
|
||||
RemainingCalls int `json:"remaining_calls"` // 剩余调用次数
|
||||
EnableHistory bool `json:"enable_history"` // 是否启用聊天记录
|
||||
Status bool `json:"status"` // 当前状态
|
||||
Term int `json:"term" default:"30"` // 会员有效期,单位:天
|
||||
ActiveTime int64 `json:"active_time"` // 激活时间
|
||||
ExpiredTime int64 `json:"expired_time"` // 到期时间
|
||||
ApiKey string `json:"api_key"` // OpenAI API KEY
|
||||
ChatRoles map[string]int `json:"chat_roles"` // 当前用户已订阅的聊天角色 map[role_key] => 0/1
|
||||
}
|
||||
|
||||
// Chat configs struct
|
||||
|
@ -6,7 +6,24 @@
|
||||
|
||||
<div class="chat-item">
|
||||
<div class="triangle"></div>
|
||||
<div class="content reply-content" :data-clipboard-text="orgContent" v-html="content"></div>
|
||||
<div class="content-box">
|
||||
<div class="content" v-html="content"></div>
|
||||
<div class="tool-box">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="复制回答"
|
||||
placement="top"
|
||||
>
|
||||
<el-button type="info" class="copy-reply" :data-clipboard-text="orgContent" plain>
|
||||
<el-icon>
|
||||
<DocumentCopy/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -14,9 +31,11 @@
|
||||
<script>
|
||||
import {defineComponent} from "vue"
|
||||
import {randString} from "@/utils/libs";
|
||||
import {DocumentCopy} from "@element-plus/icons-vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ChatReply',
|
||||
components: {DocumentCopy},
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
@ -70,28 +89,45 @@ export default defineComponent({
|
||||
top: 13px;
|
||||
}
|
||||
|
||||
.content {
|
||||
min-height 20px;
|
||||
word-break break-word;
|
||||
padding: 8px 10px;
|
||||
color var(--content-color)
|
||||
background-color: #fff;
|
||||
font-size: var(--content-font-size);
|
||||
border-radius: 5px;
|
||||
.content-box {
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0
|
||||
display flex
|
||||
flex-direction row
|
||||
|
||||
.content {
|
||||
min-height 20px;
|
||||
word-break break-word;
|
||||
padding: 8px 10px;
|
||||
color var(--content-color)
|
||||
background-color: #fff;
|
||||
font-size: var(--content-font-size);
|
||||
border-radius: 5px;
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
margin-top 0
|
||||
}
|
||||
|
||||
p > code {
|
||||
color #cc0000
|
||||
background-color #f1f1f1
|
||||
}
|
||||
}
|
||||
|
||||
p:first-child {
|
||||
margin-top 0
|
||||
}
|
||||
.tool-box {
|
||||
padding-left 10px;
|
||||
font-size 16px;
|
||||
|
||||
p > code {
|
||||
color #cc0000
|
||||
background-color #f1f1f1
|
||||
.el-button {
|
||||
height 20px
|
||||
padding 5px 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ const routes = [
|
||||
},
|
||||
{
|
||||
name: 'free', path: '/free', component: ChatFree, meta: {
|
||||
title: 'ChatGPT 免费版'
|
||||
title: 'ChatGPT 通用免费版'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -179,7 +179,7 @@ export default defineComponent({
|
||||
return;
|
||||
}
|
||||
|
||||
const clipboard = new Clipboard('.reply-content');
|
||||
const clipboard = new Clipboard('.copy-reply');
|
||||
clipboard.on('success', () => {
|
||||
ElMessage.success('复制成功!');
|
||||
})
|
||||
|
@ -3,19 +3,20 @@
|
||||
<div class="sidebar" id="sidebar">
|
||||
<nav>
|
||||
<ul>
|
||||
<li class="new-chat"><a>
|
||||
<li class="new-chat" @click="newChat"><a>
|
||||
<span class="icon"><el-icon><Plus/></el-icon></span>
|
||||
<span class="text">新建会话</span>
|
||||
<span class="btn" @click="toggleSidebar"><el-button size="small" type="info" circle><el-icon><CloseBold/></el-icon></el-button></span>
|
||||
</a></li>
|
||||
<li><a>
|
||||
<li v-for="session in sessionList" :key="session.id"><a>
|
||||
<span class="icon"><el-icon><ChatRound/></el-icon></span>
|
||||
<span class="text">会话一</span>
|
||||
</a></li>
|
||||
<li class="active"><a>
|
||||
<span class="icon"><el-icon><ChatRound/></el-icon></span>
|
||||
<span class="text">会话二</span>
|
||||
<span class="text">{{ session.title }}</span>
|
||||
<span class="btn">
|
||||
<el-icon title="编辑"><Edit/></el-icon>
|
||||
<el-icon title="删除会话"><Delete/></el-icon>
|
||||
</span>
|
||||
</a></li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
@ -121,7 +122,16 @@
|
||||
|
||||
<script>
|
||||
import {defineComponent, nextTick} from "vue"
|
||||
import {ChatRound, CloseBold, Fold, Lock, Plus, RefreshRight, VideoPause} from "@element-plus/icons-vue";
|
||||
import {
|
||||
ChatRound,
|
||||
CloseBold,
|
||||
Delete, Edit,
|
||||
Fold,
|
||||
Lock,
|
||||
Plus,
|
||||
RefreshRight,
|
||||
VideoPause
|
||||
} from "@element-plus/icons-vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import hl from "highlight.js";
|
||||
import ChatReply from "@/components/ChatReply.vue";
|
||||
@ -134,7 +144,19 @@ import Clipboard from "clipboard";
|
||||
// 免费版 ChatGPT
|
||||
export default defineComponent({
|
||||
name: 'ChatFree',
|
||||
components: {CloseBold, Lock, VideoPause, RefreshRight, ChatPrompt, ChatReply, ChatRound, Plus, Fold},
|
||||
components: {
|
||||
Edit,
|
||||
Delete,
|
||||
CloseBold,
|
||||
Lock,
|
||||
VideoPause,
|
||||
RefreshRight,
|
||||
ChatPrompt,
|
||||
ChatReply,
|
||||
ChatRound,
|
||||
Plus,
|
||||
Fold
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chatData: [],
|
||||
@ -144,6 +166,10 @@ export default defineComponent({
|
||||
showLoginDialog: false,
|
||||
role: 'gpt',
|
||||
replyIcon: 'images/avatar/gpt.png', // 回复信息的头像
|
||||
sessionList: [{
|
||||
id: randString(32),
|
||||
title: '响应式页面布局代码'
|
||||
}], // 会话列表
|
||||
|
||||
showStopGenerate: false,
|
||||
showReGenerate: false,
|
||||
@ -164,7 +190,7 @@ export default defineComponent({
|
||||
mounted() {
|
||||
this.fetchChatHistory();
|
||||
|
||||
const clipboard = new Clipboard('.reply-content');
|
||||
const clipboard = new Clipboard('.copy-reply');
|
||||
clipboard.on('success', () => {
|
||||
ElMessage.success('复制成功!');
|
||||
})
|
||||
@ -455,6 +481,11 @@ export default defineComponent({
|
||||
toggleSidebar: function () {
|
||||
document.getElementById("sidebar").classList.toggle('show');
|
||||
},
|
||||
|
||||
// 新建会话
|
||||
newChat: function () {
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -510,8 +541,26 @@ export default defineComponent({
|
||||
overflow hidden
|
||||
}
|
||||
|
||||
.btn {
|
||||
//display none
|
||||
|
||||
position absolute
|
||||
right 0;
|
||||
top 2px;
|
||||
|
||||
.el-icon {
|
||||
margin-left 5px;
|
||||
color #9f9f9f
|
||||
}
|
||||
|
||||
.el-icon:hover {
|
||||
color #ffffff
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
li.active {
|
||||
@ -521,11 +570,17 @@ export default defineComponent({
|
||||
li.new-chat {
|
||||
border: 1px solid #4A4B4D;
|
||||
|
||||
.btn {
|
||||
display none
|
||||
position absolute
|
||||
right -2px;
|
||||
top -2px;
|
||||
a {
|
||||
.btn {
|
||||
display none
|
||||
right -2px;
|
||||
top -2px;
|
||||
|
||||
.el-icon {
|
||||
margin-left 0;
|
||||
color #ffffff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -678,6 +733,10 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
.row-center {
|
||||
justify-content center
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.chat-free-page {
|
||||
|
@ -439,8 +439,8 @@ export default defineComponent({
|
||||
hl.highlightElement(block)
|
||||
})
|
||||
})
|
||||
}).catch(() => {
|
||||
// console.error(e.message)
|
||||
}).catch((e) => {
|
||||
console.error(e.message)
|
||||
})
|
||||
},
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user