|
@@ -1,87 +1,75 @@
|
|
|
<template>
|
|
|
- <view>
|
|
|
- <cu-custom bgColor="bg-cyan" :isBack="$squni.getPrePage() != null">
|
|
|
- <block slot="backText">返回</block>
|
|
|
- <block slot="content">AirSmartChat</block>
|
|
|
- </cu-custom>
|
|
|
- <view class="cu-chat">
|
|
|
- <block v-for="(x,i) in msgList" :key="i">
|
|
|
- <!-- 用户消息 -->
|
|
|
- <view v-if="x.my && x.type === 'msg'" class="cu-item self"
|
|
|
- :class="[i === 0 ? 'first' : '', i === 1 ? 'sec' : '']">
|
|
|
- <view class="main">
|
|
|
- <view class="content bg-cyan shadow"><!-- @click="x.msg && $squni.copy(x.msg)" -->
|
|
|
- <text>{{ x.msg }}</text>
|
|
|
+ <view class="w100 h100 flex">
|
|
|
+ <!-- 侧边栏 -->
|
|
|
+ <navbar :chatAsset="chatAsset" @click="getInputList" @history="getHistory" @delete="msgList = []" />
|
|
|
+ <!-- 主体 -->
|
|
|
+ <view class="w100 relative">
|
|
|
+ <!-- 标题栏 -->
|
|
|
+ <view class="title w100 flex item-center content-center size-large">
|
|
|
+ {{ `AirSmartChat(${this.model})` }}
|
|
|
+ </view>
|
|
|
+ <!-- <view v-else class="flex item-center content-center p-10">
|
|
|
+ <view class="background-color-gray p-5 gap radius flex">
|
|
|
+ <button style="width: 400rpx;" v-for="item in chatgptVersion" :key="item"
|
|
|
+ :class="['change-button radius button-none', item === model ? 'background-color-white' : 'background-color-gray']"
|
|
|
+ @click="handleChangeVersion(item)">{{ item }}</button>
|
|
|
+ </view>
|
|
|
+ </view> -->
|
|
|
+ <!-- 聊天区 -->
|
|
|
+ <view class="chat size-large">
|
|
|
+ <block v-for="(x, i) in msgList" :key="i">
|
|
|
+ <!-- 用户消息 -->
|
|
|
+ <view v-if="x.my && x.type === 'msg'" class="chat-item " style="background-color: rgba(247,247,248,0.7);">
|
|
|
+ <view class="chat-content">
|
|
|
+ <view class="flex gap">
|
|
|
+ <img class="flex direction item-end" src="../../static/logo-100.png" width="24" height="24" />
|
|
|
+ <text>{{ x.msg }}</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <image class="cu-avatar round" src="/static/logo-100.png">
|
|
|
- <view v-if="i === 0" class="date">{{ x.date }}</view>
|
|
|
- </view>
|
|
|
- <!-- AI消息 -->
|
|
|
- <view v-if="!x.my && x.type === 'msg'" class="cu-item"
|
|
|
- :class="[i === 0 ? 'first' : '', i === 1 ? 'sec' : '']">
|
|
|
- <view class="flex flex-direction align-center">
|
|
|
- <image class="cu-avatar round chat-avatar" src="/static/robot.png">
|
|
|
- <text v-if="i === 0" class="cuIcon-title" :class="[statusColor]"></text>
|
|
|
+ <!-- AI消息 -->
|
|
|
+ <view v-if="!x.my && x.type === 'msg'" class="chat-item">
|
|
|
+ <view class="chat-content">
|
|
|
+ <view class="flex gap">
|
|
|
+ <img class="flex direction item-end" src="/static/chatgpt.png" width="24" height="24" />
|
|
|
+ <img v-if="x.pic" :src="x.msg" />
|
|
|
+ <text v-else>{{x.msg}}</text>
|
|
|
+ <uni-icons class="ml-auto cursor-pointer" type="download" color="#acacbe"
|
|
|
+ @click="x.msg && $squni.copy(x.msg)" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- <view class="main">
|
|
|
- <view class="content shadow"><!-- @click="x.msg && $squni.copy(x.msg)" -->
|
|
|
- <img v-if="x.pic" :src="x.msg" />
|
|
|
- <text v-else>{{ x.msg }}</text>
|
|
|
+ <!-- 报错信息 -->
|
|
|
+ <view v-if="x.type === 'error'" class="chat-item">
|
|
|
+ <view class="chat-content flex gap item-center">
|
|
|
+ <text class="cuIcon-roundclosefill text-red"></text> {{ x.msg }}
|
|
|
</view>
|
|
|
</view>
|
|
|
- <view v-if="i === 0" class="date">{{ x.date }}</view>
|
|
|
- </view>
|
|
|
- <view v-if="x.type === 'error'" class="cu-info">
|
|
|
- <text class="cuIcon-roundclosefill text-red "></text> {{ x.msg }}
|
|
|
- </view>
|
|
|
- </block>
|
|
|
-
|
|
|
- <view v-if="msgLoading" class="cu-item">
|
|
|
- <view class="flex flex-direction align-center">
|
|
|
- <image class="cu-avatar round chat-avatar" src="/static/robot.png">
|
|
|
+ </block>
|
|
|
+ </view>
|
|
|
+ <!-- 底部 -->
|
|
|
+ <view class="w100 p-20 absolute flex b0 direction item-center background-color-white" style="height: 180px">
|
|
|
+ <view class="w100">
|
|
|
+ <view class="list flex wrap gap">
|
|
|
+ <uni-transition v-for="item in inputTipList" :key="item.type" :duration="500" show
|
|
|
+ :mode-class="['fade', 'zoom-in']" @click="handleSendMsg(item)" :title="item.name"
|
|
|
+ custom-class="item box-shadow p-15 border-gray radius cursor-pointer space-nowrap overflow-ellipsis overflow-hidden hover">
|
|
|
+ {{ item.name }}
|
|
|
+ </uni-transition>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- <view class="main">
|
|
|
- <text class="cuIcon-loading2 cuIconfont-spin text-cyan"></text>
|
|
|
+ <!-- 输入框 -->
|
|
|
+ <view class="w100">
|
|
|
+ <view class="send-message relative box-shadow mt-10 radius border-gray">
|
|
|
+ <textarea class="w100" v-model="msg" auto-height maxlength="5000" cursor-spacing="10"
|
|
|
+ :adjust-position="false" :placeholder="loading ? 'AirSmart is loading...' : 'Send a message'"
|
|
|
+ @confirm="sendMsg" @input="handleInput" @keydown.tab.native.prevent="handleTab" />
|
|
|
+ <uni-icons class="send-btn absolute cursor-pointer" type="paperplane-filled" size="24"
|
|
|
+ :color="msg ? '#19c37d' : '#4559a480'" @click="sendMsg" />
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
-
|
|
|
- <view class="cu-bar foot input" :style="[{bottom:inputBottom+'px'}]">
|
|
|
- <view class="tip-box">
|
|
|
- <button class="cu-btn round shadow sm cuIcon line-cyan" @click="startNewChat">
|
|
|
- <text class="cuIcon-paint"></text>
|
|
|
- </button>
|
|
|
- <button class="cu-btn round shadow sm line-cyan"
|
|
|
- @click="$squni.navigateTo('/pages/main/history')">历史消息</button>
|
|
|
- <button class="cu-btn round shadow sm bg-orange"
|
|
|
- @click="$squni.navigateTo('/pages/main/prompt/prompt')">提示词</button>
|
|
|
- <button class="cu-btn round shadow sm line-cyan" @click="openBottomFunc">我的信息</button>
|
|
|
- </view>
|
|
|
- <!-- <view class="action func" @click="openBottomFunc">
|
|
|
- <text class="cuIcon-list text-cyan" style="font-size: 60upx;"></text>
|
|
|
- </view> -->
|
|
|
- <view class="input-msg">
|
|
|
- <ol class="tip-list">
|
|
|
- <li class="tip-item" v-for="item in inputTipList" :key="item.type" @click="handleSendMsg(item)">
|
|
|
- {{ item.name }}
|
|
|
- </li>
|
|
|
- </ol>
|
|
|
- <input v-model="msg" class="solid padding-lr" :adjust-position="false" :focus="false" maxlength="5000"
|
|
|
- cursor-spacing="10" :placeholder="loading ? '小AirSmart正在思考中,请稍后~' : '你可以问我任何问题或输入 “/” 获取模板'" @focus="inputFocus"
|
|
|
- @blur="inputBlur" @confirm="sendMsg" @input="handleInput"></input>
|
|
|
- </view>
|
|
|
- <!-- <view class="action">
|
|
|
- <text class="cuIcon-emojifill text-grey"></text>
|
|
|
- </view> -->
|
|
|
- <button class="cu-btn bg-cyan shadow" :disabled="loading" @click="sendMsg">
|
|
|
- <text class="cuIcon-loading2 cuIconfont-spin"
|
|
|
- v-if="loading || !ready"></text>{{ !ready ? '连接中' : '发送' }}
|
|
|
- </button>
|
|
|
- </view>
|
|
|
-
|
|
|
- <bottom-func v-if="bottomFuncShow" ref="bottomFunc" :chatAsset="chatAsset"
|
|
|
- @rewarded-video-ad="() => chatAsset.dfn += 2"></bottom-func>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
@@ -99,43 +87,27 @@
|
|
|
getUserChatAssetApi,
|
|
|
startNewChatApi,
|
|
|
findPromptListApi,
|
|
|
- msgList,
|
|
|
- sendMsgPic
|
|
|
+ sendMsgPic,
|
|
|
+ msgList
|
|
|
} from '@/api/chat.js'
|
|
|
- import BottomFunc from './bottom-func'
|
|
|
- const HELLO_MSG = {
|
|
|
- type: 'msg',
|
|
|
- my: false,
|
|
|
- msg: '连接中,请稍后~',
|
|
|
- date: dateFormat(new Date(), 'yyyy年MM月dd日 hh:mm')
|
|
|
- }
|
|
|
export default {
|
|
|
- components: {
|
|
|
- BottomFunc
|
|
|
- },
|
|
|
data() {
|
|
|
return {
|
|
|
loading: false,
|
|
|
userId: null,
|
|
|
- msgList: [HELLO_MSG],
|
|
|
+ msgList: [],
|
|
|
msgContent: "",
|
|
|
msg: "",
|
|
|
msgType: 0,
|
|
|
- inputBottom: 0,
|
|
|
- bottomFuncShow: false,
|
|
|
chatAsset: {},
|
|
|
assetType: 'n',
|
|
|
- statusColor: 'text-red',
|
|
|
- statusTimer: null,
|
|
|
msgLoading: false,
|
|
|
promptId: null,
|
|
|
curPrompt: {},
|
|
|
- inputTipList: []
|
|
|
- }
|
|
|
- },
|
|
|
- computed: {
|
|
|
- ready() {
|
|
|
- return this.statusColor === 'text-green'
|
|
|
+ inputTipList: [],
|
|
|
+ model: 'gpt-4',
|
|
|
+ // chatgpt版本
|
|
|
+ chatgptVersion: ['gpt-3.5', 'gpt-4']
|
|
|
}
|
|
|
},
|
|
|
watch: {
|
|
@@ -147,17 +119,6 @@
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- statusColor(n, o) {
|
|
|
- if (n === 'text-green') {
|
|
|
- let prompt = ''
|
|
|
- if (this.curPrompt && this.curPrompt.title) {
|
|
|
- prompt = `(提示词:${this.curPrompt.title})`
|
|
|
- }
|
|
|
- HELLO_MSG.msg = '你好,我是AirSmartChat机器人,可以帮你解答疑惑或提供灵感' + prompt
|
|
|
- } else {
|
|
|
- HELLO_MSG.msg = '连接中,请稍后~'
|
|
|
- }
|
|
|
- }
|
|
|
},
|
|
|
async onShow() {
|
|
|
await this.$ready
|
|
@@ -193,7 +154,6 @@
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- this.heartStatus()
|
|
|
try {
|
|
|
//建立socket连接
|
|
|
websocket.connectSocket(this.$config.wssUrl + '/tools/chat/user/' + this.userId, msg => {
|
|
@@ -206,6 +166,13 @@
|
|
|
console.log('websocket connectSocket error:' + error)
|
|
|
}
|
|
|
},
|
|
|
+ onLoad() {
|
|
|
+ msgList().then(res => {
|
|
|
+ if (res.code === 20000) {
|
|
|
+ this.inputTipList = res.data
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
onHide() {
|
|
|
this.closeSocket()
|
|
|
},
|
|
@@ -241,37 +208,37 @@
|
|
|
this.putMsg(this.msg, true)
|
|
|
this.msgLoading = true
|
|
|
this.loading = true
|
|
|
-
|
|
|
+
|
|
|
// ======== 开发环境模拟回复 ========
|
|
|
// return this.mockReply()
|
|
|
// ======== 开发环境模拟回复 ========
|
|
|
-
|
|
|
+
|
|
|
if (this.calcAsset() === false) {
|
|
|
this.loading = false
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
- if(this.msgType === 2){
|
|
|
+
|
|
|
+ if (this.msgType === 2) {
|
|
|
sendMsgPic({
|
|
|
userId: this.$squni.getStorageSync('userId'),
|
|
|
question: msg,
|
|
|
type: this.msgType
|
|
|
}).then(res => {
|
|
|
- if(res.code === 20000) {
|
|
|
+ if (res.code === 20000) {
|
|
|
JSON.parse(res.data.ack).map(i => {
|
|
|
this.putMsg(i.url, false, 'msg', true)
|
|
|
this.loading = false
|
|
|
this.msgLoading = false
|
|
|
this.msgType = 0
|
|
|
})
|
|
|
- }else{
|
|
|
+ } else {
|
|
|
this.putMsgError('机器人被拔网线了,请稍后再试~')
|
|
|
}
|
|
|
})
|
|
|
- }else{
|
|
|
+ } else {
|
|
|
// 发送消息: M1/M2
|
|
|
websocket.sendMessage(JSON.stringify({
|
|
|
- model: 'M1',
|
|
|
+ model: this.model,
|
|
|
msg: msg,
|
|
|
platform: this.$squni.getStorageSync('platform'),
|
|
|
openid: this.$squni.getStorageSync('openid'),
|
|
@@ -304,7 +271,7 @@
|
|
|
if (msgJson.codeKey) {
|
|
|
content += `[${msgJson.codeKey}]`
|
|
|
if (msgJson.codeKey === 'chat.asset_short') {
|
|
|
- this.openBottomFunc()
|
|
|
+
|
|
|
} else if (msgJson.codeKey.indexOf('chat.asset_') >= 0) {
|
|
|
this.chatAsset[this.assetType]++
|
|
|
}
|
|
@@ -327,35 +294,6 @@
|
|
|
this.msgList = [HELLO_MSG]
|
|
|
startNewChatApi()
|
|
|
},
|
|
|
- // sendMsgBak() {
|
|
|
- // let that = this
|
|
|
- // if (this.msg == "") {
|
|
|
- // return
|
|
|
- // }
|
|
|
- // this.msgContent += (this.userId + ":" + this.msg + "\n")
|
|
|
- // this.putMsg(this.msg, true)
|
|
|
- // this.loading = true
|
|
|
-
|
|
|
- // // ======== 开发环境模拟回复 ========
|
|
|
- // return this.mockReply()
|
|
|
- // // ======== 开发环境模拟回复 ========
|
|
|
-
|
|
|
- // sendMsgApi({
|
|
|
- // userId: this.userId + '',
|
|
|
- // question: this.msgContent
|
|
|
- // }).then(({
|
|
|
- // status,
|
|
|
- // data
|
|
|
- // }) => {
|
|
|
- // if (status === 'success') {
|
|
|
- // this.putMsg(data.ack, false)
|
|
|
- // this.msgContent += ("openai:" + this.msg + "\n")
|
|
|
- // } else {
|
|
|
- // this.putMsg(res.message || '机器人开小差了,请稍后再试~', false, 'error')
|
|
|
- // }
|
|
|
- // that.loading = false
|
|
|
- // })
|
|
|
- // },
|
|
|
calcAsset() {
|
|
|
if (this.chatAsset.dfn > 0) {
|
|
|
this.chatAsset.dfn--
|
|
@@ -365,7 +303,6 @@
|
|
|
this.assetType = 'n'
|
|
|
} else {
|
|
|
this.$squni.toast('剩余次数不足')
|
|
|
- this.openBottomFunc()
|
|
|
return false
|
|
|
}
|
|
|
},
|
|
@@ -418,52 +355,34 @@
|
|
|
})
|
|
|
}, 10000)
|
|
|
},
|
|
|
- heartStatus() {
|
|
|
- this.statusTimer = interval(() => {
|
|
|
- if (websocket.isOpen) {
|
|
|
- this.statusColor = 'text-green'
|
|
|
- } else if (this.statusColor === 'text-red') {
|
|
|
- this.statusColor = 'text-yellow'
|
|
|
- } else {
|
|
|
- this.statusColor = 'text-red'
|
|
|
- }
|
|
|
- }, this.statusTimer, 200)
|
|
|
- },
|
|
|
closeSocket() {
|
|
|
websocket.closeSocket()
|
|
|
- clearInterval(this.statusTimer)
|
|
|
- this.statusColor = 'text-red'
|
|
|
- },
|
|
|
- inputFocus(e) {
|
|
|
- this.inputBottom = e.detail.height
|
|
|
- },
|
|
|
- inputBlur(e) {
|
|
|
- this.inputBottom = 0
|
|
|
},
|
|
|
// 对话输入框输入'/'显示提示信息
|
|
|
handleInput(e) {
|
|
|
- if (e.detail.value.startsWith('/')) {
|
|
|
- msgList().then(res => {
|
|
|
- this.inputTipList = res.data
|
|
|
- })
|
|
|
- }else{
|
|
|
- this.inputTipList = []
|
|
|
- }
|
|
|
-
|
|
|
- if(e.detail.value === '') {
|
|
|
+ if (e.detail.value === '') {
|
|
|
this.msgType = 0
|
|
|
}
|
|
|
},
|
|
|
- handleSendMsg(e){
|
|
|
+ // 支持tab输入空格
|
|
|
+ insertInputTxt(className, insertTxt) {
|
|
|
+ var elInput = document.getElementsByClassName(className)
|
|
|
+ var startPos = elInput[0].selectionStart
|
|
|
+ var endPos = elInput[0].selectionEnd
|
|
|
+ if (startPos === undefined || endPos === undefined) return
|
|
|
+ var txt = elInput[0].value
|
|
|
+ var result = txt.substring(0, startPos) + insertTxt + txt.substring(endPos)
|
|
|
+ this.msg = elInput[0].value = result
|
|
|
+ // elInput[0].focus()
|
|
|
+ // elInput[0].selectionStart = startPos + insertTxt.length
|
|
|
+ // elInput[0].selectionEnd = startPos + insertTxt.length
|
|
|
+ },
|
|
|
+ handleTab() {
|
|
|
+ this.insertInputTxt('uni-textarea-textarea', '\t')
|
|
|
+ },
|
|
|
+ handleSendMsg(e) {
|
|
|
this.msgType = e.type
|
|
|
this.msg = e.name
|
|
|
- this.inputTipList = []
|
|
|
- },
|
|
|
- openBottomFunc() {
|
|
|
- this.bottomFuncShow = true
|
|
|
- this.$nextTick(() => {
|
|
|
- this.$refs.bottomFunc.open()
|
|
|
- })
|
|
|
},
|
|
|
mockReply() {
|
|
|
// 开发环境模拟回复
|
|
@@ -474,80 +393,78 @@
|
|
|
}, 1000)
|
|
|
return
|
|
|
}
|
|
|
+ },
|
|
|
+
|
|
|
+ getInputList(e) {
|
|
|
+ this.inputTipList = e
|
|
|
+ this.msgList = []
|
|
|
+ },
|
|
|
+
|
|
|
+ // 切換chatgpt版本
|
|
|
+ handleChangeVersion(e) {
|
|
|
+ this.model = e
|
|
|
+ },
|
|
|
+
|
|
|
+ getHistory(e) {
|
|
|
+ if (e) {
|
|
|
+ this.msgList = e.children
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
-<style>
|
|
|
- page {
|
|
|
- padding-bottom: 220upx;
|
|
|
+<style lang="scss" scoped>
|
|
|
+ uni-page-body {
|
|
|
+ height: 100%;
|
|
|
+ background-color: #FFF;
|
|
|
}
|
|
|
|
|
|
- .cu-chat .chat-avatar.cu-avatar {
|
|
|
- width: 82upx;
|
|
|
- height: 82upx;
|
|
|
+ .title {
|
|
|
+ height: 128rpx;
|
|
|
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
|
|
|
- .cu-item:not(.first) {
|
|
|
- padding-bottom: 0upx;
|
|
|
- }
|
|
|
-
|
|
|
- .cu-item.sec {
|
|
|
- padding-top: 0upx;
|
|
|
- }
|
|
|
+ .chat {
|
|
|
+ height: calc(100% - 245px);
|
|
|
+ overflow-y: auto;
|
|
|
|
|
|
- .cu-chat .cu-item>.main {
|
|
|
- max-width: calc(100% - 160upx);
|
|
|
- }
|
|
|
+ .chat-item {
|
|
|
+ width: 100%;
|
|
|
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
- .main .content {
|
|
|
- word-wrap: break-word;
|
|
|
- cursor: text;
|
|
|
- user-select: text;
|
|
|
- }
|
|
|
+ .chat-content {
|
|
|
+ max-width: 1600rpx;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 62rpx 40rpx;
|
|
|
+ cursor: text;
|
|
|
+ user-select: text;
|
|
|
+ }
|
|
|
|
|
|
- .cu-bar.foot {
|
|
|
- box-shadow: 0 -0.5px 1px rgba(0, 0, 0, 0.1);
|
|
|
- align-items: flex-end;
|
|
|
+ .date {
|
|
|
+ margin-top: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- .foot {
|
|
|
- padding-top: 20upx;
|
|
|
- padding-bottom: 60upx;
|
|
|
+ .item {
|
|
|
+ width: 24%;
|
|
|
}
|
|
|
|
|
|
- .foot .cu-btn {
|
|
|
- margin-right: 20upx;
|
|
|
- width: 200upx;
|
|
|
+ .list,
|
|
|
+ .send-message {
|
|
|
+ max-width: 1560rpx;
|
|
|
+ margin: 10px auto 0;
|
|
|
}
|
|
|
|
|
|
- .foot .action.func {
|
|
|
- margin-left: 30upx;
|
|
|
+ uni-textarea {
|
|
|
+ padding: 15px 20px;
|
|
|
+ line-height: 1.6;
|
|
|
}
|
|
|
|
|
|
- .foot .tip-box {
|
|
|
- position: absolute;
|
|
|
- top: -60upx;
|
|
|
- margin: 0 30upx;
|
|
|
- }
|
|
|
-
|
|
|
- .input-msg{
|
|
|
- width: 100%;
|
|
|
- .tip-list{
|
|
|
- margin: 0 40upx;
|
|
|
- padding: 0 40upx;
|
|
|
-
|
|
|
- .tip-item{
|
|
|
- cursor: pointer;
|
|
|
- color: #909399;
|
|
|
- line-height: 64upx;
|
|
|
- }
|
|
|
-
|
|
|
- .tip-item:hover{
|
|
|
- background: #d9ecff;
|
|
|
- color: #000;
|
|
|
- }
|
|
|
- }
|
|
|
+ .send-btn {
|
|
|
+ right: 30rpx;
|
|
|
+ top: 50%;
|
|
|
+ transform: translate(0, -50%) rotate(45deg);
|
|
|
}
|
|
|
</style>
|