long
2023-03-03 9860e221460a0a4ac1903dad2c97160d0eed0e63
提交 | 用户 | age
36e1de 1 <template>
L 2   <div>
3     <ul ref="chatList" class="chat-list" :style="`height:${height};`">
4
5       <li v-for="(item, index) in list" :key="index">
6         <!-- 时间 -->
7         <div v-if="item.time_tx" class="flex"><div class="chat-time">{{ item.time_tx }}</div></div>
8
9         <!-- 聊天主体 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ -->
10         <!-- 通常主体 有用户信息 -->
11         <div class="chat-item" :class="{'me-item': getDirection(item.fromUser)=='right'}">
12           <img :src="item.avatar" alt="" class="avatar" fit="cover">
13           <div class="info">
14             <!-- ({{ item.msgtype }}) -->
15             <div class="name">{{ item.name }}</div>
16
17             <!-- 消息内容 ↓↓↓↓↓↓↓↓↓↓ -->
18             <!-- 文本 -->
19             <textPop v-if="item.msgtype === 'text'" :data="item.content" :direction="getDirection(item.fromUser)" />
20             <!-- 图片 -->
21             <imagePop v-if="item.msgtype === 'image'" :data="item" :direction="getDirection(item.fromUser)" />
22             <!-- 消息内容 ↑↑↑↑↑↑↑↑↑↑ -->
23           </div>
24         </div>
25         <!-- 聊天主体 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ -->
26       </li>
27
28       <div v-if="loading" class="text-center">
29         <div class="ball-rotate"><div /></div>
30       </div>
31     </ul>
32
33     <div class="handle-wrap flex flex-ver">
34       <input v-model="myTalk" type="text" class="input flex-1" maxlength="100" @keyup.enter="handleSend">
35       <div class="send-btn" @click="handleSend">发送</div>
36     </div>
37
38     <!-- audioFileUrl -->
39     <audio ref="refAudio" src="">
40       您的浏览器不支持 audio 标签。
41     </audio>
42   </div>
43 </template>
44
45 <script>
46 // const listMockData = require('./chat_list_mockData')
47 const chatFn = require('@/utils/chat/chat.js')
48 import imagePop from './msg/imagePop.vue'
49 import textPop from './msg/textPop.vue'
50 import config from '../../config/index'
51 export default {
52   name: 'ChatList',
53   components: { textPop, imagePop },
54   mixins: [],
55   props: {
56     // 高度
57     height: {
58       type: String,
59       default: '100vh'
60     }
61   },
62   data() {
63     return {
64       userId: '',
65       myTalk: '',
66
67       // 列表数据
68       loading: false,
69       list: []
70     }
71   },
72   mounted() {
73     this.init()
74   },
75   methods: {
76     init() {
77       this.userId = this.getQueryString('userId')
78       console.log('this.userId', this.userId)
79       if (!this.userId) window.appToast('缺少userID,请重新进入')
80     },
81
82     // 判断位置 return 'left' 'right'
83     getDirection(itemUserId) {
84       return itemUserId === 'mySelf' ? 'right' : 'left'
85     },
86
87     // 点击发送
88     handleSend() {
89       if (this.loading) return window.appToast('操作过快,请稍后')
90       if (!this.userId) return window.appToast('缺少userID,请重新进入')
91
92       const myTalk = JSON.parse(JSON.stringify(this.myTalk))
93       const list = JSON.parse(JSON.stringify(this.list))
94
95       if (!myTalk) return window.appToast('请输入内容')
96       this.myTalk = ''
97
98       list.push({
99         content: { 'content': myTalk },
100         name: '我',
101         msgtime: this.$moment().format('yyyy-MM-DD HH:mm:ss'),
102         action: 'send',
103         id: this.$moment().format('yyyyMMDDHHmmss'),
104         avatar: require('@/assets/imgs/mySelf.png'),
105         msgtype: 'text',
106         fromUser: 'mySelf'
107       })
108
109       // 处理聊天显示的日期
110       list && list.forEach((item, index) => {
111         if (index !== 0) item.time_tx = chatFn.chatTime(item.msgtime, list[index - 1] && list[index - 1].msgtime)
112       })
113       this.list = list
114
115       this.$nextTick(() => { this.scrollBottom() })
116
117       this.getData(myTalk)
118     },
119
120     // 获取ai返回内容
121     getData(keyWord) {
122       console.log('getChatList')
123       var { loading, userId } = this
124       const list = JSON.parse(JSON.stringify(this.list))
125       if (loading) return
126
127       this.loading = true
128
129       const url = config.is_use_test_server ? 'crm-user/chatGpt/chat' : 'chat/msg'
130
131       this.Req.http.post({
132         url: url,
133         data: {
134           msg: keyWord,
135           userId: userId
136         },
137         udData: { noLoading: true, nokey: true },
138         mockData: {
139           code: 100,
140           msg: '',
141           data: {
142             text: '机器人回复'
143           }
144         }
145       }).then(res => {
146         this.loading = false
147
148         if (res.data) {
149           const content = res.data.replace(/\n/g, '</br>')
150           list.push({
151             content: { 'content': content },
152             name: 'AI',
153             msgtime: this.$moment().format('yyyy-MM-DD HH:mm:ss'),
154             action: 'send',
155             id: this.$moment().format('yyyyMMDDHHmmss'),
156             avatar: require('@/assets/imgs/AI.png'),
157             msgtype: 'text',
158             fromUser: 'robot'
159           })
160         }
161
162         // 处理聊天显示的日期
163         list && list.forEach((item, index) => {
164           if (index !== 0) item.time_tx = chatFn.chatTime(item.msgtime, list[index - 1] && list[index - 1].msgtime)
165         })
166
167         this.list = list
168
169         this.$nextTick(() => { this.scrollBottom() })
170       }).catch(res => {
171         this.loading = false
172         const warnText = res.msg || ('请稍后再试...')
173         window.appToast(warnText)
174       })
175     },
176
177     scrollBottom() {
178       this.$nextTick(() => {
179         this.$refs.chatList.scrollTop = this.$refs.chatList.scrollHeight
180       })
181     }
182   }
183 }
184 </script>
185
186 <style scoped>
187 .loading-tx{
188   font-size: 22px;
189   color: #999;
190 }
191
192 .chat-list{
193   overflow-y: auto;
194   background-color: #fff;
195   padding-top: 40px;
196   box-sizing: border-box;
197   padding-bottom: 140px;
198 }
199
200 .chat-time{
201   background-color: #DADADA;
202   color: #fff;
203   font-size: 24px;
204   line-height: 1.2;
205   padding: 4px 0;
206   border-radius: 6px;
207   text-align: center;
208   margin: 6px auto 10px;
209   padding: 4px 10px;
210 }
211
212 .chat-item{
213   margin-bottom: 20px;
214   padding: 0 20px ;
215 }
216 .chat-item .avatar{
217   height: 60px;
218   width: 60px;
219   display: block;
220   margin-right: 10px;
221   border-radius: 4px;
222   float: left;
223 }
224 .chat-item .info{
225   /* padding-top: 4px; */
226   display: inline-block;
227   text-align: left;
228 }
229 .chat-item .info .name{
230   line-height: 1.2;
231   height: 1.2em;
232   margin-bottom: 4px;
233   font-size: 22px;
234 }
235 .chat-item.me-item{
236   text-align: right;
237 }
238 .chat-item.me-item .avatar{
239   float: right;
240   margin-right: 0;
241   margin-left: 10px;
242 }
243 .chat-item.me-item .name{
244   text-align: right;
245 }
246
247 .handle-wrap{
248   background-color: #F7F7F7;
249   position: fixed;
250   bottom: 0;
251   left: 0;
252   width: 100%;
253   padding: 20px 30px 60px 30px;
254   box-sizing: border-box;
255 }
256 .handle-wrap .input{
257   height: 60px;
258   line-height: 30px;
259   font-size: 26px;
260   border: none;
261   background-color: #fff;
262   border-radius: 6px;
263   outline: none;
264 }
265 .handle-wrap .input:focus{
266   border: none;
267 }
268 .handle-wrap .send-btn{
269   background-color: #45B1D7;
270   height: 60px;
271   line-height: 60px;
272   color: #fff;
273   padding: 0 20px;
274   border-radius: 6px;
275   letter-spacing: 4px;
276   margin-left: 20px;
277   font-size: 28px;
278 }
279
280 @keyframes ball-rotate {
281   0% {transform: rotate(0deg) scale(1); }
282
283   50% {transform: rotate(180deg) scale(0.6);}
284
285   100% {transform: rotate(360deg) scale(1);}
286 }
287 .ball-rotate{position: relative;width: 100px;height: 70px;margin: 0 auto 20px;}
288 .ball-rotate > div {
289     background-color: #000;
290     width: 30px;
291     height: 30px;
292     border-radius: 100%;
293     margin: 4px;
294     animation-fill-mode: both;
295     position: absolute;
296     top: 50%;
297     left: 50%;
298     margin: -16px 0 0 -16px;
299 }
300 .ball-rotate > div:first-child {
301     animation: ball-rotate 1s 0s cubic-bezier(.7, -.13, .22, .86) infinite;
302 }
303 .ball-rotate > div:before, .ball-rotate > div:after {
304     background-color: #000;
305     width: 30px;
306     height: 30px;
307     border-radius: 100%;
308     margin: 4px;
309     content: ""!important;
310     position: absolute;
311     opacity: .8;
312 }
313 .ball-rotate > div:before {top: 0px;left: -56px}
314 .ball-rotate > div:after {top: 0px;left: 50px}
315 </style>