Bladeren bron

iphone support

bzkf3 2 jaren geleden
bovenliggende
commit
7e472e6b07
6 gewijzigde bestanden met toevoegingen van 214 en 14 verwijderingen
  1. 1 0
      package.json
  2. 37 0
      pnpm-lock.yaml
  3. 140 0
      src/components/tinymce-area-m/index.vue
  4. 3 0
      src/main.ts
  5. 11 7
      src/pages/student/consult.vue
  6. 22 7
      src/pages/teacher/consult/chat-list.vue

+ 1 - 0
package.json

@@ -26,6 +26,7 @@
     "element-plus": "^2.2.28",
     "tinymce": "^6.3.1",
     "vant": "^4.1.2",
+    "vconsole": "^3.15.0",
     "vue": "^3.2.45",
     "vue-router": "^4.1.6"
   },

+ 37 - 0
pnpm-lock.yaml

@@ -25,6 +25,9 @@ dependencies:
   vant:
     specifier: ^4.1.2
     version: 4.1.2(vue@3.2.45)
+  vconsole:
+    specifier: ^3.15.0
+    version: 3.15.0
   vue:
     specifier: ^3.2.45
     version: 3.2.45
@@ -218,6 +221,13 @@ packages:
     dependencies:
       '@babel/types': 7.20.7
 
+  /@babel/runtime@7.21.5:
+    resolution: {integrity: sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      regenerator-runtime: 0.13.11
+    dev: false
+
   /@babel/types@7.20.7:
     resolution: {integrity: sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==}
     engines: {node: '>=6.9.0'}
@@ -1202,6 +1212,16 @@ packages:
     resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
     dev: true
 
+  /copy-text-to-clipboard@3.1.0:
+    resolution: {integrity: sha512-PFM6BnjLnOON/lB3ta/Jg7Ywsv+l9kQGD4TWDCSlRBGmqnnTM5MrDkhAFgw+8HZt0wW6Q2BBE4cmy9sq+s9Qng==}
+    engines: {node: '>=12'}
+    dev: false
+
+  /core-js@3.30.1:
+    resolution: {integrity: sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==}
+    requiresBuild: true
+    dev: false
+
   /cross-spawn@7.0.3:
     resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
     engines: {node: '>= 8'}
@@ -2577,6 +2597,10 @@ packages:
     resolution: {integrity: sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==}
     dev: true
 
+  /mutation-observer@1.0.3:
+    resolution: {integrity: sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==}
+    dev: false
+
   /nanoid@3.3.4:
     resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -2848,6 +2872,10 @@ packages:
       picomatch: 2.3.1
     dev: true
 
+  /regenerator-runtime@0.13.11:
+    resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+    dev: false
+
   /regexp-tree@0.1.24:
     resolution: {integrity: sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==}
     hasBin: true
@@ -3344,6 +3372,15 @@ packages:
       vue: 3.2.45
     dev: false
 
+  /vconsole@3.15.0:
+    resolution: {integrity: sha512-8hq7wabPcRucSWQyN7/1tthMawP9JPvM95zgtMHpPknMMMCKj+abpoK7P7oKK4B0qw58C24Mdvo9+raUdpHyVQ==}
+    dependencies:
+      '@babel/runtime': 7.21.5
+      copy-text-to-clipboard: 3.1.0
+      core-js: 3.30.1
+      mutation-observer: 1.0.3
+    dev: false
+
   /vite-plugin-windicss@1.8.10(vite@4.0.4):
     resolution: {integrity: sha512-scywsuzo46lcTBohspmF0WiwhWEte6p+OUVrX4yr7VMRvLHMHVfLtJReyD5pppjijG7YOwVsZn7XBWWZtF658Q==}
     peerDependencies:

+ 140 - 0
src/components/tinymce-area-m/index.vue

@@ -0,0 +1,140 @@
+<script setup>
+import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
+import tinymce from 'tinymce'
+import { REQUEST } from '~/utils/request'
+
+const props = defineProps({
+  id: {
+    type: String,
+    default: `tiny-${Date.now()}-${~~(Math.random() * 10000)}`,
+  },
+  htmlClass: { default: '', type: String },
+  modelValue: String,
+  o: {
+    default() {
+      return {}
+    },
+    type: Object,
+  },
+})
+
+const emits = defineEmits(['update:modelValue', 'click:audio', 'click:video', 'click:submit'])
+
+const content = ref('')
+
+watch(
+  () => content.value,
+  (v) => {
+    emits('update:modelValue', v)
+  },
+)
+
+
+function handleClickAudioCall() {
+  emits('click:audio')
+}
+
+function handleClickVideoCall() {
+  emits('click:video')
+}
+
+
+const handleAfterRead = (fileProxy) => {
+  // const file = blobInfo.blob()
+  const { file } = fileProxy
+  console.log('file : ', file)
+  return REQUEST.upload({
+    url: '/upload/main/file',
+    data: {
+      filedata: file,
+    },
+    // onUploadProgress(progressEvent) {
+    //   progress(~~(progressEvent.loaded / progressEvent.total * 100 | 0))
+    // },
+  }).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())
+      emits('click:submit', `<img src="${window.GLOBAL_CONFIG.oss + (res.data.url)}" width="150" />`)
+    }
+    else {
+      fileProxy.status = 'failed'
+      fileProxy.message = '上传失败'
+    }
+  })
+}
+
+defineExpose({
+  clear(v = '') {
+    content.value = v
+  },
+})
+
+
+</script>
+
+<template>
+  <div class="relative">
+
+    <div class=" 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">
+          <i:uil:image-upload class=" w-6 h-6" />
+        </van-uploader>
+      </div>
+
+      <div @click="handleClickAudioCall" class="cursor-pointer flex_center box-content tbtn ">
+        <i:material-symbols:call class="w-6 h-6" />
+      </div>
+      <div @click="handleClickVideoCall" class="cursor-pointer flex_center box-content tbtn ">
+        <i:material-symbols:video-call-outline class=" w-6 h-6" />
+      </div>
+    </div>
+
+    <!-- <textarea :id="id" class="tinyarea w-full min-h-42px" v-model="content"></textarea> -->
+    <van-field v-model="content" label-align="top" rows="1" autosize type="textarea"  />
+  </div>
+</template>
+
+<style lang="scss">
+.tinyarea+.tox-tinymce {
+  border: none;
+  border-radius: 0;
+  // height: 42px !important;
+
+  // .tox-editor-header{
+  //   display: none;
+  // }
+
+  &.tox:not(.tox-tinymce-inline) .tox-editor-header {
+    box-shadow: none;
+    padding: 0 0;
+
+    .tox-toolbar-overlord .tox-toolbar {
+      background: #e4e6eb;
+    }
+  }
+
+  .tox-tbtn {
+    margin-top: 0;
+    margin-bottom: 0;
+    cursor: pointer;
+    width: 40px;
+    height: 40px;
+    // background: #f3f4f6;
+    box-shadow: inset 0 0 0 1px #e4e6eb;
+    border-radius: 2px;
+  }
+
+
+}
+
+.tbtn {
+  width: 40px;
+  height: 40px;
+  // background: #f3f4f6;
+  box-shadow: inset 0 0 0 1px #e4e6eb;
+  border-radius: 2px;
+}
+</style>

