|
@@ -1,7 +1,7 @@
|
|
|
<template>
|
|
|
<view-full-screen :is-full-screen="true" class="right">
|
|
|
- <div style="width: 100%; height: 100%; background-color: #1f1f1f">
|
|
|
- <div ref="scrollView" style="overflow-y: scroll; height: calc(100% - 52px)">
|
|
|
+ <div :class="bodyClass">
|
|
|
+ <div ref="scrollView" :style="{ 'overflow-y': 'scroll', 'height': 'calc(100% - ' + (52 + safeAreaInsets.bottom) + 'px)' }">
|
|
|
<template v-for="cell in model">
|
|
|
<CCDChatCell :cell-type="cell.type" :message="cell.content" @shutdown="doShutdown"></CCDChatCell>
|
|
|
</template>
|
|
@@ -10,9 +10,18 @@
|
|
|
<div class="bottomAskBar" style="height: 50px; padding-right: 4px">
|
|
|
<div style="height: 50px; width: calc(100% - 50px);">
|
|
|
<input v-if="allowInput" ref="inputAsk" @keyup.enter="doPostMessage" type="text" placeholder="输入问题..." value=""/>
|
|
|
- <input v-else value="机器人正在输入..." style="text-align: center" disabled/>
|
|
|
+ <input v-else value=" 请等待AI回复" style="text-align: center" disabled/>
|
|
|
</div>
|
|
|
- <button v-if="allowInput" @click="doPostMessage" style="height: 50px; width: 50px; right: 0px; top: calc(100% - 50px); position: absolute; float: right; background: transparent; border: none">
|
|
|
+ <button v-if="allowInput" @click="doPostMessage" :style="{
|
|
|
+ height: '50px',
|
|
|
+ width: '50px',
|
|
|
+ right: '0px',
|
|
|
+ top: 'calc(100% - ' + (50 + safeAreaInsets.bottom) + 'px)',
|
|
|
+ position: 'absolute',
|
|
|
+ float: 'right',
|
|
|
+ background: 'transparent',
|
|
|
+ border: 'none'
|
|
|
+ }">
|
|
|
<img src="../assets/run.png" style="width: calc(100% - 1px); height: calc(100% - 3px)">
|
|
|
</button>
|
|
|
</div>
|
|
@@ -24,6 +33,16 @@
|
|
|
p {
|
|
|
color: white;
|
|
|
}
|
|
|
+.bodyBackground_normal {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background-color: #1f1f1f;
|
|
|
+}
|
|
|
+.bodyBackground_xcwk {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background-color: #1f1f1f00;
|
|
|
+}
|
|
|
</style>
|
|
|
<style scoped>
|
|
|
input {
|
|
@@ -66,13 +85,65 @@ export default {
|
|
|
socketObject: null,
|
|
|
wannaKill: false,
|
|
|
sseInstance: null,
|
|
|
- abortController: null
|
|
|
+ abortController: null,
|
|
|
+ bodyClass: "bodyBackground_xcwk",
|
|
|
+ safeAreaInsets: {
|
|
|
+ left: 0,
|
|
|
+ top: 0,
|
|
|
+ right: 0,
|
|
|
+ bottom: 0
|
|
|
+ },
|
|
|
+ isHostInApp: false,
|
|
|
+ inAppPendingCard: null,
|
|
|
+ inAppSSEHandle: null
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
-
|
|
|
+ let self = this
|
|
|
+ try {
|
|
|
+ if (__private_xcwk_webViewId == undefined) {
|
|
|
+ self.bodyClass = "bodyBackground_normal"
|
|
|
+ } else {
|
|
|
+ self.bodyClass = "bodyBackground_xcwk"
|
|
|
+ self.isHostInApp = true
|
|
|
+ }
|
|
|
+ __xcwk_js2na("xcwkstd/getSafeAreaInsets", { }, function (safeAreaInsets) {
|
|
|
+ self.safeAreaInsets = safeAreaInsets
|
|
|
+ })
|
|
|
+ __xcwk_na2js(function (key, params) {
|
|
|
+ if (key == "keyboardWillShow") {
|
|
|
+ if (params.dict && params.dict.webViewId == __private_xcwk_webViewId) {
|
|
|
+ self.keyboardWillShow(params)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (key == "keyboardWillHide") {
|
|
|
+ if (params.dict && params.dict.webViewId == __private_xcwk_webViewId) {
|
|
|
+ self.keyboardWillHide(params)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (key == "sseRequestUpdate") {
|
|
|
+ self.doInAppSSE_ReceiveMessage(params.dict)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ __xcwk_js2na("xcwkstd/isLogin", { }, function (params) {
|
|
|
+ if (!params.isLogin) {
|
|
|
+ __xcwk_js2na("notebook/launchLogin", { }, function (params) { });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (err) {
|
|
|
+ self.bodyClass = "bodyBackground_normal"
|
|
|
+ }
|
|
|
},
|
|
|
methods: {
|
|
|
+ keyboardWillShow(params) {
|
|
|
+ this.safeAreaInsets.bottom = 0
|
|
|
+ },
|
|
|
+ keyboardWillHide(params) {
|
|
|
+ let self = this
|
|
|
+ __xcwk_js2na("xcwkstd/getSafeAreaInsets", { }, function (safeAreaInsets) {
|
|
|
+ self.safeAreaInsets = safeAreaInsets
|
|
|
+ })
|
|
|
+ },
|
|
|
scrollViewScrollToBottom() {
|
|
|
let self = this
|
|
|
this.$nextTick(function () {
|
|
@@ -103,10 +174,111 @@ export default {
|
|
|
"content" : question
|
|
|
})
|
|
|
this.$refs.inputAsk.value = ""
|
|
|
- this.doAsyncSSE(question)
|
|
|
+ this.doChatWithMessage(question)
|
|
|
+ },
|
|
|
+ doChatWithMessage(message) {
|
|
|
+ if (this.isHostInApp) {
|
|
|
+ let self = this
|
|
|
+ __xcwk_js2na("xcwkstd/isLogin", {}, function (params) {
|
|
|
+ if (params.isLogin) {
|
|
|
+ self.doInAppSSE(message)
|
|
|
+ } else {
|
|
|
+ __xcwk_js2na("notebook/launchLogin", {}, function (params) { });
|
|
|
+ self.insertMessageBlock({
|
|
|
+ "type" : "answer",
|
|
|
+ "content" : "请先登录App"
|
|
|
+ })
|
|
|
+ self.$refs.inputAsk.value = message
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.doAsyncSSE(message)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ doInAppSSE(message) {
|
|
|
+ let self = this
|
|
|
+
|
|
|
+ self.allowInput = false
|
|
|
+ let messageCard = {
|
|
|
+ "type" : "answer",
|
|
|
+ "content" : ""
|
|
|
+ }
|
|
|
+ self.inAppPendingCard = messageCard
|
|
|
+
|
|
|
+ let messageModel = []
|
|
|
+ for (let index in self.model) {
|
|
|
+ let model = self.model[index]
|
|
|
+ if (model.type == "ask") {
|
|
|
+ messageModel.push({
|
|
|
+ "role" : "user",
|
|
|
+ "content" : model.content
|
|
|
+ })
|
|
|
+ }
|
|
|
+ else if (model.type == "answer") {
|
|
|
+ messageModel.push({
|
|
|
+ "role" : "assistant",
|
|
|
+ "content" : model.content
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ self.insertMessageBlock(messageCard)
|
|
|
+ self.insertMessageBlock({
|
|
|
+ "type" : "answerTyping",
|
|
|
+ "content" : ""
|
|
|
+ })
|
|
|
+
|
|
|
+ __xcwk_js2na("xcwkstd/sseRequest", {
|
|
|
+ "path" : "/freeai/llm",
|
|
|
+ "data" : {
|
|
|
+ "stream" : true,
|
|
|
+ "messages" : messageModel
|
|
|
+ }
|
|
|
+ }, function (params) {
|
|
|
+ self.inAppSSEHandle = params.sseId
|
|
|
+ });
|
|
|
+ },
|
|
|
+ doInAppSSE_ReceiveMessage(message) {
|
|
|
+ let self = this
|
|
|
+ let messageCard = self.inAppPendingCard
|
|
|
+ console.log(JSON.stringify(message))
|
|
|
+ switch (message.type) {
|
|
|
+ case 0x1: { // data
|
|
|
+ let partModel = JSON.parse(message.data)
|
|
|
+ if (partModel["v"] != null) {
|
|
|
+ messageCard.content += partModel["v"]
|
|
|
+ self.scrollViewScrollToBottom()
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 0x3: { // error
|
|
|
+ if (messageCard.content.length < 5) {
|
|
|
+ messageCard.content = "回答出了一点问题"
|
|
|
+ }
|
|
|
+ self.sseInstance = null
|
|
|
+ self.allowInput = true
|
|
|
+ self.doShutdown()
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 0x4: { // close
|
|
|
+ self.sseInstance = null
|
|
|
+ self.allowInput = true
|
|
|
+ self.abortController = null
|
|
|
+ self.inAppPendingCard = null
|
|
|
+ self.inAppSSEHandle = null
|
|
|
+ for (let id = 0; id < self.model.length; id++) {
|
|
|
+ if (self.model[id].type == "answerTyping") {
|
|
|
+ self.model.splice(id, 1)
|
|
|
+ id--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
},
|
|
|
doAsyncSSE(message) {
|
|
|
let self = this
|
|
|
+
|
|
|
self.allowInput = false
|
|
|
let messageCard = {
|
|
|
"type" : "answer",
|
|
@@ -257,6 +429,22 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ if (self.inAppSSEHandle != null) {
|
|
|
+ __xcwk_js2na("xcwkstd/cancelSSERequest", {
|
|
|
+ "sseId" : self.inAppSSEHandle
|
|
|
+ }, function (params) { })
|
|
|
+ self.inAppSSEHandle = null
|
|
|
+ self.sseInstance = null
|
|
|
+ self.allowInput = true
|
|
|
+ self.abortController = null
|
|
|
+ self.inAppPendingCard = null
|
|
|
+ for (let id = 0; id < self.model.length; id++) {
|
|
|
+ if (self.model[id].type == "answerTyping") {
|
|
|
+ self.model.splice(id, 1)
|
|
|
+ id--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|