123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- <script setup lang="ts">
- import type { type_dyaw_xlfw_zxhd, type_dyaw_xlfw_zxhd_log } from '~/types';
- import user from '~/store/user';
- import { createSocket, socketSend } from '~/utils/ws';
- import type { TSocketRes } from '~/utils/ws';
- import { formatTimestamp } from '~/utils/time'
- import { CHAT_STATUS, CHAT_OPERATION } from '~/types';
- const router = useRouter()
- let teacher
- const SessionConsultTeacher = sessionStorage.getItem('consult_teacher')
- if (SessionConsultTeacher !== null) {
- teacher = JSON.parse(SessionConsultTeacher)
- } else {
- router.back()
- }
- function formatter(e: string) {
- if (!e) return e
- // 转义字符串中的危险字符
- return e.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
- }
- let teacherInfo = $ref<{
- dxp_user_realname: string;
- dxp_user_phone: string;
- dxp_wx_qrcode: string;
- dxp_jj: string;
- dxp_user_avatar: string;
- }>()
- request({
- url: '/dyaw/xlfw_pbgl/detail',
- data: {
- user_id: teacher.user_id
- }
- }).then(res => {
- if (res.code === '1') {
- teacherInfo = res.data.one_info
- }
- })
- const dyaw_xlfw_zxhd: type_dyaw_xlfw_zxhd = (await request({
- url: '/dyaw/xlfw_zxhd/add',
- data: {
- dyaw_xlfw_zxhd: {
- dxz_stu_user_id: user.user_id,
- dxz_tea_user_id: teacher.user_id
- }
- }
- })).data.one_info
- let infoDialogVisible = $ref(false)
- let rateDialogVisible = $ref(false)
- let endTime = $ref<string>()
- let rateNum = $ref(5)
- function handleClickEnd() {
- // ChatAudioRef && ChatAudioRef.open()
- endTime = getDatabaseTime(new Date())
- rateDialogVisible = true
- }
- function handleConfirmRate() {
- request({
- url: '/dyaw/xlfw_zxhd/edit',
- data: {
- dxz_id: dyaw_xlfw_zxhd.dxz_id,
- dyaw_xlfw_zxhd: {
- dxz_star: rateNum,
- dxz_star_datetime: endTime,
- // 下行代码【可能】导致使用问题
- dxz_status: '1'
- }
- }
- }).then(res => {
- if (res.code === '1') {
- rateDialogVisible = false
- router.back()
- }
- })
- }
- let infoList = $ref<Array<type_dyaw_xlfw_zxhd_log>>([])
- let inputValue = $ref('')
- let isSending = $ref(false)
- let TinyRef = $ref<typeof import('~/components/tinymce-area/index.vue')['default']>()
- async function handleClickSend(val?: string) {
- if (isSending) return;
- isSending = true
- const reqDate = {
- dxz_id: dyaw_xlfw_zxhd.dxz_id,
- dxzl_stu_user_id: dyaw_xlfw_zxhd.dxz_stu_user_id,
- dxzl_stu_user_realname: dyaw_xlfw_zxhd.dxz_stu_user_realname,
- dxzl_tea_user_id: dyaw_xlfw_zxhd.dxz_tea_user_id,
- dxzl_tea_user_realname: dyaw_xlfw_zxhd.dxz_tea_user_realname,
- dxzl_last_msg_content: encodeURIComponent(val || formatter(inputValue)),
- dxzl_type: (val || formatter(inputValue)).includes('<img') ? '2' : '1'
- }
- // infoList.push({
- // create_user_id: user.user_id,
- // create_dateline: Date.now().toString().slice(0, 10),
- // ...reqDate
- // })
- TinyRef?.clear()
- // console.log('inputValue :>> ', inputValue);
- request({
- url: '/dyaw/xlfw_zxhd_log/add',
- data: {
- dyaw_xlfw_zxhd_log: reqDate
- }
- }).then(res => {
- if (res.code === '1') {
- const fullSendData = {
- create_user_id: user.user_id,
- create_dateline: Date.now().toString().slice(0, 10),
- ...reqDate,
- dxzl_id: `${res.data.insert_id}`
- }
- infoList.push(fullSendData)
- socketSend(ws, fullSendData)
- isSending = false
- }
- })
- }
- request({
- url: '/dyaw/xlfw_zxhd_log/index',
- data: {
- dxz_id: dyaw_xlfw_zxhd.dxz_id,
- limit: 100
- }
- }).then(res => {
- if (res.code === '1') {
- infoList = res.data.page_data.reverse()
- }
- })
- const ws = createSocket(
- { teacher: teacher.user_id, student: user.user_id },
- {
- message(socketRes: TSocketRes<type_dyaw_xlfw_zxhd_log>) {
- if (socketRes.from_client_name.endsWith('teacher')) {
- infoList.push(socketRes.content)
- }
- }
- }
- )
- // console.log('ws :>> ', ws);
- if (!dyaw_xlfw_zxhd.old_data) {
- socketSend(ws, {
- $: true,
- dxz_stu_user_id: dyaw_xlfw_zxhd.dxz_stu_user_id,
- dyaw_xlfw_zxhd
- })
- }
- const scrollbarRef = $ref<HTMLElement>()
- function scrollToBottom() {
- if (!scrollbarRef) return;
- const scrollHeight = scrollbarRef!.scrollHeight;
- scrollbarRef!.scrollTo(0, scrollHeight);
- }
- watch(
- () => (infoList),
- () => {
- nextTick(() => {
- scrollToBottom()
- })
- },
- {
- deep: true,
- }
- )
- // ==========
- // chat audio/video
- // ==========
- let RtcDialogRef = $ref<typeof import("~/components/rtc-dialog/index.vue")['default']>()
- const ws2 = createSocket(
- { teacher: teacher.user_id, student: '*' },
- {
- message(socketRes: TSocketRes<type_dyaw_xlfw_zxhd_log & { operate: CHAT_OPERATION }>) {
- console.log('enter', socketRes);
- if (socketRes.from_client_name.endsWith('teacher')) {
- if (socketRes.content.dxzl_stu_user_id === user.user_id) {
- RtcDialogRef!.publisher(socketRes.content)
- }
- }
- }
- }
- )
- onMounted(() => {
- RtcDialogRef!.init(ws2)
- })
- async function handleAudioChatStart() {
- RtcDialogRef!.open(dyaw_xlfw_zxhd, 'audio')
- }
- async function handleVideoChatStart() {
- RtcDialogRef!.open(dyaw_xlfw_zxhd, 'video')
- }
- function emitUpdateInfo(info: type_dyaw_xlfw_zxhd_log, isUpdate?: boolean) {
- if (!isUpdate) {
- if (info.dxz_id === dyaw_xlfw_zxhd?.dxz_id)
- infoList.push(info)
- }
- else {
- const target = infoList.find(item => item.dxzl_id === info.dxzl_id)
- if (target)
- target.dxzl_last_msg_content = info.dxzl_last_msg_content
- }
- }
- // 从⼼灵氧吧/梦⾥寻芳/微⻛细⾬/花⾹⻦语获取随机昵称
- function getRandomName() {
- const names = ['⼼灵氧吧', '梦⾥寻芳', '微⻛细⾬', '花⾹⻦语']
- return names[Math.floor(Math.random() * names.length)]
- }
- const virtualName = getRandomName()
- </script>
- <template>
- <div class="h-full flex justify-center divide-x">
- <div class="w-full h-full divide-y flex flex-col relative">
- <van-nav-bar :title="`${dyaw_xlfw_zxhd.dxz_tea_user_realname}`" @click-left="infoDialogVisible = true"
- right-text="结束咨询" @click-right="handleClickEnd"
- style="--van-nav-bar-background:#397FF6;--van-nav-bar-icon-color:#fff;--van-nav-bar-title-text-color:#fff;--van-nav-bar-title-font-size:18px;--van-nav-bar-text-color:#fff;">
- <template #left>
- <van-icon name="wap-nav" size="18" />
- </template>
- </van-nav-bar>
- <div ref="scrollbarRef"
- class="bg-hex-ededed space-y-2 flex-auto py-2 px-6 scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent relative">
- <div ref="scrollContainRef" class="scrollContainRef space-y-2">
- <info-item v-for="item in infoList" :key="item.dxzl_id" :left="item.create_user_id !== user.user_id" :d="item"
- :w="300" :virtualName="virtualName"></info-item>
- </div>
- </div>
- <div class="bg-hex-e4e6eb p-5px flex justify-between space-x-2 items-end">
- <tinymce-area-m v-model="inputValue" ref="TinyRef" @click:audio="handleAudioChatStart" class="flex-auto"
- @click:video="handleVideoChatStart" @click:submit="handleClickSend"></tinymce-area-m>
- <van-button type="primary" @click="handleClickSend()">发送</van-button>
- </div>
- </div>
- </div>
- <rtc-dialog ref="RtcDialogRef" @update-info="emitUpdateInfo"></rtc-dialog>
- <van-dialog v-model:show="infoDialogVisible" title="" @confirm="infoDialogVisible = false">
- <div class="space-y-4 bg-white flex flex-col items-stretch px-30px py-4 divide-y">
- <template v-if="teacherInfo">
- <div class="w-full min-h-48">
- <img :src="teacherInfo.dxp_wx_qrcode" alt="">
- </div>
- <div class="py-1 space-y-2">
- <div class="text-2xl font-bold max-w-24 truncate">{{ teacherInfo.dxp_user_realname }}</div>
- <div class="text-hex-5B9FF0">联系电话:{{ teacherInfo.dxp_user_phone }}</div>
- <div class=" text-hex-00000041 h-10 leading-5">
- {{ teacherInfo.dxp_jj }}
- </div>
- </div>
- </template>
- </div>
- </van-dialog>
- <van-dialog v-model:show="rateDialogVisible" title="请评价" @confirm="handleConfirmRate" show-cancel-button>
- <div class="flex flex-col py-6 px-10 space-y-4">
- <p>于 {{ endTime }} 结束沟通</p>
- <div>
- <div>评价:</div>
- <van-rate :size="25" v-model="rateNum"></van-rate>
- </div>
- </div>
- </van-dialog>
- <rtc-dialog ref="RtcDialogRef" @update-info="emitUpdateInfo"></rtc-dialog>
- <!-- <chat-dialog ref="ChatVideoRef">
- <i:ant-design:audio-outlined />
- <i:ant-design:audio-muted-outlined />
- <i:material-symbols:video-camera-back-rounded />
- <i:material-symbols:video-camera-front-off-rounded />
- </chat-dialog> -->
- </template>
- <style scoped lang="scss">
- </style>
|