zhengnaiwen_citu 9 месяцев назад
Родитель
Сommit
90ee42da2d

+ 42 - 29
src/hooks/web/useIM.js

@@ -21,35 +21,41 @@ import {
   // Message, StreamItem, ChannelTypeGroup, MessageStatus, SyncOptions, MessageExtra, MessageContent
 } from "wukongimjssdk"
 
-// 注册消息体
-class ObjectContent extends MessageContent {
-  constructor(text) {
-    super();
-    this.content = text;
-  }
-
-  get conversationDigest() {
-      // 这里需要实现具体的逻辑
-      return this.content
-  }
 
-  get contentType() {
-      // 这里需要实现具体的逻辑
-      return 101; // 示例实现
-  }
+const ObjType = [ 101, 102 ]
 
-  decodeJSON(content) {
-      this.content = content.text;
+const { ObjectContent } = initRegister(101)
+const { ObjectContent: ObjectContent2 } = initRegister(102)
+// 注册消息体
+function initRegister (contentType) {
+  class ObjectContent extends MessageContent {
+    constructor(text) {
+      super();
+      this.content = text
+    }
+    get conversationDigest() {
+        // 这里需要实现具体的逻辑
+        return this.content
+    }
+    get contentType() {
+        // 这里需要实现具体的逻辑
+        return contentType; // 示例实现
+    }
+    decodeJSON(content) {
+        this.content = content.text;
+    }
+    encodeJSON() {
+        return {
+          content: this.content
+        };
+    }
   }
-
-  encodeJSON() {
-      return {
-        content: this.content
-      };
+  // 注册101类型为面试
+  WKSDK.shared().register(contentType, () => new ObjectContent())
+  return {
+    ObjectContent
   }
 }
-// 注册101类型为面试
-WKSDK.shared().register(101, () => new ObjectContent())
 
 
 const HISTORY_QUERY = {
@@ -119,13 +125,13 @@ export function useDataSource () {
             // const message = Convert.toMessage(msg);
             // msg.channel = new Channel(msg.channel_id, msg.channel_type)
             msg.payload = JSON.parse(Base64.decode(msg.payload))
-            if (msg.payload.type === 101) {
+            if (ObjType.includes(msg.payload.type)) {
               msg.payload.content = JSON.parse(msg.payload.content ?? '{}')
             }
             resultMessages.push(msg);
         });
     }
-    console.log(resultMessages)
+    // console.log(resultMessages)
     const more = resp.more === 1
     return {
       more,
@@ -344,23 +350,30 @@ export async function getMoreMessages (pageSize, channel) {
  * @param { Number } type : 101 面试主体 
  * @returns 
  */
+  // 发送职位使用101
 export function send (text, _channel, type) {
   let _text
   if (type === 101) {
     _text = new ObjectContent(text)
     WKSDK.shared().chatManager.send(_text, _channel)
-    console.log(_text)
+    // console.log(_text)
+    return
+  }
+  if (type === 102) {
+    _text = new ObjectContent2(text)
+    WKSDK.shared().chatManager.send(_text, _channel)
+    // console.log(_text)
     return
   }
   _text = new MessageText(text)
-  console.log(_text)
+  // console.log(_text)
   WKSDK.shared().chatManager.send(_text, _channel)
 }
 
 // 对话开场白
 export async function prologue ({userId, enterpriseId, text}) {
   const { channel } = await checkConversation(userId, enterpriseId)
-  send(text, channel)
+  send(text, channel, 102)
 }
 
 // 检测是否存在频道

+ 102 - 31
src/views/recruit/components/message/components/chatting.vue

@@ -49,6 +49,48 @@
         </div>
         <div v-for="(val, i) in items" :key="i" :id="val.id">
           <div class="time-box">{{ timesTampChange(+(val.timestamp.padEnd(13, '0'))) }}</div>
+          
+          <!-- <template v-if="val.payload.type === 102 && val.from_uid !== IM.uid"> -->
+          <template v-if="val.payload.type === 102">
+            <v-card
+              color="teal"
+              variant="tonal"
+              class="mx-auto"
+              width="400"
+              min-height="150"
+              :elevation="3"
+            >
+              <div class="pa-3">
+                <div class="text-h6"> {{ val.payload.content.positionInfo.name }}</div>
+                <div class="text-subtitle-2">薪酬待遇: {{ val.payload.content.positionInfo.payFrom }} - {{ val.payload.content.positionInfo.payTo }}</div>
+                <div>
+                  <v-chip
+                    color="secondary"
+                    v-for="(v, i) in val.payload.content.positionInfo.enterprise.welfareList"
+                    :key="val.message_id + v + i"
+                    x-small
+                    class="mt-1 mr-1"
+                  >
+                    {{ v }}
+                  </v-chip>
+                </div>
+                <v-divider class="my-3"></v-divider>
+                <div class="text-subtitle-2 text-right">
+                  <v-avatar size="24">
+                    <v-img :src="val.payload.content.positionInfo.contact.avatar"></v-img>
+                  </v-avatar>
+                  {{ val.payload.content.positionInfo.contact.name }}
+                  {{ val.payload.content.positionInfo.contact.postNameCn }}
+                  {{ val.payload.content.positionInfo.enterprise.name }}
+                </div>
+                <div class="text-subtitle-2 text-right">
+                  地址:{{ val.payload.content.positionInfo.address }}
+                </div>
+                
+                
+              </div>
+            </v-card>
+          </template>
           <div :class="['message-view_item', val.from_uid === IM.uid ? 'is-self' : 'is-other']">
             <div style="width: 40px; height: 40px;">
               <v-avatar>
@@ -60,7 +102,14 @@
                 ></v-img>
               </v-avatar>
             </div>
-            <div v-if="val.payload.type === 101">
+            <!-- 显示沟通职位 -->
+            <template v-if="val.payload.type === 102">              
+              <div  class="message-text" :class="{ active: val.from_uid === IM.uid}">
+                {{ val.payload.content.text }}
+              </div>
+            </template>
+            <!-- 发起职位 -->
+            <div v-else-if="val.payload.type === 101">
               <v-chip
                 class="ma-2"
                 color="teal"
@@ -74,29 +123,42 @@
               {{ val.payload.content }}
             </div>
           </div>
-          <div v-if="val.payload.type === 101 && val.from_uid !== IM.uid" class="d-flex justify-center">
+          <!-- 插入面试职位邀请 -->
+          <div v-if="val.payload.type === 101" class="d-flex justify-center">
             <v-card
               color="teal"
               variant="tonal"
               class="mx-auto"
+              min-width="400"
+              min-height="150"
+              :elevation="3"
             >
               <v-card-item>
                 <div>
+                  <!-- {{val.payload.content}} -->
                   <div class="text-overline mb-1">
                     面试邀请
                   </div>
-                  <div class="text-h6 mb-1">
-                    {{ val.payload.content.jobName }}
+                  <div class=" d-flex justify-space-between">
+                    <div class="text-h6 mb-1">
+                      {{ val.payload.content.positionInfo.data.name }}
+                    </div>
+                    <div>
+                      {{ val.payload.content.positionInfo.data.payFrom }} - 
+                      {{ val.payload.content.positionInfo.data.payTo }}
+                    </div>
                   </div>
-                  
+
                   <!-- <div class="text-caption"></div> -->
-                  <div class="text-caption">联系电话: {{ val.payload.content.invitePhone }} 面试时间: {{ val.payload.content.time }}</div>
+                  <div class="text-caption">面试时间: {{ timesTampChange(val.payload.content?.time) }}</div>
+                  <div class="text-caption">面试地点: {{ val.payload.content.address }}</div>
+                  <div class="text-caption">联系电话: {{ val.payload.content.invitePhone }}</div>
                 </div>
               </v-card-item>
 
-              <v-card-actions>
-                <v-btn>
-                  同意
+              <v-card-actions class="justify-end" v-if="val.from_uid !== IM.uid">
+                <v-btn  @click="handleAgree(val.payload.content)">
+                  接受邀请
                 </v-btn>
               </v-card-actions>
             </v-card>
@@ -106,7 +168,8 @@
     </div>
     <!-- <v-divider></v-divider> -->
     <div class="tools pa-3" v-if="Object.keys(info).length > 0">
-      <v-btn
+      <slot name="tools"></slot>
+      <!-- <v-btn
         v-for="tool in tools"
         :key="tool.name"
         size="small"
@@ -115,7 +178,7 @@
         @click="tool.handle"
       >
         {{ tool.name }}
-      </v-btn>
+      </v-btn> -->
     </div>
     <div class="bottom-info">
       <v-divider></v-divider>
@@ -148,16 +211,16 @@
 
 <script setup>
 defineOptions({ name: 'message-chatting'})
-import { ref, nextTick, onMounted, inject } from 'vue'
+import { ref, nextTick, onMounted } from 'vue'
 import { timesTampChange } from '@/utils/date'
 import { useIMStore } from '@/store/im'
 
 import { useUserStore } from '@/store/user'
-const isEnterprise = inject('isEnterprise')
+// const isEnterprise = inject('isEnterprise')
 
-const emits = defineEmits(['handleInquire', 'handleInvite', 'handleMore', 'handleSend'])
+const emits = defineEmits(['handleMore', 'handleSend'])
 
-const props = defineProps({
+defineProps({
   items: {
     type: Array,
     default: () => []
@@ -188,26 +251,26 @@ const inputVal = ref('')
 const pullDowning = ref(false) // 下拉中
 const pulldownFinished = ref(false) // 下拉完成
 
-const enterpriseTools = [
-  { name: '查看面试', icon: 'mdi-email-newsletter', handle: handleInquire },
-  { name: '面试邀约', icon: 'mdi-email', handle: handleInvite }
-]
+// const enterpriseTools = [
+//   { name: '查看面试', icon: 'mdi-email-newsletter', handle: handleInquire },
+//   { name: '面试邀约', icon: 'mdi-email', handle: handleInvite }
+// ]
 
-const userTools = [
-  { name: '查看面试', icon: 'mdi-email-newsletter', handle: handleInquire }
-]
+// const userTools = [
+//   { name: '查看面试', icon: 'mdi-email-newsletter', handle: handleInquire }
+// ]
 
-const tools = isEnterprise ? enterpriseTools : userTools
+// const tools = isEnterprise ? enterpriseTools : userTools
 
 // 查看
-function handleInquire () {
-  console.log(props.info)
-  emits('handleInquire', props.info.userId, props.info.enterpriseId || undefined)
-}
-// 邀请
-function handleInvite () {
-  emits('handleInvite', props.info.userId, props.info.enterpriseId || undefined)
-}
+// function handleInquire () {
+//   console.log(props.info)
+//   emits('handleInquire', props.info.userId, props.info.enterpriseId || undefined)
+// }
+// // 邀请
+// function handleInvite () {
+//   emits('handleInvite', props.info.userId, props.info.enterpriseId || undefined)
+// }
 
 // 滚动到底部
 const scrollBottom = () => {
@@ -305,6 +368,13 @@ const handleKeyDown = (event) => {
 const reset = () => {
   inputVal.value = ''
 }
+
+const handleAgree = (val) => {
+  const { positionInfo, ...obj } = val
+  console.log(positionInfo)
+  emits('handleAgree', obj)
+}
+
 defineExpose({
   reset,
   changeLoading,
@@ -380,6 +450,7 @@ defineExpose({
     margin: 8px 0;
     position: relative;
     .message-text {
+      overflow-wrap: break-word;
       background-color: #f0f2f5;
       border-radius: 6px;
       max-width: 85%;

+ 102 - 15
src/views/recruit/components/message/index.vue

@@ -79,15 +79,32 @@
         :has-more="hasMore"
         @handleSend="handleUpdate"
         @handleMore="handleGetMore"
-        @handleInquire="handleInquire"
-        @handleInvite="handleInvite"
-      ></Chatting>
+        @handleAgree="handleAgree"
+      >
+        <template #tools>
+          <v-btn
+            v-for="tool in tools"
+            :key="tool.name"
+            size="small"
+            :prepend-icon="tool.icon"
+            class="mr-3"
+            :color="tool.color"
+            @click="tool.handle(tool)"
+          >
+            {{ tool.name }}
+          </v-btn>
+        </template>
+      </Chatting>
     </div>
   </div>
+  <CtDialog :visible="showInvite" :widthType="2" titleClass="text-h6" title="面试信息" @close="showInvite = false" @submit="handleSubmit">
+    <InvitePage v-if="showInvite" ref="inviteRef" :item-data="itemData" :position="positionList"></InvitePage>
+  </CtDialog>
 </template>
 
 <script setup>
 defineOptions({ name: 'personal-message-index'})
+import InvitePage from '@/views/recruit/enterprise/interview/components/invite'
 import { timesTampChange } from '@/utils/date'
 import { ref, inject, watch,onMounted, nextTick } from 'vue'
 import Chatting from './components/chatting.vue'
@@ -97,11 +114,19 @@ import { getPositionDetails } from '@/api/position'
 import { getUserInfo } from '@/api/personal/user'
 import { useIMStore } from '@/store/im'
 import { useUserStore } from '@/store/user'
+// import { getJobAdvertisedSearch } from '@/api/position'
 import Snackbar from '@/plugins/snackbar'
 
+import { getJobAdvertised } from '@/api/enterprise'
+import { dealDictArrayData } from '@/utils/position'
+import { saveInterviewInvite } from '@/api/recruit/enterprise/interview'
+import { useI18n } from '@/hooks/web/useI18n'
+// import Confirm from '@/plugins/confirm'
+const { t } = useI18n()
 const chatRef = ref()
 
 const IM = useIMStore()
+// 自己的信息
 const { baseInfo } = useUserStore()
 
 const isEnterprise = inject('isEnterprise')
@@ -113,8 +138,14 @@ const messageItems = ref([])
 const pageSize = ref(1)
 const hasMore = ref(false)
 
+const showInvite = ref(false)
+
+const positionList = ref([])
 
 const showDelete = ref(false)
+const itemData = ref({})
+
+const inviteRef = ref()
 
 if (!IM) {
   console.log('IM is disconnected')
@@ -200,6 +231,17 @@ const handleUpdate = (val) => {
 //   console.log(searchInputVal.value, 'search')
 // }
 
+const enterpriseTools = ref([
+  { name: '查看面试', icon: 'mdi-email-newsletter', color:"teal", handle: handleInquire },
+  { name: '面试邀约', icon: 'mdi-email', color:"warning", loading: false, handle: handleInvite }
+])
+
+const userTools = ref([
+  { name: '查看面试', icon: 'mdi-email-newsletter', color:"teal", handle: handleInquire }
+])
+
+const tools = isEnterprise ? enterpriseTools.value : userTools.value
+
 async function handleChange (items) {
   // console.log([...items])
   try {
@@ -246,28 +288,71 @@ const handleDelete = async ({ channel }) => {
   updateUnreadCount()
 }
 
+// const handleSendInvite  = (item) => {
+//   Confirm('是否发送该职位邀请').then(() => {
+//     send(JSON.stringify(item), channelItem.value, 101)
+//   })
+// }
+
 // 没有企业ID则enterpriseId为undefined
 // 发送消息体 { text, type: 2 }
 // 面试邀约
-const handleInvite = (userId, enterpriseId) => {
-  const query = { userId, enterpriseId }
-  // IM 信息发送
-  const msg = {
-    jobName: '可控核聚变工程师',
-    time: '2024-07-06 15:00:00',
-    invitePhone: '138001380000',
-    address: '北京市海淀区中关村',
-    remark: '备注'
+async function handleInvite (item) {
+  item.loading = true
+  positionList.value = []
+  try {
+    const data = await getJobAdvertised({ hire: false })
+    if (!data.length) return
+    const list = dealDictArrayData([], data)
+    positionList.value = list.map(e => {
+      return {
+        label: `${e.name}${e.areaName ? '_' + e.areaName : ''} ${e.payFrom}-${e.payTo}/${e.payName}`,
+        value: e.id,
+        data: e
+      }
+    })
+    // itemData.value = {
+    //   userId: '',
+    //   jobId: ''
+    // }
+    showInvite.value = true
+    // send(JSON.stringify(msg), channelItem.value, 101)
+    // console.log(query)
+  } catch (error) {
+    console.log(error)
+  } finally {
+    item.loading = false
   }
-  send(JSON.stringify(msg), channelItem.value, 101)
-  console.log(query)
+  
 }
 // 查看面试
-const handleInquire = (userId, enterpriseId) => {
+function handleInquire (userId, enterpriseId) {
   const query = { userId, enterpriseId }
   console.log(query)
 }
 
+const handleSubmit = async () => {
+  // console.log(inviteRef.value.CtFormRef.validate)
+  const { valid } = await inviteRef.value.CtFormRef.formRef.validate()
+  if (!valid) {
+    return
+  }
+  const query = inviteRef.value.getQuery()
+  query.userId = info.value.userId
+  query.positionInfo = positionList.value.find(e => e.value === query.jobId)
+  send(JSON.stringify(query), channelItem.value, 101)
+  // console.log(query)
+  // await saveInterviewInvite(query)
+  // Snackbar.success(t('common.operationSuccessful'))
+  showInvite.value = false
+
+}
+
+const handleAgree = async (val) => {
+  await saveInterviewInvite(val)
+    Snackbar.success(t('common.operationSuccessful'))
+}
+
 </script>
 
 <style scoped lang="scss">
@@ -275,6 +360,7 @@ const handleInquire = (userId, enterpriseId) => {
   display: flex;
   &-left {
     position: relative;
+    flex-shrink: 0;
     height: 100%;;
     width: 360px;
     background-color: #fff;
@@ -326,6 +412,7 @@ const handleInquire = (userId, enterpriseId) => {
   &-right {
     height: 100%;
     flex: 1;
+    width: 0;
     position: relative;
     background-color: #fff;
     border-radius: 8px;

+ 5 - 1
src/views/recruit/personal/position/components/details.vue

@@ -291,7 +291,11 @@ const toDetails = async (info) => {
   const userId = info.contact.userId
   const enterpriseId = info.contact.enterpriseId
   const text = '您好,我对该职位很感兴趣,希望能有机会与您进一步沟通。'
-  await prologue({userId, enterpriseId, text})
+  const textObj = {
+    text: text,
+    positionInfo: positionInfo.value
+  }
+  await prologue({userId, enterpriseId, text: JSON.stringify(textObj)})
   let url = `/recruit/personal/message?id=${info.id}`
   if (info.contact.enterpriseId) {
     url += `&enterprise=${info.contact.enterpriseId}`