consult.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <script setup lang="ts">
  2. import type { type_dyaw_xlfw_zxhd, type_dyaw_xlfw_zxhd_log } from '~/types';
  3. import user from '~/store/user';
  4. import { createSocket, socketSend } from '~/utils/ws';
  5. import type { TSocketRes } from '~/utils/ws';
  6. import { formatTimestamp } from '~/utils/time'
  7. const router = useRouter()
  8. let teacher
  9. const SessionConsultTeacher = sessionStorage.getItem('consult_teacher')
  10. if (SessionConsultTeacher !== null) {
  11. teacher = JSON.parse(SessionConsultTeacher)
  12. } else {
  13. router.back()
  14. }
  15. const dyaw_xlfw_zxhd: type_dyaw_xlfw_zxhd = (await request({
  16. url: '/dyaw/xlfw_zxhd/add',
  17. data: {
  18. dyaw_xlfw_zxhd: {
  19. dxz_stu_user_id: user.user_id,
  20. dxz_tea_user_id: teacher.user_id
  21. }
  22. }
  23. })).data.one_info
  24. const ChatAudioRef = $ref<typeof import('~/components/chat-audio/index.vue')['default']>()
  25. let rateDialogVisible = $ref(false)
  26. let endTime = $ref<string>()
  27. let rateNum = $ref(5)
  28. function handleClickEnd() {
  29. // ChatAudioRef && ChatAudioRef.open()
  30. endTime = getDatabaseTime(new Date())
  31. rateDialogVisible = true
  32. }
  33. function handleConfirmRate() {
  34. request({
  35. url: '/dyaw/xlfw_zxhd/edit',
  36. data: {
  37. dxz_id: dyaw_xlfw_zxhd.dxz_id,
  38. dyaw_xlfw_zxhd: {
  39. dxz_star: rateNum,
  40. dxz_star_datetime: endTime,
  41. dxz_status: '1'
  42. }
  43. }
  44. }).then(res => {
  45. if (res.code === '1') {
  46. rateDialogVisible = false
  47. router.back()
  48. }
  49. })
  50. }
  51. let infoList = $ref<Array<type_dyaw_xlfw_zxhd_log>>([])
  52. let inputValue = $ref('')
  53. let isSending = $ref(false)
  54. let TinyRef = $ref<typeof import('~/components/tinymce-area/index.vue')['default']>()
  55. async function handleClickSend() {
  56. if (isSending) return;
  57. isSending = true
  58. const reqDate = {
  59. dxz_id: dyaw_xlfw_zxhd.dxz_id,
  60. dxzl_stu_user_id: dyaw_xlfw_zxhd.dxz_stu_user_id,
  61. dxzl_stu_user_realname: dyaw_xlfw_zxhd.dxz_stu_user_realname,
  62. dxzl_tea_user_id: dyaw_xlfw_zxhd.dxz_tea_user_id,
  63. dxzl_tea_user_realname: dyaw_xlfw_zxhd.dxz_tea_user_realname,
  64. dxzl_last_msg_content: encodeURIComponent(inputValue),
  65. dxzl_type: inputValue.includes('><img') ? '2' : '1'
  66. }
  67. // infoList.push({
  68. // create_user_id: user.user_id,
  69. // create_dateline: Date.now().toString().slice(0, 10),
  70. // ...reqDate
  71. // })
  72. TinyRef?.clear()
  73. // console.log('inputValue :>> ', inputValue);
  74. request({
  75. url: '/dyaw/xlfw_zxhd_log/add',
  76. data: {
  77. dyaw_xlfw_zxhd_log: reqDate
  78. }
  79. }).then(res => {
  80. if (res.code === '1') {
  81. const fullSendData = {
  82. create_user_id: user.user_id,
  83. create_dateline: Date.now().toString().slice(0, 10),
  84. ...reqDate,
  85. dxzl_id: `${res.data.insert_id}`
  86. }
  87. infoList.push(fullSendData)
  88. socketSend(ws, fullSendData)
  89. isSending = false
  90. }
  91. })
  92. }
  93. request({
  94. url: '/dyaw/xlfw_zxhd_log/index',
  95. data: {
  96. dxz_id: dyaw_xlfw_zxhd.dxz_id,
  97. limit: 100
  98. }
  99. }).then(res => {
  100. if (res.code === '1') {
  101. infoList = res.data.page_data.reverse()
  102. }
  103. })
  104. const ws = createSocket(
  105. { teacher: teacher.user_id, student: user.user_id },
  106. {
  107. message(socketRes: TSocketRes<type_dyaw_xlfw_zxhd_log>) {
  108. if (socketRes.from_client_name.endsWith('teacher')) {
  109. infoList.push(socketRes.content)
  110. }
  111. }
  112. }
  113. )
  114. const scrollbarRef = $ref<HTMLElement>()
  115. function scrollToBottom() {
  116. if (!scrollbarRef) return;
  117. const scrollHeight = scrollbarRef!.scrollHeight;
  118. scrollbarRef!.scrollTo(0, scrollHeight);
  119. }
  120. watch(
  121. () => (infoList),
  122. () => {
  123. nextTick(() => {
  124. scrollToBottom()
  125. })
  126. },
  127. {
  128. deep: true,
  129. }
  130. )
  131. </script>
  132. <template>
  133. <div class="h-640px bg-hex-f2f2f295 flex justify-center divide-x">
  134. <div class="w-1000px h-full divide-y">
  135. <div class="bg-pink-300 flex justify-between h-48px items-center px-18px">
  136. <span class="text-lg px-1 tracking-wider">{{`${dyaw_xlfw_zxhd.dxz_tea_user_realname}老师正在为您服务`}}</span>
  137. <div class="text-pink-300 text-sm bg-white w-100px h-32px flex_center rounded-2xl cursor-pointer"
  138. @click="handleClickEnd">结束会话</div>
  139. </div>
  140. <div ref="scrollbarRef"
  141. class="bg-hex-fff8fb space-y-2 h-410px py-2 px-6 scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent">
  142. <div class="w-full text-center text-sm">{{ formatTimestamp(dyaw_xlfw_zxhd.create_dateline) }} 开始沟通</div>
  143. <info-item v-for="item in infoList" :key="item.dxzl_id" :left="item.create_user_id === user.user_id"
  144. :d="item"></info-item>
  145. </div>
  146. <div class="bg-white h-180px p-5px flex flex-col justify-between">
  147. <!-- <div class="h-48px"></div>
  148. <el-input type="textarea"></el-input> -->
  149. <tinymce-area v-model="inputValue" ref="TinyRef"></tinymce-area>
  150. <div class="flex justify-end">
  151. <div class="bg-pink-300 text-sm text-white w-80px h-32px flex_center rounded-2xl cursor-pointer"
  152. @click="handleClickSend">发送</div>
  153. </div>
  154. </div>
  155. </div>
  156. <div
  157. class="w-400px h-full bg-white overflow-y-auto flex flex-col items-center px-30px py-4 divide-y scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent">
  158. <div class="w-full"></div>
  159. <div>
  160. </div>
  161. </div>
  162. </div>
  163. <el-dialog v-model="rateDialogVisible" title="请评价" align-center width="405px">
  164. <p>于 {{ endTime }} 结束沟通</p>
  165. <div class="flex items-center my-4">
  166. <div>评价:</div>
  167. <el-rate size="large" v-model="rateNum"></el-rate>
  168. </div>
  169. <template #footer>
  170. <div class="flex_center">
  171. <el-button round type="primary" @click="handleConfirmRate">
  172. 确认
  173. </el-button>
  174. <el-button round @click="rateDialogVisible = false">取消</el-button>
  175. </div>
  176. </template>
  177. </el-dialog>
  178. <chat-audio ref="ChatAudioRef"></chat-audio>
  179. </template>
  180. <style scoped lang="scss">
  181. </style>