123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- <template>
- <view>
- <image class="page-bg" src="https://cos.his.cdwjyyh.com/fs/20250506/730750385fab4dd5a8a268b1550c81d6.png"
- mode="widthFix" style="width: 100%;"></image>
- <view class="nav-bar" :style="{height: `calc(88rpx + ${statusBarHeight}px)`}">
- <view class="statusBar" :style="{height: statusBarHeight + 'px'}"></view>
- <image class="nav-bg" src="https://cos.his.cdwjyyh.com/fs/20250506/730750385fab4dd5a8a268b1550c81d6.png"
- mode="widthFix"></image>
- <view class="nav-bar-box">
- <view class="uni-page-head-hd es-mr-24" @click="$navBack()">
- <uni-icons type="left" size="26" color="#000000"></uni-icons>
- </view>
- <view class="nav-bar-left">
- <image :src="roleInfo.avatar||'https://fs-1319721001.cos.ap-chongqing.myqcloud.com/fs/20240229/be32b8d2ae9f497297d10327656bb43c.png'" mode="aspectFill"></image>
- <view class="nav-bar-head">
- <view class="nav-bar-name textOne">{{roleName}}</view>
- <view class="textOne" v-show="roleInfo.title">{{roleInfo.title}}</view>
- </view>
- <!-- <uni-icons type="more-filled" size="26" style="flex-shrink: 0;" color="#000000"></uni-icons> -->
- </view>
- </view>
- </view>
- <scroll-view class="msg-scroll" :scroll-top="scrollTop" scroll-y="true"
- :scroll-with-animation="true">
- <view class="container-body" :style="{paddingTop: `calc(100rpx + ${statusBarHeight}px)`}">
- <view class="banner-box" v-if="roleInfo.imageUrl||roleInfo.textDescription">
- <image :src="roleInfo.imageUrl" mode="aspectFill"></image>
- <view class="banner-txt" v-show="roleInfo.textDescription">{{roleInfo.textDescription}}</view>
- </view>
- <view class="ques-box" v-if="quesList&&quesList.length>0">
- <view v-for="(ques,i) in quesList" :key="i" @click="handleQues(ques)">{{ques}}</view>
- </view>
- <view class="TUI-message-list es-mt-24" @touchstart="handleTouchStart">
- <!-- <view class="loading-text" v-if="isCompleted">没有更多</view> -->
- <view v-for="(item, index) in msgs" :key="index" :id="'view' + index">
- <!-- <view class="time-container" v-if="item.showTime">{{ caculateTimeago(item.time * 1000) }}</view> -->
- <view :class="item.type == 1 ? 'msg-item my-msg':'msg-item ai-msg'">
- <image v-if="item.type == 1" class="avatar" :src="userInfo.avatar||'https://fs-1319721001.cos.ap-chongqing.myqcloud.com/fs/20240229/1d7eb0607a074892964dd32e8735e540.jpg'" mode="aspectFill"></image>
- <image v-if="item.type != 1" class="avatar" :src="roleInfo.avatar||'https://fs-1319721001.cos.ap-chongqing.myqcloud.com/fs/20240229/be32b8d2ae9f497297d10327656bb43c.png'" mode="aspectFill"></image>
- <view class="msg-text"><text>{{item.content}}</text></view>
- </view>
- </view>
- </view>
- </view>
- </scroll-view>
- <view class="chatinput">
- <u--input placeholder="请输入想要咨询的问题" border="none" v-model.trim="inputText" clearable @confirm="handleInput"></u--input>
- <!-- <image src="/static/images/update_pic_icon.png" mode="aspectFill" class="icon" @click="choosePic">
- </image> -->
- <image src="/static/images/send_message_icon.png" mode="aspectFill" class="icon2" :style="{opacity:inputText?1:0.7}"
- @click="handleInput"></image>
- </view>
- </view>
- </template>
- <script>
- import { getSessionDetailInfo } from "@/api/ai.js"
- export default {
- data() {
- return {
- sessionId: '',
- statusBarHeight: uni.getSystemInfoSync().statusBarHeight,
- roleName: '',
- chatList: [],
- quesList:[],
- roleInfo: {},
- inputText: '',
- triggered: false,
- isCompleted: false,
- scrollTop: 0,
- oldMessageTime: 0,
- inputText:"",
- roleId:null,
- msgTimes:null,
- isSocketOpen:false,
- socket:null,
- isSend:true,
- userInfo: {},
- doctorId: '',
- sessionId: null,
- msgEnd: false,
- msgs: []
- }
- },
- onLoad(options) {
- this.roleName = options.roleName || ''
- this.roleId = options.roleId || ''
- this.userInfo = uni.getStorageSync("userInfo") ? JSON.parse(uni.getStorageSync("userInfo")) : {}
- this.sessionId = options.sessionId || null
- this.getSessionDetailInfo()
- this.initSocket();
- },
- // 监听数据初次渲染,展示最新一条消息
- // TODO app 中获取不到DOM 元素
- onReady() {
- this.handleScrollBottom();
- },
- mounted() {
- // this.handleShowTime();
- },
- methods: {
- back() {
- uni.navigateBack()
- },
- getSessionDetailInfo() {
- let that = this
- getSessionDetailInfo(this.sessionId,this.roleId).then(res=>{
- if(res.code == 200) {
- let chatList = res.chatList
- this.roleInfo = res.roleInfo;
- this.quesList = res.roleInfo&&res.roleInfo.wordList ? res.roleInfo.wordList.split('||') : [];
- let list = []
- if(res.roleInfo.welcomeMessage) {
- list = [{
- sendType: 2,
- content: res.roleInfo.welcomeMessage
- }]
- }
- if(chatList&&chatList.length > 0) {
- list = list.concat(chatList)
- }
- list.forEach(function(value, index, array) {
- that.addMsg(value.sendType, value.content, 1);
- });
- }
- })
- },
- choosePic() {
- },
- handleQues(ques) {
- this.inputText = ques
- this.handleInput()
- },
- handleInput() {
- if(this.msgEnd || !this.isSocketOpen) {
- // 重新发起会话
- this.initSocket()
- } else {
- this.sendMsg();
- }
- },
- handleScrollBottom() {
- setTimeout(() => {
- uni.createSelectorQuery()
- .select('.container-body')
- .boundingClientRect((res) => {
- if(res) {
- const scrollH = res.height;
- this.scrollTop = res.height;
- }
- }).exec();
- },500);
- },
- // 滑动触发时,失焦收起键盘
- handleTouchStart() {
- uni.hideKeyboard();
- },
- handleShowTime() {
- if (this.msgs&&this.msgs.length>0) {
- this.msgs.forEach((item) => {
- if (item.time - this.oldMessageTime > 5 * 60) {
- this.oldMessageTime = item.time;
- item.showTime = true;
- } else {
- item.showTime = false;
- }
- });
- console.log(this.msgs)
- }
- },
- caculateTimeago(time) {
- return time
- },
- initSocket() {
- //创建一个socket连接
- var userId = this.userInfo.userId;
- var that = this;
- if (this.socket) {
- this.socket.close()
- this.socket = null;
- }
- this.socket = uni.connectSocket({
- url: getApp().globalData.aiWSUrl + "/app/interestAiWebSocket/" + userId,
- multiple: true,
- success: res => {
- console.log('WebSocket连接已打开1!');
- that.isSocketOpen = true
- },
- error: res => {
- console.log(res)
- },
- })
- this.socket.onMessage((res) => {
- console.log("收到消息",res)
- that.isSend = true;
- that.addMsg(2, res.data, 2);
- })
- //监听socket打开
- this.socket.onOpen(() => {
- console.log('WebSocket连接已打开2!');
- that.isSocketOpen = true
- if(that.msgEnd) {
- // 重新发起会话
- that.isSend = true;
- that.sendMsg();
- }
- })
- //监听socket关闭
- this.socket.onClose(() => {
- that.isSocketOpen = false
- that.socket = null
- console.log('WebSocket连接已关闭!');
- that.msgEnd = true
- that.handleScrollBottom();
- if(that.msgTimes) {
- clearInterval(that.msgTimes)
- that.msgTimes = null
- }
- })
- //监听socket错误
- this.socket.onError(() => {
- that.isSocketOpen = false
- that.socket = null
- that.msgEnd = true
- console.log('WebSocket连接打开失败');
- if(that.msgTimes) {
- clearInterval(that.msgTimes)
- that.msgTimes = null
- }
- })
-
-
- },
- sendMsg() {
- if (this.inputText == "") {
- return;
- }
- if (!this.isSend) {
- return;
- }
- if (this.isSocketOpen) {
- var userId = this.userInfo.userId;
- var data = {
- userId: this.userInfo.userId,
- roleId: this.roleId,
- nickName: this.userInfo.nickName,
- avatar: this.userInfo.avatar,
- roleName: this.roleName,
- message: this.inputText,
- sessionId: this.sessionId,
- };
- this.socket.send({
- data: JSON.stringify(data),
- success: () => {
- console.log("发送成功")
- this.addMsg(1, this.inputText, 1);
- this.addMsg(2, "正在思考中...", 1);
- this.isSend = false;
- },
- fail: () => {
- console.log("发送失败")
- }
- });
-
- }
-
- },
- addMsg(type, content, inputType) {
- this.msgEnd = false
- var obj = {
- type: type,
- content: content
- }
- if (inputType == 2) {
- this.msgs.splice(-1);
- this.msgs.push(obj);
- } else if (inputType == 1) {
- this.msgs.push(obj)
- }
- this.inputText = ""
- var that = this;
- that.handleScrollBottom();
- //先确保清除了之前的消息定时器
- if(that.msgTimes) {
- clearInterval(that.msgTimes)
- that.msgTimes = null
- }
- // 5分钟无消息自动结束
- that.msgTimes=setInterval(()=>{
- console.log("5分钟无消息自动结束")
- clearInterval(that.msgTimes)
- if(this.socket!=null){
- this.socket.close()
- }
- that.msgTimes = null
- },300000)
- },
- },
- onUnload() {
- this.msgEnd = true
- if(this.socket!=null){
- this.socket.close()
- }
- if(this.msgTimes) {
- clearInterval(this.msgTimes)
- this.msgTimes = null
- }
- uni.$off('refreshOrderPatient')
- }
- }
- </script>
- <style scoped lang="scss">
- @mixin u-flex($flexD, $alignI, $justifyC) {
- display: flex;
- flex-direction: $flexD;
- align-items: $alignI;
- justify-content: $justifyC;
- }
- .nav-bar {
- position: fixed;
- z-index: 9999;
- top: 0;
- left: 0;
- width: 100%;
- overflow: hidden;
- .nav-bg {
- width: 100%;
- height: 100%;
- position: absolute;
- left: 0;
- top: 0;
- z-index: 1;
- background-color: #fff;
- }
- &-box {
- position: relative;
- padding: 0 24rpx;
- @include u-flex(row, center, flex-start);
- height: 88rpx;
- box-sizing: border-box;
- z-index: 3;
- }
- &-left {
- width: 100%;
- @include u-flex(row, center, flex-start);
- overflow: hidden;
- image {
- flex-shrink: 0;
- width: 64rpx;
- height: 64rpx;
- border-radius: 12rpx 12rpx 12rpx 12rpx;
- }
- }
- &-name {
- font-family: PingFang SC, PingFang SC;
- font-weight: 600;
- font-size: 28rpx;
- color: #222222;
- }
- &-head {
- flex: 1;
- overflow: hidden;
- margin-left: 22rpx;
- margin-right: 22rpx;
- font-family: PingFang SC, PingFang SC;
- font-weight: 400;
- font-size: 23rpx;
- color: #999999;
- }
- }
- .page-bg {
- position: absolute;
- top: 0;
- left: 0;
- }
- .container-body {
- position: relative;
- padding: 32rpx 30rpx;
- z-index: 2;
- @include u-flex(column, flex-star, center);
- .banner-box {
- width: 100%;
- min-width: 686rpx;
- overflow: hidden;
- background: rgba(255, 255, 255, 0.9);
- border-radius: 24rpx 24rpx 24rpx 24rpx;
- border: 4rpx solid #FFFFFF;
- box-sizing: border-box;
- image {
- width: 100%;
- min-width: 690rpx;
- height: 264rpx;
- background: #f7f7f7;
- }
- }
- .banner-txt {
- padding: 32rpx;
- font-family: PingFang SC, PingFang SC;
- font-weight: 400;
- font-size: 32rpx;
- color: #333333;
- word-break: break-all;
- }
- .ques-box {
- @include u-flex(row, flex-star, flex-star);
- flex-wrap: wrap;
- margin: 22rpx -8rpx -10rpx;
- view {
- padding: 12rpx 36rpx;
- background: #FFFFFF;
- border-radius: 16rpx 16rpx 16rpx 16rpx;
- border: 1rpx solid #ECECEC;
- font-family: PingFang SC, PingFang SC;
- font-weight: 500;
- font-size: 28rpx;
- color: #333333;
- margin: 10rpx 8rpx;
- }
- }
- }
- .chatinput {
- position: fixed;
- left: 32rpx;
- right: 32rpx;
- z-index: 999;
- bottom: calc(var(--window-bottom) + 24rpx);
- height: 96rpx;
- background-color: green;
- background: #FFFFFF;
- box-shadow: 0rpx 8rpx 21rpx 0rpx rgba(0, 0, 0, 0.1);
- border-radius: 24rpx 24rpx 24rpx 24rpx;
- @include u-flex(row, center, center);
- padding: 0 24rpx;
- box-sizing: border-box;
-
- .icon {
- height: 48rpx;
- width: 48rpx;
- margin-left: 32rpx;
- }
-
- .icon2 {
- height: 56rpx;
- width: 56rpx;
- margin-left: 32rpx;
- }
- }
- .msg-scroll {
- height: calc(100vh - var(--window-bottom) - 120rpx);
- }
- .TUI-message-list {
- width: 100%;
- box-sizing: border-box;
- .time-container {
- font-family: PingFang SC, PingFang SC;
- font-weight: 400;
- font-size: 24rpx;
- color: #999999;
- padding: 36rpx 0;
- margin: 10px;
- text-align: center;
- }
- .avatar {
- flex-shrink: 0;
- width: 88rpx;
- height: 88rpx;
- background: #FFFFFF;
- border-radius: 12rpx 12rpx 12rpx 12rpx;
- }
- .msg-item {
- margin-bottom: 24rpx;
- }
- .ai-msg {
- @include u-flex(row, flex-start, flex-start);
- .avatar {
- margin-right: 24rpx;
- }
- .msg-text {
- border-radius: 0rpx 24rpx 24rpx 24rpx;
- }
- }
- .my-msg {
- @include u-flex(row-reverse, flex-start, flex-start);
- .avatar {
- margin-left: 24rpx;
- }
- .msg-text {
- border-radius: 24rpx 0 24rpx 24rpx;
- background: #FEC75C;
- }
- }
- .msg-text {
- padding: 24rpx;
- background: #FFFFFF;
- overflow: hidden;
- }
- }
- </style>
|