bzkf3 2 年之前
父節點
當前提交
2f5279c91b

File diff suppressed because it is too large
+ 3 - 2
public/tiny/icons/default/icons.js


+ 119 - 0
src/components/RemoteListHalf/index.vue

@@ -0,0 +1,119 @@
+<script setup>
+const props = defineProps({
+  url: {
+    type: String,
+    required: true,
+  },
+  d: {
+    type: Object,
+    required: false,
+  },
+  loop: {
+    type: Boolean,
+    required: false,
+    default: false,
+  },
+})
+
+let page = 1
+
+let loading = $ref(false)
+let error = $ref(false)
+let finished = $ref(false)
+let refreshing = $ref(false)
+let refreshloop = $ref(false)
+
+let list = $ref([])
+
+// request({
+//   url: props.url,
+//   data: {
+//     page,
+//     ...props.d
+//   },
+// })
+
+// 将一个数组的元素分割成指定长度的数组
+function chunk(arr, size) {
+  const result = []
+  for (let i = 0; i < arr.length; i += size) {
+    result.push(arr.slice(i, i + size))
+  }
+  return result
+}
+
+
+function onLoad() {
+  loading = true
+  request({
+    url: props.url,
+    data: {
+      page,
+      limit: 20,
+      ...props.d
+    },
+  }).then(res => {
+    if (res.code === '1') {
+      // if (refreshing || refreshloop) {
+      //   list = [];
+      //   refreshing = false;
+      //   refreshloop = false;
+
+      // }
+      list = chunk(res.data.page_data, 2)
+      loading = false
+      page++
+      if (res.data.page_now === res.data.total_page) {
+        finished = true
+      }
+    } else {
+      error = true
+    }
+
+  }).catch(err => {
+    console.log(err)
+  })
+
+}
+
+function onRefresh() {
+  finished = false
+  page = 1
+  onLoad()
+}
+
+let timer;
+
+// if (props.loop) {
+//   timer = setTimeout(async () => {
+//     refreshloop = true;
+//     await onRefresh();
+//   }, 60 * 1000);
+// }
+
+onBeforeUnmount(() => {
+  timer && clearInterval(timer)
+})
+
+watch(
+  () => props.d.keyword,
+  async (val, oldVal) => {
+    if (oldVal === '搜索') return;
+    // refreshloop = true;
+    onRefresh()
+  },
+)
+</script>
+
+<template>
+  <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
+    <van-list v-model:loading="loading" v-model:error="error" error-text="请求失败,点击重新加载" :finished="finished"
+      finished-text="没有更多了" @load="onLoad" @click.stop>
+      <div v-for="items in list" class="flex justify-between mb-2">
+        <div class="w-49/100" v-for="item in items">
+          <slot :row="item"></slot>
+        </div>
+      </div>
+    </van-list>
+  </van-pull-refresh>
+</template>

+ 20 - 2
src/components/archives-card/index.vue