+ 3 - 0
src/main.ts

@@ -1,6 +1,9 @@
 import { createApp } from 'vue'
 import 'virtual:windi.css'
 
+import VConsole from 'vconsole';
+const vConsole = new VConsole();
+
 import App from './App.vue'
 import router from './router/index'
 

+ 11 - 7
src/pages/student/consult.vue

@@ -14,7 +14,11 @@ if (SessionConsultTeacher !== null) {
 } else {
   router.back()
 }
-
+function formatter(e: string) {
+  if (!e) return e
+  // 转义字符串中的危险字符
+  return e.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
+}
 let teacherInfo = $ref<{
   dxp_user_realname: string;
   dxp_user_phone: string;
@@ -79,7 +83,7 @@ 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() {
+async function handleClickSend(val?: string) {
   if (isSending) return;
   isSending = true
   const reqDate = {
@@ -88,8 +92,8 @@ async function handleClickSend() {
     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'
+    dxzl_last_msg_content: encodeURIComponent(val || formatter(inputValue)),
+    dxzl_type: (val || formatter(inputValue)).includes('<img') ? '2' : '1'
   }
 
   // infoList.push({
@@ -242,10 +246,10 @@ const virtualName = getRandomName()
 
       </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>
+        <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>
+        <van-button type="primary" @click="handleClickSend()">发送</van-button>
 
       </div>
     </div>

+ 22 - 7
src/pages/teacher/consult/chat-list.vue

@@ -17,13 +17,19 @@ const emits = defineEmits<{
   (event: 'openRtcDialog', dyaw_xlfw_zxhd: type_dyaw_xlfw_zxhd, type: 'audio' | 'video'): void;
 }>()
 
-const props = defineProps<{updateFnList:Function[]}>()
+const props = defineProps<{ updateFnList: Function[] }>()
+
+function formatter(e: string) {
+  if (!e) return e
+  // 转义字符串中的危险字符
+  return e.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
+}
 
 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() {
+async function handleClickSend(val?: string) {
   if (!dyaw_xlfw_zxhd) return;
   if (isSending) return;
   isSending = true
@@ -33,9 +39,18 @@ async function handleClickSend() {
     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'
+    dxzl_last_msg_content: encodeURIComponent(val || formatter(inputValue)),
+    dxzl_type: (val || formatter(inputValue)).includes('<img') ? '2' : '1'
   }
+  // 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,
@@ -350,10 +365,10 @@ let showRightArchives = $ref(false)
 
         </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>
+          <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>
+          <van-button type="primary" @click="handleClickSend()">发送</van-button>
 
         </div>
       </template>