mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-19 17:56:39 +08:00
add reply content to clipboard function is ready
This commit is contained in:
parent
a88f55372c
commit
676457f350
@ -61,15 +61,19 @@ func (s *Server) ChatHandle(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Info("Receive a message: ", string(message))
|
logger.Info("Receive a message: ", string(message))
|
||||||
//replyMessage(client, "当前 TOKEN 无效,请使用合法的 TOKEN 登录!", false)
|
replyMessage(client, "当前 TOKEN 无效,请使用合法的 TOKEN 登录!", false)
|
||||||
//replyMessage(client, "", true)
|
replyMessage(client, "", true)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
//ctx, cancel := context.WithCancel(context.Background())
|
||||||
s.ReqCancelFunc[sessionId] = cancel
|
//s.ReqCancelFunc[sessionId] = cancel
|
||||||
// 回复消息
|
//// 回复消息
|
||||||
err = s.sendMessage(ctx, session, chatRole, string(message), client, false)
|
//err = s.sendMessage(ctx, session, chatRole, string(message), client, false)
|
||||||
if err != nil {
|
//if err != nil {
|
||||||
logger.Error(err)
|
// logger.Error(err)
|
||||||
}
|
//} else {
|
||||||
|
// replyChunkMessage(client, types.WsMessage{Type: types.WsEnd, IsHelloMsg: false})
|
||||||
|
// logger.Info("回答完毕: " + string(message))
|
||||||
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@ -232,7 +236,6 @@ func (s *Server) sendMessage(ctx context.Context, session types.ChatSession, rol
|
|||||||
err = json.Unmarshal([]byte(line[6:]), &responseBody)
|
err = json.Unmarshal([]byte(line[6:]), &responseBody)
|
||||||
if err != nil { // 数据解析出错
|
if err != nil { // 数据解析出错
|
||||||
logger.Error(err, line)
|
logger.Error(err, line)
|
||||||
replyChunkMessage(ws, types.WsMessage{Type: types.WsEnd, IsHelloMsg: false})
|
|
||||||
replyMessage(ws, ErrorMsg, false)
|
replyMessage(ws, ErrorMsg, false)
|
||||||
replyMessage(ws, "", true)
|
replyMessage(ws, "", true)
|
||||||
break
|
break
|
||||||
@ -246,9 +249,8 @@ func (s *Server) sendMessage(ctx context.Context, session types.ChatSession, rol
|
|||||||
message.Role = responseBody.Choices[0].Delta.Role
|
message.Role = responseBody.Choices[0].Delta.Role
|
||||||
replyChunkMessage(ws, types.WsMessage{Type: types.WsStart, IsHelloMsg: false})
|
replyChunkMessage(ws, types.WsMessage{Type: types.WsStart, IsHelloMsg: false})
|
||||||
continue
|
continue
|
||||||
} else if responseBody.Choices[0].FinishReason != "" { // 输出完成或者输出中断了
|
} else if responseBody.Choices[0].FinishReason != "" {
|
||||||
replyChunkMessage(ws, types.WsMessage{Type: types.WsEnd, IsHelloMsg: false})
|
break // 输出完成或者输出中断了
|
||||||
break
|
|
||||||
} else {
|
} else {
|
||||||
content := responseBody.Choices[0].Delta.Content
|
content := responseBody.Choices[0].Delta.Content
|
||||||
contents = append(contents, content)
|
contents = append(contents, content)
|
||||||
@ -262,9 +264,7 @@ func (s *Server) sendMessage(ctx context.Context, session types.ChatSession, rol
|
|||||||
// 监控取消信号
|
// 监控取消信号
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
// 结束输出
|
_ = response.Body.Close() // 关闭响应流
|
||||||
replyChunkMessage(ws, types.WsMessage{Type: types.WsEnd, IsHelloMsg: false})
|
|
||||||
_ = response.Body.Close()
|
|
||||||
return errors.New("用户取消了请求:" + prompt)
|
return errors.New("用户取消了请求:" + prompt)
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
@ -306,7 +306,7 @@ func (s *Server) sendMessage(ctx context.Context, session types.ChatSession, rol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 随机获取一个 API Key,如果请求失败,则更换 API Key 重试
|
// 随机获取一个 API Key,如果请求失败,则更换 API Key 重试
|
||||||
|
71
web/package-lock.json
generated
71
web/package-lock.json
generated
@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "yycloud-webssh",
|
"name": "chatgpt-plus",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "yycloud-webssh",
|
"name": "chatgpt-plus",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.1.0",
|
"@element-plus/icons-vue": "^2.1.0",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
"clipboard": "^2.0.11",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"element-plus": "^2.1.11",
|
"element-plus": "^2.1.11",
|
||||||
"good-storage": "^1.1.1",
|
"good-storage": "^1.1.1",
|
||||||
@ -3994,6 +3995,16 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clipboard": {
|
||||||
|
"version": "2.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
|
||||||
|
"integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
|
||||||
|
"dependencies": {
|
||||||
|
"good-listener": "^1.2.2",
|
||||||
|
"select": "^1.1.2",
|
||||||
|
"tiny-emitter": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/clipboardy": {
|
"node_modules/clipboardy": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz",
|
||||||
@ -4832,6 +4843,11 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delegate": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
|
||||||
|
},
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz",
|
||||||
@ -6157,6 +6173,14 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/good-listener": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
|
||||||
|
"dependencies": {
|
||||||
|
"delegate": "^3.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/good-storage": {
|
"node_modules/good-storage": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/good-storage/-/good-storage-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/good-storage/-/good-storage-1.1.1.tgz",
|
||||||
@ -9194,6 +9218,11 @@
|
|||||||
"node": ">= 8.9.0"
|
"node": ">= 8.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/select": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
|
||||||
|
},
|
||||||
"node_modules/select-hose": {
|
"node_modules/select-hose": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz",
|
||||||
@ -10050,6 +10079,11 @@
|
|||||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-emitter": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
||||||
|
},
|
||||||
"node_modules/to-fast-properties": {
|
"node_modules/to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
@ -14236,6 +14270,16 @@
|
|||||||
"integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==",
|
"integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"clipboard": {
|
||||||
|
"version": "2.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
|
||||||
|
"integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
|
||||||
|
"requires": {
|
||||||
|
"good-listener": "^1.2.2",
|
||||||
|
"select": "^1.1.2",
|
||||||
|
"tiny-emitter": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"clipboardy": {
|
"clipboardy": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz",
|
||||||
@ -14896,6 +14940,11 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
||||||
},
|
},
|
||||||
|
"delegate": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
|
||||||
|
},
|
||||||
"depd": {
|
"depd": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz",
|
||||||
@ -15959,6 +16008,14 @@
|
|||||||
"slash": "^3.0.0"
|
"slash": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"good-listener": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
|
||||||
|
"requires": {
|
||||||
|
"delegate": "^3.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"good-storage": {
|
"good-storage": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/good-storage/-/good-storage-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/good-storage/-/good-storage-1.1.1.tgz",
|
||||||
@ -18302,6 +18359,11 @@
|
|||||||
"ajv-keywords": "^3.5.2"
|
"ajv-keywords": "^3.5.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"select": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
|
||||||
|
},
|
||||||
"select-hose": {
|
"select-hose": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz",
|
||||||
@ -19010,6 +19072,11 @@
|
|||||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"tiny-emitter": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
||||||
|
},
|
||||||
"to-fast-properties": {
|
"to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.1.0",
|
"@element-plus/icons-vue": "^2.1.0",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
"clipboard": "^2.0.11",
|
||||||
"core-js": "^3.8.3",
|
"core-js": "^3.8.3",
|
||||||
"element-plus": "^2.1.11",
|
"element-plus": "^2.1.11",
|
||||||
"good-storage": "^1.1.1",
|
"good-storage": "^1.1.1",
|
||||||
|
@ -6,13 +6,14 @@
|
|||||||
|
|
||||||
<div class="chat-item">
|
<div class="chat-item">
|
||||||
<div class="triangle"></div>
|
<div class="triangle"></div>
|
||||||
<div class="content" v-html="content"></div>
|
<div class="content reply-content" :data-clipboard-text="orgContent" v-html="content"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {defineComponent} from "vue"
|
import {defineComponent} from "vue"
|
||||||
|
import {randString} from "@/utils/libs";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ChatReply',
|
name: 'ChatReply',
|
||||||
@ -21,14 +22,22 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
orgContent: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'images/gpt-icon.png',
|
default: 'images/gpt-icon.png',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {}
|
return {
|
||||||
|
id: randString(32),
|
||||||
|
clipboard: null,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<div class="chat-item">
|
<div class="chat-item">
|
||||||
<div class="triangle"></div>
|
<div class="triangle"></div>
|
||||||
<div class="content" v-html="content"></div>
|
<div class="content reply-content" :data-clipboard-text="orgContent" v-html="content"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -21,6 +21,10 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
orgContent: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'images/gpt-icon.png',
|
default: 'images/gpt-icon.png',
|
||||||
|
@ -12,7 +12,12 @@
|
|||||||
:key="item.key"
|
:key="item.key"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:value="item.key"
|
:value="item.key"
|
||||||
/>
|
>
|
||||||
|
<div class="role-option">
|
||||||
|
<el-image :src="item.icon"></el-image>
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
</div>
|
||||||
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|
||||||
<el-button type="danger" class="clear-history" size="small" circle @click="clearChatHistory">
|
<el-button type="danger" class="clear-history" size="small" circle @click="clearChatHistory">
|
||||||
@ -36,29 +41,50 @@
|
|||||||
:content="chat.content"/>
|
:content="chat.content"/>
|
||||||
<chat-reply v-else-if="chat.type==='reply'"
|
<chat-reply v-else-if="chat.type==='reply'"
|
||||||
:icon="chat.icon"
|
:icon="chat.icon"
|
||||||
|
:org-content="chat.orgContent"
|
||||||
:content="chat.content"/>
|
:content="chat.content"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div><!-- end chat box -->
|
</div><!-- end chat box -->
|
||||||
|
|
||||||
<div class="input-box" :style="{width: inputBoxWidth+'px'}">
|
<div class="input-box" :style="{width: inputBoxWidth+'px'}">
|
||||||
<div class="input-container">
|
<div class="re-generate">
|
||||||
<el-input
|
<div class="btn-box">
|
||||||
ref="text-input"
|
<el-button type="info" v-if="showStopGenerate" @click="stopGenerate" plain>
|
||||||
v-model="inputValue"
|
<el-icon>
|
||||||
:autosize="{ minRows: 1, maxRows: 10 }"
|
<VideoPause/>
|
||||||
v-on:keydown="inputKeyDown"
|
</el-icon>
|
||||||
v-on:focus="focus"
|
停止生成
|
||||||
autofocus
|
</el-button>
|
||||||
type="textarea"
|
|
||||||
placeholder="开始你的提问"
|
<el-button type="primary" v-if="showReGenerate" @click="reGenerate" plain>
|
||||||
/>
|
<el-icon>
|
||||||
|
<RefreshRight/>
|
||||||
|
</el-icon>
|
||||||
|
重新生成
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-container">
|
<div class="input-wrapper">
|
||||||
<el-row>
|
<div class="input-container">
|
||||||
<el-button type="success" class="send" :disabled="sending" v-on:click="sendMessage">发送</el-button>
|
<el-input
|
||||||
</el-row>
|
ref="text-input"
|
||||||
|
v-model="inputValue"
|
||||||
|
:autosize="{ minRows: 1, maxRows: 10 }"
|
||||||
|
v-on:keydown="inputKeyDown"
|
||||||
|
v-on:focus="focus"
|
||||||
|
autofocus
|
||||||
|
type="textarea"
|
||||||
|
placeholder="开始你的提问"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-container">
|
||||||
|
<el-row>
|
||||||
|
<el-button type="success" class="send" :disabled="sending" v-on:click="sendMessage">发送</el-button>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div><!-- end input box -->
|
</div><!-- end input box -->
|
||||||
@ -75,7 +101,7 @@
|
|||||||
title="请输入口令继续访问"
|
title="请输入口令继续访问"
|
||||||
>
|
>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-input v-model="token" placeholder="在此输入口令" @keyup="loginInputKeyup">
|
<el-input v-model="token" placeholder="在此输入口令" type="password" @keyup="loginInputKeyup">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<el-icon class="el-input__icon">
|
<el-icon class="el-input__icon">
|
||||||
<Lock/>
|
<Lock/>
|
||||||
@ -104,16 +130,17 @@ import ChatPrompt from "@/components/ChatPrompt.vue";
|
|||||||
import ChatReply from "@/components/ChatReply.vue";
|
import ChatReply from "@/components/ChatReply.vue";
|
||||||
import {isMobile, randString} from "@/utils/libs";
|
import {isMobile, randString} from "@/utils/libs";
|
||||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||||
import {Tools, Lock, Delete} from '@element-plus/icons-vue'
|
import {Tools, Lock, Delete, VideoPause, RefreshRight} from '@element-plus/icons-vue'
|
||||||
import ConfigDialog from '@/components/ConfigDialog.vue'
|
import ConfigDialog from '@/components/ConfigDialog.vue'
|
||||||
import {httpPost, httpGet} from "@/utils/http";
|
import {httpPost, httpGet} from "@/utils/http";
|
||||||
import {getSessionId, getUserInfo, setLoginUser} from "@/utils/storage";
|
import {getSessionId, getUserInfo, setLoginUser} from "@/utils/storage";
|
||||||
import hl from 'highlight.js'
|
import hl from 'highlight.js'
|
||||||
import 'highlight.js/styles/a11y-dark.css'
|
import 'highlight.js/styles/a11y-dark.css'
|
||||||
|
import Clipboard from "clipboard";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "XChat",
|
name: "XChat",
|
||||||
components: {ChatPrompt, ChatReply, Tools, Lock, Delete, ConfigDialog},
|
components: {RefreshRight, VideoPause, ChatPrompt, ChatReply, Tools, Lock, Delete, ConfigDialog},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
title: 'ChatGPT 控制台',
|
title: 'ChatGPT 控制台',
|
||||||
@ -128,6 +155,11 @@ export default defineComponent({
|
|||||||
userInfo: {},
|
userInfo: {},
|
||||||
showLoginDialog: false,
|
showLoginDialog: false,
|
||||||
|
|
||||||
|
showStopGenerate: false,
|
||||||
|
showReGenerate: false,
|
||||||
|
canReGenerate: false, // 是否可以重新生
|
||||||
|
previousText: '', // 上一次提问
|
||||||
|
|
||||||
token: '', // 会话 token
|
token: '', // 会话 token
|
||||||
replyIcon: 'images/avatar/gpt.png', // 回复信息的头像
|
replyIcon: 'images/avatar/gpt.png', // 回复信息的头像
|
||||||
|
|
||||||
@ -148,6 +180,15 @@ export default defineComponent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clipboard = new Clipboard('.reply-content');
|
||||||
|
clipboard.on('success', () => {
|
||||||
|
ElMessage.success('复制成功!');
|
||||||
|
})
|
||||||
|
|
||||||
|
clipboard.on('error', () => {
|
||||||
|
ElMessage.error('复制失败!');
|
||||||
|
})
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
this.chatBoxHeight = window.innerHeight - this.toolBoxHeight;
|
this.chatBoxHeight = window.innerHeight - this.toolBoxHeight;
|
||||||
ElMessage.warning("强烈建议使用PC浏览器访问获的更好的聊天体验!")
|
ElMessage.warning("强烈建议使用PC浏览器访问获的更好的聊天体验!")
|
||||||
@ -207,13 +248,21 @@ export default defineComponent({
|
|||||||
content: "",
|
content: "",
|
||||||
cursor: true
|
cursor: true
|
||||||
});
|
});
|
||||||
|
if (data['is_hello_msg'] !== true) {
|
||||||
|
this.canReGenerate = true;
|
||||||
|
}
|
||||||
} else if (data.type === 'end') {
|
} else if (data.type === 'end') {
|
||||||
this.sending = false;
|
this.sending = false;
|
||||||
|
if (data['is_hello_msg'] !== true) {
|
||||||
|
this.showReGenerate = true;
|
||||||
|
}
|
||||||
|
this.showStopGenerate = false;
|
||||||
this.lineBuffer = ''; // 清空缓冲
|
this.lineBuffer = ''; // 清空缓冲
|
||||||
} else {
|
} else {
|
||||||
this.lineBuffer += data.content;
|
this.lineBuffer += data.content;
|
||||||
|
this.chatData[this.chatData.length - 1]['orgContent'] = this.lineBuffer;
|
||||||
let md = require('markdown-it')();
|
let md = require('markdown-it')();
|
||||||
this.chatData[this.chatData.length - 1]["content"] = md.render(this.lineBuffer);
|
this.chatData[this.chatData.length - 1]['content'] = md.render(this.lineBuffer);
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
hl.configure({ignoreUnescapedHTML: true})
|
hl.configure({ignoreUnescapedHTML: true})
|
||||||
@ -296,7 +345,7 @@ export default defineComponent({
|
|||||||
this.chatData.push(data[i]);
|
this.chatData.push(data[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
data[i].orgContent = data[i].content;
|
||||||
data[i].content = md.render(data[i].content);
|
data[i].content = md.render(data[i].content);
|
||||||
this.chatData.push(data[i]);
|
this.chatData.push(data[i]);
|
||||||
}
|
}
|
||||||
@ -347,8 +396,11 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.sending = true;
|
this.sending = true;
|
||||||
|
this.showStopGenerate = true;
|
||||||
|
this.showReGenerate = false;
|
||||||
this.socket.send(this.inputValue);
|
this.socket.send(this.inputValue);
|
||||||
this.$refs["text-input"].blur();
|
this.$refs["text-input"].blur();
|
||||||
|
this.previousText = this.inputValue;
|
||||||
this.inputValue = '';
|
this.inputValue = '';
|
||||||
// 等待 textarea 重新调整尺寸之后再自动获取焦点
|
// 等待 textarea 重新调整尺寸之后再自动获取焦点
|
||||||
setTimeout(() => this.$refs["text-input"].focus(), 100);
|
setTimeout(() => this.$refs["text-input"].focus(), 100);
|
||||||
@ -412,6 +464,26 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 停止生成
|
||||||
|
stopGenerate: function () {
|
||||||
|
this.showStopGenerate = false;
|
||||||
|
httpPost("/api/chat/stop").then(() => {
|
||||||
|
console.log("stopped generate.")
|
||||||
|
this.sending = false;
|
||||||
|
if (this.canReGenerate) {
|
||||||
|
this.showReGenerate = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重新生成
|
||||||
|
reGenerate: function () {
|
||||||
|
this.sending = true;
|
||||||
|
this.showStopGenerate = true;
|
||||||
|
this.showReGenerate = false;
|
||||||
|
this.socket.send('重新生成上述问题的答案:' + this.previousText);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -485,54 +557,77 @@ export default defineComponent({
|
|||||||
.input-box {
|
.input-box {
|
||||||
padding 10px;
|
padding 10px;
|
||||||
background #ffffff;
|
background #ffffff;
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0
|
bottom: 0
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-flow: column;
|
||||||
|
|
||||||
.input-container {
|
.re-generate {
|
||||||
overflow hidden
|
position relative
|
||||||
width 100%
|
display flex
|
||||||
margin: 0;
|
justify-content center
|
||||||
border: none;
|
|
||||||
border-radius: 6px;
|
|
||||||
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
|
|
||||||
background-color: rgba(255, 255, 255, 1);
|
|
||||||
padding: 5px 10px;
|
|
||||||
|
|
||||||
.el-textarea__inner {
|
.btn-box {
|
||||||
box-shadow: none
|
position absolute
|
||||||
padding 5px 0
|
bottom 20px
|
||||||
}
|
|
||||||
|
|
||||||
.el-textarea__inner::-webkit-scrollbar {
|
.el-icon {
|
||||||
width: 0;
|
margin-right 5px;
|
||||||
height: 0;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-container {
|
.input-wrapper {
|
||||||
margin-left 10px;
|
width 100%;
|
||||||
|
display flex;
|
||||||
|
|
||||||
.el-row {
|
.input-container {
|
||||||
flex-wrap nowrap
|
overflow hidden
|
||||||
//width 106px;
|
width 100%
|
||||||
align-items center
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
|
||||||
|
background-color: rgba(255, 255, 255, 1);
|
||||||
|
padding: 5px 10px;
|
||||||
|
|
||||||
|
.el-textarea__inner {
|
||||||
|
box-shadow: none
|
||||||
|
padding 5px 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-textarea__inner::-webkit-scrollbar {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.send {
|
.btn-container {
|
||||||
width 60px;
|
margin-left 10px;
|
||||||
height 40px;
|
|
||||||
background-color: var(--el-color-success)
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-disabled {
|
.el-row {
|
||||||
background-color: var(--el-button-disabled-bg-color);
|
flex-wrap nowrap
|
||||||
border-color: var(--el-button-disabled-border-color);
|
//width 106px;
|
||||||
|
align-items center
|
||||||
|
}
|
||||||
|
|
||||||
|
.send {
|
||||||
|
width 60px;
|
||||||
|
height 40px;
|
||||||
|
background-color: var(--el-color-success)
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-disabled {
|
||||||
|
background-color: var(--el-button-disabled-bg-color);
|
||||||
|
border-color: var(--el-button-disabled-border-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// end of input wrapper
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,4 +679,26 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-select-dropdown {
|
||||||
|
.el-select-dropdown__item {
|
||||||
|
padding 8px 5px;
|
||||||
|
|
||||||
|
.role-option {
|
||||||
|
display flex
|
||||||
|
flex-flow row
|
||||||
|
|
||||||
|
.el-image {
|
||||||
|
width 20px
|
||||||
|
height 20px
|
||||||
|
border-radius 50%
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-left 5px;
|
||||||
|
height 20px;
|
||||||
|
line-height 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
:content="chat.content"/>
|
:content="chat.content"/>
|
||||||
<chat-reply v-else-if="chat.type==='reply'"
|
<chat-reply v-else-if="chat.type==='reply'"
|
||||||
:icon="chat.icon"
|
:icon="chat.icon"
|
||||||
|
:org-content="chat.orgContent"
|
||||||
:content="chat.content"/>
|
:content="chat.content"/>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- end chat box -->
|
</div><!-- end chat box -->
|
||||||
@ -192,6 +193,7 @@ import {httpPost, httpGet} from "@/utils/http";
|
|||||||
import {getSessionId, getUserInfo, setLoginUser} from "@/utils/storage";
|
import {getSessionId, getUserInfo, setLoginUser} from "@/utils/storage";
|
||||||
import hl from 'highlight.js'
|
import hl from 'highlight.js'
|
||||||
import 'highlight.js/styles/a11y-dark.css'
|
import 'highlight.js/styles/a11y-dark.css'
|
||||||
|
import Clipboard from "clipboard";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "ChatPlus",
|
name: "ChatPlus",
|
||||||
@ -250,6 +252,15 @@ export default defineComponent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const clipboard = new Clipboard('.reply-content');
|
||||||
|
clipboard.on('success', () => {
|
||||||
|
ElMessage.success('复制成功!');
|
||||||
|
})
|
||||||
|
|
||||||
|
clipboard.on('error', () => {
|
||||||
|
ElMessage.error('复制失败!');
|
||||||
|
})
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
this.resizeElement();
|
this.resizeElement();
|
||||||
})
|
})
|
||||||
@ -326,7 +337,8 @@ export default defineComponent({
|
|||||||
} else {
|
} else {
|
||||||
this.lineBuffer += data.content;
|
this.lineBuffer += data.content;
|
||||||
let md = require('markdown-it')();
|
let md = require('markdown-it')();
|
||||||
this.chatData[this.chatData.length - 1]["content"] = md.render(this.lineBuffer);
|
this.chatData[this.chatData.length - 1]['orgContent'] = this.lineBuffer;
|
||||||
|
this.chatData[this.chatData.length - 1]['content'] = md.render(this.lineBuffer);
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
hl.configure({ignoreUnescapedHTML: true})
|
hl.configure({ignoreUnescapedHTML: true})
|
||||||
@ -415,6 +427,7 @@ export default defineComponent({
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data[i].orgContent = data[i].content;
|
||||||
data[i].content = md.render(data[i].content);
|
data[i].content = md.render(data[i].content);
|
||||||
this.chatData.push(data[i]);
|
this.chatData.push(data[i]);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user