@@ -60,9 +60,9 @@ watch(
 </script>
 
 <template>
-  <div class="border border-dark-100 divide-y divide-dark-100 w-full text-xs relative">
+  <div class="border border-dark-100 divide-y divide-dark-100 w-full text-xs relative" :class="disabled && 'disabled'">
 
-    <div class="absolute inset-0 z-1" v-if="disabled"></div>
+    <!-- <div class="absolute inset-0 z-1" v-if="disabled"></div> -->
 
     <div class="flex justify-between items-end">
       <div class="p-1 text-base font-bold">咨询记录</div>
@@ -129,3 +129,21 @@ watch(
 
   </div>
 </template>
+
+<style scoped>
+.disabled {
+  --ep-disabled-bg-color: transparent;
+  --ep-disabled-border-color: transparent;
+  --ep-disabled-text-color: #1c1c1e;
+  --ep-text-color-placeholder: #1c1c1e;
+  --ep-radio-text-color: #1c1c1e;
+}
+
+.disabled :deep(.ep-radio__inner) {
+  border-color: #e4e7ed
+}
+
+.disabled :deep(.ep-textarea__inner) {
+  box-shadow: none;
+}
+</style>

+ 10 - 2
src/components/rtc-dialog/index.vue

@@ -39,6 +39,7 @@ function handleClose() {
 }
 defineExpose({
   init(ws: WebSocket) {
+    console.log("init")
     ws2 = ws
   },
   open(d: type_dyaw_xlfw_zxhd, _mode: 'audio' | 'video') {
@@ -117,6 +118,7 @@ defineExpose({
         isOpen = false
         await rtcInstance.client!.leave();
         handleSwitchUse(false)
+
         break;
       default:
         break;
@@ -176,6 +178,7 @@ function handleInfoEdit(data: Partial<type_dyaw_xlfw_zxhd_log>, tip?: string) {
   })
 }
 
+
 const LocalPlayerContainerRef = $ref<HTMLElement>()
 const RemotePlayerContainerRef = $ref<HTMLElement>()
 // ==========
@@ -203,6 +206,11 @@ function handleSwitchUse(v: boolean) {
     videoInUse = v
     rtcInstance?.localAudioTrack?.setEnabled(v)
     rtcInstance?.localVideoTrack?.setEnabled(v)
+    // ! will error
+    // if (!v) {
+    //   rtcInstance?.localAudioTrack?.close()
+    //   rtcInstance?.localVideoTrack?.close()
+    // }
   } catch (error) {
     console.error(error);
   }
@@ -438,7 +446,7 @@ function handleChangeMins() {
         <i:clarity:window-min-line class="cursor-pointer" @click="handleChangeMins" />
         <i:ic:outline-close class="cursor-pointer" @click="handleCloseForce" />
       </div>
-      <div class="h-634px relative">
+      <div class="h-screen relative">
         <slot>
 
           <div v-show="mode === 'video' && currentChatStatus === CHAT_STATUS.CHATING" ref="LocalPlayerContainerRef"
@@ -447,7 +455,7 @@ function handleChangeMins() {
             class="absolute z-1 inset-0"></div>
 
 
-          <div class="absolute z-11 left-0 bottom-66px text-xl flex justify-around w-full text-light-50 ">
+          <div class="absolute z-11 left-0 bottom-36 text-xl flex justify-around w-full text-light-50 ">
             <div v-show="currentChatStatus === CHAT_STATUS.WAITING_YOU_ACCEPT"
               class="bg-green-600 w-12 h-12 rounded-1 cursor-pointer flex items-center justify-around"
               @click="handleAudioChatAccept">

+ 1 - 1
src/components/teacher-card/index.vue

@@ -10,7 +10,7 @@ function handleClickConsult() {
 </script>
 
 <template>
-  <div class="bg-white w-370px h-120px rounded-md box_shadow flex flex-col justify-between p-3 ">
+  <div class="bg-white h-120px rounded-md box_shadow flex flex-col justify-between p-3 ">
     <div class="flex items-center justify-between">
       <div class="flex space-x-4 items-center">
         <el-avatar :size="48" :src="d.dxp_user_avatar"></el-avatar>

+ 13 - 13
src/components/tinymce-area/index.vue

@@ -45,7 +45,7 @@ function init() {
     autosave_restore_when_empty: false,
     content_style: 'body { margin: 0.4rem; line-height: 1;min-height: 1rem } p { margin: 0; } ',
     setup(_editor) {
-      _editor.on('KeyDown', (e) => {
+      _editor?.on('KeyDown', (e) => {
         if (e.ctrlKey && e.keyCode === 13) {
           e.preventDefault();
           emits('click:submit')
@@ -54,17 +54,17 @@ function init() {
       })
     },
     init_instance_callback: (_editor) => {
-      // console.log('init_instance_callback :>> ', _editor.isNas);
+      // console.log('init_instance_callback :>> ', _editor?.isNas);
 
-      _editor.on('KeyUp', (e) => {
+      _editor?.on('KeyUp', (e) => {
         submitNewContent()
       })
-      _editor.on('Change', (e) => {
-        if (_editor.getContent() !== props.modelValue)
+      _editor?.on('Change', (e) => {
+        if (_editor?.getContent() !== props.modelValue)
           submitNewContent()
       })
-      _editor.on('init', (e) => {
-        _editor.setContent(content.value)
+      _editor?.on('init', (e) => {
+        _editor?.setContent(content.value)
         // emits('input', content.value);
       })
       editor = _editor
@@ -104,7 +104,7 @@ function submitNewContent() {
   checkerTimeout = setTimeout(() => {
     isTyping = false
   }, 300)
-  emits('update:modelValue', editor.getContent())
+  emits('update:modelValue', editor?.getContent())
 }
 
 onMounted(() => {
@@ -113,12 +113,12 @@ onMounted(() => {
 })
 
 onBeforeUnmount(() => {
-  editor.destroy()
+  editor?.destroy()
 })
 
 defineExpose({
   clear(v = '') {
-    editor.resetContent(v)
+    editor?.resetContent(v)
   },
 })
 
@@ -145,8 +145,8 @@ const handleAfterRead = (fileProxy) => {
     // },
   }).then((res) => {
     if (res.code === '1') {   // return window.GLOBAL_CONFIG.oss + (res.data.url)
-      editor.selection.setContent(`<img src="${window.GLOBAL_CONFIG.oss + (res.data.url)}" width="150" />`)
-      emits('update:modelValue', editor.getContent())
+      editor?.selection.setContent(`<img src="${window.GLOBAL_CONFIG.oss + (res.data.url)}" width="150" />`)
+      emits('update:modelValue', editor?.getContent())
     }
     else {
       fileProxy.status = 'failed'
@@ -161,7 +161,7 @@ const handleAfterRead = (fileProxy) => {
   <div class="relative">
     <textarea :id="id" class="tinyarea w-full min-h-42px" v-model="content"></textarea>
 
-    <div class="absolute right-0 top-0 px-13px flex z-1000 text-hex-222f3e space-x-1px">
+    <div class="absolute left-14 top-0 flex z-1000 text-hex-222f3e space-x-1px">
 
       <div class="cursor-pointer flex_center box-content tbtn">
         <van-uploader :after-read="handleAfterRead" class=" w-6 h-6">

+ 2 - 2
src/pages/admin/home.vue

@@ -9,7 +9,7 @@ const teacherList = (await request({
   url: '/dyaw/xlfw_pbgl_pb/index',
   data: {
     // dxpp_date: (new Date('2023-4-2')).getTime()
-    dxpp_date: Date.now()
+    // dxpp_date: Date.now()
   }
 })).data?.page_data
 
@@ -28,7 +28,7 @@ function onClickLeft() {
     <div
       class="w-full h-full overflow-y-auto flex flex-col items-center space-y-4 py-4 scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent">
       <el-empty v-if="!teacherList?.length" description="今天暂无排班老师"></el-empty>
-      <teacher-card v-for="item in teacherList" class="flex-none" :d="item">
+      <teacher-card v-for="item in teacherList" class="flex-none w-94vw" :d="item">
         <el-tag :type="item.online ? 'success' : 'danger'">
           {{ item.online ? '在 线' : '离 线' }}
         </el-tag>

+ 295 - 0
src/pages/student/consult copy.vue

@@ -0,0 +1,295 @@
+<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()
+}
+
+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 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() {
+  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(inputValue),
+    dxzl_type: 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-640px bg-hex-f2f2f295 flex justify-center divide-x">
+    <div class="w-1000px h-full divide-y">
+      <div class="bg-pink-300 flex justify-between h-48px items-center px-18px">
+        <span class="text-lg px-1 tracking-wider">{{ `${dyaw_xlfw_zxhd.dxz_tea_user_realname}老师正在为您服务` }}</span>
+        <div class="text-pink-300 text-sm bg-white w-100px h-32px flex_center rounded-2xl cursor-pointer"
+          @click="handleClickEnd">结束会话</div>
+      </div>
+      <div ref="scrollbarRef"
+        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">
+        <div class="w-full text-center text-sm">{{ formatTimestamp(dyaw_xlfw_zxhd.create_dateline) }} 开始沟通</div>
+        <info-item v-for="item in infoList" :key="item.dxzl_id" :left="item.create_user_id !== user.user_id" :d="item"
+          :virtualName="virtualName" :w="780"></info-item>
+      </div>
+      <div class="bg-white h-180px p-5px flex flex-col justify-between">
+        <!-- <div class="h-48px"></div>
+            <el-input type="textarea"></el-input> -->
+        <tinymce-area v-model="inputValue" ref="TinyRef" @click:audio="handleAudioChatStart"
+          @click:video="handleVideoChatStart" @click:submit="handleClickSend"></tinymce-area>
+        <div class="flex justify-end">
+          <div class="bg-pink-300 text-sm text-white w-80px h-32px flex_center rounded-2xl cursor-pointer"
+            @click="handleClickSend">发送</div>
+        </div>
+      </div>
+    </div>
+    <div
+      class="w-400px h-full space-y-4 bg-white overflow-y-auto flex flex-col items-stretch px-30px py-4 divide-y scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent">
+
+      <template v-if="teacherInfo">
+        <div class="w-full">
+          <img :src="teacherInfo.dxp_wx_qrcode" alt="">
+        </div>
+        <div class="py-4">
+          <div class="text-lg 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 mt-1">
+            {{ teacherInfo.dxp_jj }}
+          </div>
+        </div>
+      </template>
+    </div>
+  </div>
+
+  <el-dialog v-model="rateDialogVisible" title="请评价" align-center width="405px">
+    <p>于 {{ endTime }} 结束沟通</p>
+    <div class="flex items-center my-4">
+      <div>评价:</div>
+      <el-rate size="large" v-model="rateNum"></el-rate>
+    </div>
+    <template #footer>
+      <div class="flex_center">
+        <el-button round type="primary" @click="handleConfirmRate">
+          确认
+        </el-button>
+        <el-button round @click="rateDialogVisible = false">取消</el-button>
+      </div>
+    </template>
+
+  </el-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>

+ 54 - 46
src/pages/student/consult.vue

@@ -45,6 +45,8 @@ const dyaw_xlfw_zxhd: type_dyaw_xlfw_zxhd = (await request({
 
 
 
+let infoDialogVisible = $ref(false)
+
 let rateDialogVisible = $ref(false)
 let endTime = $ref<string>()
 let rateNum = $ref(5)
@@ -220,75 +222,81 @@ const virtualName = getRandomName()
 </script>
 
 <template>
-  <div class="h-640px bg-hex-f2f2f295 flex justify-center divide-x">
-    <div class="w-1000px h-full divide-y">
-      <div class="bg-pink-300 flex justify-between h-48px items-center px-18px">
-        <span class="text-lg px-1 tracking-wider">{{ `${dyaw_xlfw_zxhd.dxz_tea_user_realname}老师正在为您服务` }}</span>
-        <div class="text-pink-300 text-sm bg-white w-100px h-32px flex_center rounded-2xl cursor-pointer"
-          @click="handleClickEnd">结束会话</div>
-      </div>
+  <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-fff8fb space-y-2 h-410px py-2 px-6 scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent">
-        <div class="w-full text-center text-sm">{{ formatTimestamp(dyaw_xlfw_zxhd.create_dateline) }} 开始沟通</div>
-        <info-item v-for="item in infoList" :key="item.dxzl_id" :left="item.create_user_id !== user.user_id" :d="item"
-          :virtualName="virtualName" :w="780"></info-item>
+        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-white h-180px p-5px flex flex-col justify-between">
-        <!-- <div class="h-48px"></div>
-            <el-input type="textarea"></el-input> -->
-        <tinymce-area v-model="inputValue" ref="TinyRef" @click:audio="handleAudioChatStart"
+      <div class="bg-hex-e4e6eb p-5px flex justify-between space-x-2 items-end">
+        <tinymce-area v-model="inputValue" ref="TinyRef" @click:audio="handleAudioChatStart" class="flex-auto"
           @click:video="handleVideoChatStart" @click:submit="handleClickSend"></tinymce-area>
-        <div class="flex justify-end">
-          <div class="bg-pink-300 text-sm text-white w-80px h-32px flex_center rounded-2xl cursor-pointer"
-            @click="handleClickSend">发送</div>
-        </div>
+
+        <van-button type="primary" @click="handleClickSend">发送</van-button>
+
       </div>
     </div>
-    <div
-      class="w-400px h-full space-y-4 bg-white overflow-y-auto flex flex-col items-stretch px-30px py-4 divide-y scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent">
+
+
+  </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">
+        <div class="w-full min-h-48">
           <img :src="teacherInfo.dxp_wx_qrcode" alt="">
         </div>
-        <div class="py-4">
-          <div class="text-lg font-bold max-w-24 truncate">{{ teacherInfo.dxp_user_realname }}</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 mt-1">
+          <div class=" text-hex-00000041 h-10 leading-5">
             {{ teacherInfo.dxp_jj }}
           </div>
         </div>
       </template>
     </div>
-  </div>
+  </van-dialog>
 
-  <el-dialog v-model="rateDialogVisible" title="请评价" align-center width="405px">
-    <p>于 {{ endTime }} 结束沟通</p>
-    <div class="flex items-center my-4">
-      <div>评价:</div>
-      <el-rate size="large" v-model="rateNum"></el-rate>
-    </div>
-    <template #footer>
-      <div class="flex_center">
-        <el-button round type="primary" @click="handleConfirmRate">
-          确认
-        </el-button>
-        <el-button round @click="rateDialogVisible = false">取消</el-button>
-      </div>
-    </template>
 
-  </el-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: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> -->
+                  <i:material-symbols:video-camera-back-rounded />
+                  <i:material-symbols:video-camera-front-off-rounded />
+                </chat-dialog> -->
 </template>
 
 <style scoped lang="scss">

+ 8 - 2
src/pages/student/home.vue

@@ -8,7 +8,7 @@ const imgSrc = (await request({
 const teacherList = (await request({
   url: '/dyaw/xlfw_pbgl_pb/index',
   data: {
-    dxpp_date: Date.now()
+    // dxpp_date: Date.now()
   }
 })).data?.page_data
 
@@ -24,11 +24,17 @@ function onClickLeft() {
     <div class="w-full">
       <img v-show="imgSrc" :src="imgSrc" class="w-full object-contain">
     </div>
-    <div
+    <!-- <div
       class="w-full h-full overflow-y-auto flex flex-col items-center space-y-4 py-4 scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent">
       <el-empty v-if="!teacherList?.length" description="当前暂无排班老师"></el-empty>
       <teacher-card v-for="item in teacherList" class="flex-none" :d="item">
       </teacher-card>
+    </div> -->
+    <div
+      class="w-full h-full overflow-y-auto flex flex-col items-center space-y-4 py-4 scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent">
+      <el-empty v-if="!teacherList?.length" description="今天暂无排班老师"></el-empty>
+      <teacher-card v-for="item in teacherList" class="flex-none w-94vw" :d="item">
+      </teacher-card>
     </div>
   </div>
 </template>

+ 62 - 0
src/pages/teacher/archives-stu.vue

@@ -0,0 +1,62 @@
+<script setup lang='ts'>
+import { showFailToast } from 'vant';
+
+const props = defineProps({
+  id: {
+    type: String,
+    required: true,
+  },
+})
+
+const router = useRouter()
+function onClickLeft() {
+  router.back()
+}
+
+let list = $ref([])
+request({
+  url: "/dyaw/xlfw_xsda_dajl/index",
+  data: {
+    limit: 100,
+    user_id: props.id,
+  },
+}).then(res => {
+  if (res.code == "1") {
+    console.log(res.data.page_data)
+    list = res.data.page_data
+  } else {
+    showFailToast(res.msg)
+  }
+}).catch(error => {
+  showFailToast(error)
+})
+
+</script>
+
+<template>
+  <van-nav-bar title="学生档案" left-text="" left-arrow @click-left="onClickLeft"
+    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;" />
+
+  <div class="p-1 flex-auto">
+    <van-swipe class="h-full">
+      <van-swipe-item v-for="item in list" class="p-1 ">
+        <archives-card :d="item" disabled></archives-card>
+        <div class="mt-4">咨询时长: {{ item?.xxd_zxsc }}</div>
+      </van-swipe-item>
+      <template #indicator="{ active, total }">
+        <div class="custom-indicator">{{ active + 1 }} / {{ total }}</div>
+      </template>
+    </van-swipe>
+  </div>
+</template>
+
+<style scoped>
+.custom-indicator {
+  position: absolute;
+  right: 5px;
+  bottom: 5px;
+  padding: 2px 5px;
+  font-size: 12px;
+  background: rgba(0, 0, 0, 0.1);
+}
+</style>

+ 50 - 149
src/pages/teacher/archives.vue

@@ -1,160 +1,60 @@
 <script setup lang="ts">
-import { ref, reactive } from "vue";
-import { Plus, Calendar, Search,Delete } from "@element-plus/icons-vue";
-import { ElMessage, ElMessageBox } from 'element-plus'
-import user from '~/store/user';
-
-import { useRouter } from "vue-router";
+import { Search, } from "@element-plus/icons-vue";
+import type { RouteLocationRaw } from 'vue-router'
 
 const router = useRouter()
 
-const pageNum = ref(1);
-const totalNum = ref(0);
 const searchValue = ref("");
-const tableData = ref([]);
-const InitData = () => {
-  request({
-    url: "/dyaw/xlfw_xsda/index",
-    data: {
-      page: pageNum.value,
-      keyword: searchValue.value,
-      admin:1,
-    },
-  })
-    .then((res) => {
-      if (res.code == "1") {
-        tableData.value = res.data.page_data;
-        pageNum.value = Number(res.data.total_page);
-        totalNum.value = Number(res.data.total_rows);
-      }
-    })
-    .catch((err) => {
-      console.log(err);
-    });
-};
-InitData();
-
-//多选
-const multipleSelection = ref([]);
-const handleSelectionChange = (val) => {
-  multipleSelection.value = val;
-};
 
-//批量删除
-const batchDel = () => {
-  if (multipleSelection.value.length == 0) {
-    ElMessage({
-      showClose: true,
-      message: "请至少选择一项~",
-      type: "error",
-    });
-  } else {
-    let tempArr = [];
-    multipleSelection.value.forEach((item) => {
-      tempArr.push(item.dxx_id);
-    });
-    request({
-      url: "/dyaw/xlfw_xsda/delete",
-      data: {
-        dxx_id: tempArr.join(),
-      },
-    })
-      .then((res) => {
-        if (res.code == "1") {
-          ElMessage({
-            message: res.msg,
-            type: "success",
-          });
-          InitData();
-        }
-      })
-      .catch((err) => {
-        console.log(err);
-      });
-  }
-};
-//按钮-搜索
-const searchBtn = () => {
-  InitData();
-};
-//按钮-全部档案(跳转)
-const allFile = () => {
-  router.push("studocAll");
-};
-//表格选项-详情(跳转)
-const handlDetailClick = (paramsAll) => {
-  router.push({
-    path: "studocSingle",
-    query: {
-      userId: paramsAll.user_id,
-      recordName:paramsAll.dxx_user_realname,
-    },
-  });
-};
-//表格选项-删除
-const handleDel = (paramsAll) => {
-  request({
-    url: "/dyaw/xlfw_xsda/delete",
-    data: {
-      dxx_id: paramsAll.dxx_id,
-    },
-  })
-    .then((res) => {
-      if (res.code == "1") {
-        ElMessage({
-          showClose: true,
-          message: res.msg,
-          type: "success",
-        });
-        InitData();
-      }
-    })
-    .catch((err) => {
-      console.log(err);
-    });
-};
-
-//分页
-const handleCurrentChange = (val) => {
-  pageNum.value = val;
-  InitData();
-};
 
+function onClickLeft() {
+  router.back()
+}
 
+function routerPush(to: RouteLocationRaw) {
+  router.push(to)
+}
 </script>
 
 <template>
-  <div class="xsda">
-    <div class="allOptions">
-      <el-button type="danger" :icon="Delete" round @click="batchDel">批量删除</el-button>
-      <el-input class="bor10" v-model="searchValue" style="width: 10%; margin: 0 5px 0 20px" placeholder="关键词搜索" :prefix-icon="Search"
-        round />
-      <el-button type="primary" round @click="searchBtn">搜索</el-button>
-      <el-button type="primary" round @click="allFile">全部档案</el-button>
-    </div>
-    <div class="allTables">
-      <el-table ref="multipleTableRef" :data="tableData" stripe @selection-change="handleSelectionChange"
-        style="width: 100%">
-        <el-table-column type="selection" width="55" />
-        <el-table-column type="index" width="80" label="序号" />
-        <el-table-column prop="dxx_user_realname" label="姓名" />
-        <el-table-column prop="dxx_school_name" label="学校" />
-        <el-table-column prop="dxx_grade_name" label="年级" />
-        <el-table-column prop="dxx_class_name" label="班级" />
-        <el-table-column prop="dxx_zxcs" label="咨询次数" />
-        <el-table-column prop="dxx_dasl" label="档案数量" />
-        <el-table-column fixed="right" label="操作">
-          <template #default="scope">
-            <el-button link type="primary" size="small" @click="handlDetailClick(scope.row)">详情</el-button>
-            <el-button link type="primary" size="small" @click="handleDel(scope.row)">删除</el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-    </div>
-    <div class="pag">
-        <el-pagination class="pageC" background small layout=" prev, pager, next" :total="totalNum"
-          @current-change="handleCurrentChange" />
-      </div>
+  <van-nav-bar title="学生档案" left-text="" left-arrow @click-left="onClickLeft"
+    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;" />
+  <div class="p-10px flex-auto flex flex-col justify-start bg-hex-f2f2f2 space-y-4">
+    <el-input autocomplete="off" name="searchValue" v-model="searchValue" size="large" :prefix-icon="Search"
+      @focus="searchValue = ''" clearable placeholder="搜索"
+      :style="`${searchValue === '搜索' && '--ep-input-text-color:var(--ep-text-color-placeholder);'}`"></el-input>
+
+    <remote-list-half url="/dyaw/xlfw_xsda/index" :d="{
+      keyword: searchValue === '搜索' ? undefined : searchValue,
+      admin: 1,
+    }" class="mt-2 flex-auto">
+      <template #default="{ row: item }">
+        <div @click="routerPush({ name: 'teacher_archives_stu', params: { id: item.user_id } })"
+          class="space-y-2 bg-white shadow rounded  p-3 text-sm">
+          <div class="flex">
+            <div class="w-18">姓名:</div>
+            <div>{{ item.dxx_user_realname }}</div>
+          </div>
+          <div class="flex">
+            <div class="w-18">学校:</div>
+            <div>{{ item.dxx_school_name }}</div>
+          </div>
+          <div class="flex">
+            <div class="w-18">年级班级:</div>
+            <div>{{ item.dxx_class_name }}</div>
+          </div>
+          <div class="flex">
+            <div class="w-18">咨询次数:</div>
+            <div>{{ item.dxx_zxcs }}</div>
+          </div>
+          <div class="flex">
+            <div class="w-18">档案数量:</div>
+            <div>{{ item.dxx_dasl }}</div>
+          </div>
+          <div class="flex_center pt-1"><van-button type="primary" size="small">查看</van-button></div>
+        </div>
+      </template>
+    </remote-list-half>
   </div>
 </template>
 
@@ -171,7 +71,7 @@ const handleCurrentChange = (val) => {
 }
 
 .allTables {
-  min-height:300px;
+  min-height: 300px;
   margin-top: 20px;
 }
 
@@ -183,7 +83,8 @@ const handleCurrentChange = (val) => {
 .pageC {
   float: right;
 }
-.el-input__wrapper{
-  border-radius:20px;
+
+.el-input__wrapper {
+  border-radius: 20px;
 }
 </style>

+ 3 - 13
src/pages/teacher/chat-list.vue

@@ -275,6 +275,7 @@ const ws2 = createSocket(
       if (socketRes.from_client_name.endsWith('student')) {
         // infoList.push(socketRes.content)
         if (socketRes.content.dxzl_tea_user_id === user.user_id) {
+          console.log('RtcDialogRef : ', RtcDialogRef)
           RtcDialogRef!.publisher(socketRes.content)
         }
       }
@@ -320,17 +321,13 @@ let showRightArchives = $ref(false)
   <div class="h-full flex justify-center divide-x">
     <div class="w-full h-full divide-y flex flex-col relative">
       <template v-if="dyaw_xlfw_zxhd">
-        <!-- <div class="bg-pink-300 flex justify-between h-48px items-center px-18px">
-                                        <span class="text-lg px-1 tracking-wider">{{ dyaw_xlfw_zxhd.dxz_stu_user_realname }}
-                                          {{ dyaw_xlfw_zxhd.dxz_stu_school_name }} {{ dyaw_xlfw_zxhd.dxz_class_name }}</span>
-                                      </div> -->
         <van-nav-bar
           :title="`${dyaw_xlfw_zxhd.dxz_stu_user_realname}  ${dyaw_xlfw_zxhd.dxz_stu_school_name}  ${dyaw_xlfw_zxhd.dxz_class_name}`"
           left-text="" left-arrow @click-left="onClickLeft" right-text="学生档案" @click-right="showRightArchives = true"
           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;" />
 
         <div ref="scrollbarRef"
-          class="bg-hex-fff8fb space-y-2 flex-auto py-2 px-6 scrollbar scrollbar-thin scrollbar-thumb-rounded-md scrollbar-thumb-gray-200 scrollbar-track-transparent relative">
+          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">
             <div v-show="!cardLoading" @click="handleLoadMoreInfo"
               class="w-full text-center text-sm text-blue-400 hover:underline underline-blue-400 cursor-pointer">查看更多
@@ -341,13 +338,6 @@ let showRightArchives = $ref(false)
 
 
         </div>
-        <!--  -->
-        <!-- <div v-show="unreadNum > 10"
-                                class="absolute bg-white right-2 bottom-200px text-blue-400 rounded-l-full cursor-pointer z-100 flex items-center space-x-1 p-2 text-sm"
-                                @click="getAllUnreadMsg">
-                                <i:material-symbols:keyboard-double-arrow-up />
-                                <span>{{ unreadNum }}条消息</span>
-                              </div> -->
         <div class="bg-hex-e4e6eb p-5px flex justify-between space-x-2 items-end">
           <tinymce-area v-model="inputValue" ref="TinyRef" @click:audio="handleAudioChatStart" class="flex-auto"
             @click:video="handleVideoChatStart" @click:submit="handleClickSend"></tinymce-area>
@@ -382,7 +372,7 @@ let showRightArchives = $ref(false)
               <van-button @click="handleSubmitArchives" type="success" size="large">提交</van-button>
             </div>
           </div>
-          <div class="bg-hex-FFF7FA py-2 space-y-1">
+          <div class=" py-2 space-y-1">
             <div class="flex_center text-lg">历史档案</div>
             <el-empty v-show="archivesList.length === 0" :image-size="60" description="暂无历史档案"></el-empty>
             <archives-card disabled v-for="item in archivesList" :d="item" :key="item.dxxd_id"></archives-card>

+ 1 - 1
src/pages/teacher/consult.vue

@@ -71,7 +71,7 @@ function onClickLeft() {
 <template>
   <van-nav-bar title="咨询页面" left-text="" left-arrow @click-left="onClickLeft"
     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;" />
-  <div class="p-10px h-full flex flex-col justify-start bg-white space-y-4">
+  <div class="p-10px flex-auto flex flex-col justify-start bg-white space-y-4">
     <el-input autocomplete="off" name="searchValue" v-model="searchValue" size="large" :prefix-icon="Search"
       @focus="searchValue = ''" clearable placeholder="搜索"
       :style="`${searchValue === '搜索' && '--ep-input-text-color:var(--ep-text-color-placeholder);'}`"></el-input>

+ 14 - 8
src/router/routes/teacher.ts

@@ -28,15 +28,21 @@ export default {
       component: () => import('@/pages/teacher/archives.vue'),
     },
     {
-      path: 'studocSingle',
-      name: 'archives_studocSingle',
-      component: () => import('@/pages/teacher/studoc/studocSingle.vue')
-    },
-    {
-      path: 'studocAll',
-      name: 'archives_studocAll',
-      component: () => import('@/pages/teacher/studoc/studocAll.vue'),
+      path: 'archives_stu/:id',
+      name: 'teacher_archives_stu',
+      component: () => import('@/pages/teacher/archives-stu.vue'),
+      props: true
     },
+    // {
+    //   path: 'studocSingle',
+    //   name: 'archives_studocSingle',
+    //   component: () => import('@/pages/teacher/studoc/studocSingle.vue')
+    // },
+    // {
+    //   path: 'studocAll',
+    //   name: 'archives_studocAll',
+    //   component: () => import('@/pages/teacher/studoc/studocAll.vue'),
+    // },
 
   ]
 }