Browse Source

临时通信

zhengnaiwen_citu 10 months ago
parent
commit
8715dac80f

+ 7 - 0
src/api/common/index.js

@@ -228,4 +228,11 @@ export const getConversationSync = async (data) => {
     url: '/app-api/im/conversation/sync',
     data
   })
+}
+// 同步最近会话
+export const getMessageSync = async (data) => {
+  return await request.post({
+    url: '/app-api/im/im/channel/messagesync',
+    data
+  })
 }

+ 154 - 79
src/hooks/web/useIM.js

@@ -2,7 +2,14 @@
 
 
 import { ref, onMounted, onUnmounted } from 'vue';
-import { getConversationSync } from '@/api/common'
+import { getConversationSync, getMessageSync, getChatKey } from '@/api/common'
+import { Base64 } from 'js-base64'
+
+import { useUserStore } from '@/store/user'
+import { useLoginType } from '@/store/loginType'
+
+const userStore = useUserStore()
+const loginType = useLoginType()
 
 // 配置悟空IM
 import {
@@ -12,11 +19,13 @@ import {
   ChannelTypePerson,
   // ChannelTypeGroup
 } from "wukongimjssdk"
-import Snackbar from '@/plugins/snackbar'
-
-import { useUserStore } from '@/store/user'
 
-const userStore = useUserStore()
+const HISTORY_QUERY = {
+  limit: 15,
+  startMessageSeq: 0,
+  endMessageSeq: 0,
+  pullMode: 1
+}
 
 const ConnectStatus = {
   Disconnect: 0, // 断开连接
@@ -26,115 +35,181 @@ const ConnectStatus = {
   ConnectKick: 4, // 连接被踢,服务器要求客户端断开(一般是账号在其他地方登录,被踢)
 }
 
-
 initDataSource()
 // api 接入
 function initDataSource () {
   // 最近会话数据源
   WKSDK.shared().config.provider.syncConversationsCallback  = async () => {
-    const res = await getConversationSync({ msg_count: 100 })
+    const query = {
+      msg_count: 100
+    }
+    if (loginType.loginType === 'enterprise') {
+      Object.assign(query, { enterpriseId: userStore.baseInfo.enterpriseId })
+    }
+    const res = await getConversationSync(query)
     return res
   }
+  // 同步频道消息数据源
+  WKSDK.shared().config.provider.syncMessagesCallback = async function(channel, opts) {
+    // 后端提供的获取频道消息列表的接口数据 然后构建成 Message对象数组返回
+    let resultMessages  = new Array()
+    const query = {
+      channel_id: channel.channelID,
+      channel_type: channel.channelType,
+      start_message_seq: opts.startMessageSeq,
+      end_message_seq: opts.endMessageSeq,
+      limit: opts.limit,
+      pull_mode: opts.pullMode,
+    }
+    if (loginType.loginType === 'enterprise') {
+      Object.assign(query, { enterpriseId: userStore.baseInfo.enterpriseId })
+    }
+    const resp = await getMessageSync(query)
+    const messageList = resp && resp["messages"]
+    if (messageList) {
+        messageList.forEach((msg) => {
+            // const message = Convert.toMessage(msg);
+            resultMessages.push(msg);
+        });
+    }
+    return resultMessages
+  }
 }
-export function initKey () {
-  return new Promise((resolve) => { 
-    userStore.getChatKey().then(() => {
-      resolve()
-    }).catch((error) => {
-      // 报错重连
-      console.log(error.msg)
-      setTimeout(() => {
-        initKey()
-      }, 3000)
-    })
-  })
+
+export const initKey = async () => {
+  // 通过自身userId和企业id获取token和uid
+  const keyQuery = {
+    userId: userStore.userInfo.id
+  }
+  if (loginType.loginType === 'enterprise') {
+    Object.assign(keyQuery, { enterpriseId: userStore.baseInfo.enterpriseId })
+  }
+  const { uid, wsUrl, token } = await getChatKey(keyQuery)
+  // 单机模式可以直接设置地址
+  WKSDK.shared().config.addr = 'ws://' + wsUrl // 默认端口为5200
+  // 认证信息
+  WKSDK.shared().config.uid = uid // 用户uid(需要在悟空通讯端注册过)
+  WKSDK.shared().config.token = token // 用户token (需要在悟空通讯端注册过)
+  return {
+    uid
+  }
 }
 
-export function initConnect () {
+export function initConnect (userId, enterpriseId, callback) {
+  
   const connected = ref(false)
   const conversationList = ref([])
-  // 单机模式可以直接设置地址
-  WKSDK.shared().config.addr = 'ws://' + userStore.IMConfig.wsUrl // 默认端口为5200
-  // 认证信息
-  WKSDK.shared().config.uid = userStore.IMConfig.uid // 用户uid(需要在悟空通讯端注册过)
-  WKSDK.shared().config.token = userStore.IMConfig.token // 用户token (需要在悟空通讯端注册过)
+  const messageItems = ref([])
+  const toUid = ref()
+  const channel = ref()
   // 连接
+  console.log(WKSDK.shared().config)
   onMounted(() => {
     // 连接状态监听
-    WKSDK.shared().connectManager.addConnectStatusListener(
-      async (status, reasonCode) => {
-        if (status === ConnectStatus.Connected) {
-          console.log('连接成功')
-          connected.value = true
-          // 获取列表
-          const res = await syncConversation()
-          conversationList.value = [...res]
-        } else {
-          console.log('reasonCode', reasonCode)
-          connected.value = false
-          conversationList.value = []
-        }
-      }
-    )
-
+    WKSDK.shared().connectManager.addConnectStatusListener(connectStatusListener)
     // 消息发送状态监听
     WKSDK.shared().chatManager.addMessageStatusListener(statusListen)
     // 常规消息监听
     WKSDK.shared().chatManager.addMessageListener(messageListen)
-    
-    connect()
+    // 连接
+    WKSDK.shared().connectManager.connect()
   })
   onUnmounted(() => {
-    // 连接状态监听移除
-    WKSDK.shared().connectManager.disconnect()
+    WKSDK.shared().connectManager.removeConnectStatusListener(connectStatusListener)
     // 消息发送状态监听移除
     WKSDK.shared().chatManager.removeMessageStatusListener(statusListen)
     // 常规消息监听移除
     WKSDK.shared().chatManager.removeMessageListener(messageListen)
+    // 连接状态监听移除
+    WKSDK.shared().connectManager.disconnect()
   })
-  return { connected, conversationList }
-}
 
-// 消息发送状态监听
-function statusListen (packet) {
-  if (packet.reasonCode === 1) {
-    // 发送成功
-  } else {
-    // 发送失败
+  const connectStatusListener = async (status) => {
+    console.log('连接状态', status)
+    if (status === ConnectStatus.Connected) {
+      console.log('连接成功')
+      connected.value = true
+      // 连接成功 创建channel
+      // const { message } = await syncConversation()
+      // conversationList.value = message
+      if (!userId) {
+        return
+      }
+      await initChart(userId, enterpriseId)
+      
+      callback()
+    } else {
+      connected.value = false
+      conversationList.value = []
+      toUid.value = null
+    }
   }
-}
 
-// 常规消息监听
-function messageListen (message) {
-  console.log('收到消息', message)
-  // message.content // 消息内容
-  // message.channel // 消息频道
-  // message.fromUID // 消息发送者
-}
+  // 消息发送状态监听
+  function statusListen (packet) {
+    if (packet.reasonCode === 1) {
+      // 发送成功
+      console.log('发送成功')
+    } else {
+      // 发送失败
+    }
+  }
 
+  // 常规消息监听
+  async function messageListen (message) {
+    console.log('收到消息', message)
+    const { channel: _channel } = message
+    const conversation = WKSDK.shared().conversationManager.findConversation(_channel)
+    if(!conversation) {
+      // 如果最近会话不存在,则创建一个空的会话
+      WKSDK.shared().conversationManager.createEmptyConversation(_channel)
+    }
+    channel.value = _channel
+    getMessage(_channel)
+    // 创建通道
+  }
 
-export function connect () {
-  WKSDK.shared().connectManager.connect()
-}
+  async function getMessage (channel) {
+    const res = await WKSDK.shared().chatManager.syncMessages(channel, HISTORY_QUERY)
+    // console.log(res)
+    messageItems.value = res.map(_e => {
+      _e.payload = JSON.parse(Base64.decode(_e.payload))
+      return _e
+    })
+  }
 
-export function send (user, text) {
-  // 例如发送文本消息hello给用户u10001
-  const _text = new MessageText(text) // 文本消息
-  WKSDK.shared().chatManager.send(_text, new Channel(user, ChannelTypePerson))
+  // 发起聊天
+  async function initChart (userId, enterpriseId) {
+    const { uid } = await getChatKey({userId, enterpriseId})
+    const _channel = new Channel(uid, ChannelTypePerson)
+    channel.value = _channel
+  }
 
-  // 例如发送文本消息hello给群频道g10001
-  // WKSDK.shared().chatManager.send(_text, new Channel(user,ChannelTypeGroup))
+  // 同步最近会话
+  // async function syncConversation () {
+  //   return new Promise((resolve, reject) => {
+  //     WKSDK.shared().conversationManager.sync().then(res => {
+  //       resolve(res)
+  //     }).catch(error => {
+  //       reject(error)
+  //     })
+  //   })
+  // }
+
+  return {
+    connected,
+    conversationList,
+    messageItems,
+    channel
+  }
 }
 
-// 同步最近会话
-export async function syncConversation () {
-  return new Promise((resolve) => {
-    WKSDK.shared().conversationManager.sync().then(res => {
-      resolve(res)
-    }).catch(error => {
-      Snackbar.error(error.msg)
-    })
-  })
+
+export function send (text, _channel) {
+  const _text = new MessageText(text) // 文本消息
+  console.log('发送', _text)
+  WKSDK.shared().chatManager.send(_text, _channel)
 }
 
 
+

+ 1 - 1
src/layout/company/navBar.vue

@@ -74,7 +74,7 @@
             </v-list>
           </v-menu> -->
 
-          <v-btn size="small" icon="mdi-bell-outline"></v-btn>
+          <v-btn size="small" icon="mdi-bell-outline" @click="router.push('/recruit/enterprise/communication')"></v-btn>
         </div>
       </div>
     </v-toolbar>

+ 1 - 1
src/store/user.js

@@ -27,7 +27,7 @@ export const useUserStore = defineStore('user',
       // loginType: null, // 登录类型 // 330为企业登录
       accountInfo: {}, // 登录返回的信息
       userInfo: localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')) : {}, // 当前登录账号信息
-      baseInfo: {}, // 人才信息
+      baseInfo: localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}, // 人才信息
       userAccount: {}, // 用户账户信息
       enterpriseUserAccount: {}, // 企业账户信息
       IMConfig: localStorage.getItem('IMConfig') ? JSON.parse(localStorage.getItem('IMConfig')) : {} // IM聊天密钥

+ 93 - 37
src/views/recruit/personal/message/components/chatting.vue → src/views/recruit/components/message/components/chatting.vue

@@ -3,37 +3,65 @@
     <div class="top-info">
       <div class="user-info d-flex align-center">
         <p class="d-flex align-center float-left">
-          <span class="name">钟女士</span>
-          <span>广州市番禺区洛浦街道12号</span>
+          <span class="name">{{ info.contact?.name }}</span>
+          <span>{{ info.address }}</span>
           <span class="septal-line"></span>
-          <span>负责人</span>
+          <span>{{ info.contact?.postNameCn }}</span>
         </p>
       </div>
-      <div class="position-content">
-        <span class="color-222">无责4k电话客服+朝九晚六+不加班</span>
-        <span class="salary mx-4">4-8k</span>
-        <span class="color-333">广州</span>
+      <div class="position-content" v-if="!isEnterprise">
+        <div class="d-flex mb-2">
+          <div class="font-weight-black">{{ info.name }}</div>
+          <div class="position-content-emolument">{{ info.payFrom }} - {{ info.payFrom }}</div>
+        </div>
+        <div class="text-subtitle-2">{{ info.enterprise?.name }}</div>
+        <div class="pt-3">
+          <v-chip
+            v-for="(item, index) in info.enterprise?.welfareList"
+            :key="item + index"
+            color="green"
+            label
+            class="mr-3 mb-3"
+          >
+            {{ item }}
+          </v-chip>
+        </div>
+        <!-- <div>{{ info.content }}</div> -->
       </div>
     </div>
     <div class="mt-3 message-box" @scroll="handleScroll" ref="chatRef">
       <div>
         <div v-for="(val, i) in items" :key="i" :id="val.id">
-          <div class="time-box">{{ val.time }}</div>
-          <div :class="['message-view_item', val.userId === myUserId ? 'is-self' : 'is-other']">
+          <div class="time-box">{{ timesTampChange(+(val.timestamp.padEnd(13, '0'))) }}</div>
+          <div :class="['message-view_item', val.from_uid === uid ? 'is-self' : 'is-other']">
             <div style="width: 40px; height: 40px;"><v-img :src="val.avatar" :width="40" height="40" rounded></v-img></div>
-            <div class="message-text">{{ val.text }}</div>
+            <div class="message-text">{{ val.payload.content }}</div>
           </div>
         </div>
       </div>
     </div>
     <div class="bottom-info">
       <v-divider></v-divider>
-      <div class="px-5 py-3">
-        <v-text-field  v-model="inputVal" label="请输入消息" color="primary" density="comfortable" variant="plain"></v-text-field>
-        <div class="d-flex align-center justify-end bottom-send">
+      <div class="pa-3">
+        <v-textarea
+          v-model="inputVal"
+          label="请输入消息"
+          placeholder="请输入消息"
+          hide-details
+          no-resize
+          bg-color="white"
+          variant="plain"
+        >
+          <!-- @keydown.stop.prevent="handleKeyDown" -->
+          <template #append-inner>
+            <v-btn color="primary" :disabled="!inputVal" style="align-self: center;" @click="handleSend">发送</v-btn>
+          </template>
+        </v-textarea>
+        <!-- <v-text-field  v-model="inputVal" label="请输入消息" color="primary" density="comfortable" variant="plain" hide-details></v-text-field> -->
+        <!-- <div class="d-flex align-center justify-end bottom-send">
           <div class="color-ccc font-size-14 mr-5">按Enter键发送</div>
-          <v-btn color="primary" size="small" :disabled="!inputVal">发送</v-btn>
-        </div>
+          <v-btn color="primary" size="small" :disabled="!inputVal" @click="handleSend">发送</v-btn>
+        </div> -->
       </div>
     </div>
   </div>
@@ -41,27 +69,37 @@
 
 <script setup>
 defineOptions({ name: 'message-chatting'})
-import { ref, nextTick, onMounted } from 'vue'
+import { ref, nextTick, onMounted, watch, defineEmits, inject } from 'vue'
+import { timesTampChange } from '@/utils/date'
+const isEnterprise = inject('isEnterprise')
+
+const emits = defineEmits(['inputVal'])
+const props = defineProps({
+  items: {
+    type: Array,
+    default: () => []
+  },
+  info: {
+    type: Object,
+    default: () => ({})
+  },
+  uid: {
+    type: String,
+    default: ''
+  }
+})
+
+watch(
+  () => props.items,
+  (res) => {
+    console.log('列表', res)
+  }
+)
 
 const chatRef = ref()
 const inputVal = ref()
 const pullDowning = ref(false) // 下拉中
 const pulldownFinished = ref(false) // 下拉完成
-const myUserId = 1
-
-const items = ref([
-  { userId: 1, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 1 },
-  { userId: 2, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 2 },
-  { userId: 2, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 3 },
-  { userId: 1, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 4 },
-  { userId: 1, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 5 },
-  { userId: 2, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 6 },
-  { userId: 1, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 7 },
-  { userId: 1, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 8 },
-  { userId: 2, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 9 },
-  { userId: 1, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 10 },
-  { userId: 2, text: '你好', avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg', time: '11:15', id: 11 }
-])
 
 // 滚动到底部
 const scrollBottom = () => {
@@ -79,6 +117,11 @@ onMounted(() => {
   })
 })
 
+
+const handleSend = () => {
+  emits('handleSend', inputVal)
+}
+
 // const pullDown = async () => {
 //   if (messages.value.length == 0) {
 //     return
@@ -128,6 +171,12 @@ const handleScroll = (e) => {
     // })
   }
 }
+// const handleKeyDown = (event) => {
+//   if (event.keyCode === 13) {
+    
+//     console.log(event)
+//   } 
+// }
 </script>
 
 <style scoped lang="scss">
@@ -136,16 +185,17 @@ const handleScroll = (e) => {
   height: 100%;
 }
 .top-info {
-  height: 104px;
+  // height: 104px;
+  padding: 0 30px;
   .user-info {
     position: relative;
     height: 48px;
     justify-content: space-between;
-    padding: 0 30px;
+    // padding: 0 30px;
     span {
       font-size: 14px;
       font-weight: 400;
-      color: var(--color-666);
+      // color: var(--color-666);
       line-height: 22px;
     }
     .name {
@@ -157,15 +207,21 @@ const handleScroll = (e) => {
     position: relative;
     padding: 16px;
     border-radius: 12px;
-    margin: auto;
-    width: 760px;
-    background: linear-gradient(90deg, #f5fcfc, #fcfbfa);
+    // margin: auto;
+    width: 60%;
+    max-width: 800px;
+    // width: 760px;
+    background: linear-gradient(90deg, #d5ffff, #f0fff4);
     .salary {
       font-size: 20px;
       font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
       color: var(--v-error-base);
       line-height: 24px;
     }
+    &-emolument {
+      color: #f30;
+      font-weight: 600;
+    }
   }
 }
 .message-box {

+ 206 - 0
src/views/recruit/components/message/index.vue

@@ -0,0 +1,206 @@
+<template>
+  <div class="default-width message pa-3" :style="`height: calc(100vh - ${isEnterprise ? '130px' : '50px'});`">
+    <div class="message-left">
+      <div class="message-left-search d-flex align-center justify-center">
+        <!-- {{ connected ? '连接成功': '连接失败' }} -->
+        <TextInput v-model="searchInputVal" :item="textItem" @appendInnerClick="handleSearch" @enter="handleSearch"></TextInput>
+      </div>
+      <div class="message-chat-box mt-5">
+        <v-overlay
+          :model-value="!connected"
+          contained
+          class="align-center justify-center"
+        >
+          <v-progress-circular
+            color="primary"
+            size="64"
+            indeterminate
+          ></v-progress-circular>
+        </v-overlay>
+        <div v-if="conversationList.length">
+          <v-list density="compact">
+            <v-list-item
+              v-for="(val, i) in conversationList"
+              :key="i"
+              :value="val"
+              color="primary"
+              :title="val.channel_id"
+              :subtitle="val.timestamp"
+            >
+              <template v-slot:prepend>
+                <v-avatar :image="val.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+              </template>
+              <template v-slot:append>
+                <v-badge
+                  v-if="val.unread > 0"
+                  color="error"
+                  :content="val.unread"
+                  inline
+                ></v-badge>
+              </template>
+            </v-list-item>
+          </v-list>
+          <div class="message-no-more-text mt-3">没有更多了</div>
+        </div>
+        <div v-else class="left-noData">
+          <Empty :elevation="false" message="暂无30天内联系人" width="300" height="150"></Empty>
+        </div>
+      </div>
+    </div>
+
+    <div class="message-right">
+      <div v-if="showRightNoData" class="right-noData">
+        <Empty :elevation="false" message="与您进行过沟通的 Boss 都会在左侧列表中显示"></Empty>
+      </div>
+      <Chatting :items="messageItems" :info="info" :uid="uid" @handleSend="handleUpdate"></Chatting>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'personal-message-index'})
+import { ref, inject } from 'vue'
+import Chatting from './components/chatting.vue'
+import { initConnect, send, initKey } from '@/hooks/web/useIM'
+import { useRoute } from 'vue-router'
+import { getPositionDetails } from '@/api/position'
+// import { useUserStore } from '@/store/user'
+
+// const userStore = useUserStore()
+const isEnterprise = inject('isEnterprise')
+// 实例
+const route = useRoute()
+
+// const channel = ref(null)
+
+// const enterpriseId = isEnterprise ? userStore.baseInfo.enterpriseId : route.query.enterprise
+
+const { uid } = await initKey()
+const {
+  connected,
+  conversationList,
+  messageItems,
+  channel
+} = initConnect(route.query.id, route.query.enterprise)
+
+
+
+
+const showRightNoData = ref(false)
+const searchInputVal = ref()
+const textItem = ref({
+  type: 'text',
+  value: null,
+  width: 336,
+  clearable: true,
+  hideDetails: true,
+  appendInnerIcon: 'mdi-magnify',
+  label: '搜索30天内的联系人'
+})
+
+const info = ref({})
+
+if (route.query.job) {
+  const res = await getPositionDetails({ id: route.query.job })
+  info.value = res
+}
+
+// 左侧聊天列表
+// const chatList = ref([
+//   {
+//     id: 1,
+//     name: '钟女士',
+//     avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg',
+//     enterpriseName: '泰康泰康泰康泰康泰康泰康',
+//     postName: '人事HR',
+//     updateTime: 1715337950000,
+//     tip: '你好'
+//   },
+//   {
+//     id: 2,
+//     name: '林先生',
+//     avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg',
+//     enterpriseName: '众宝联合',
+//     postName: '人力资源主管',
+//     updateTime: 1715337950000,
+//     tip: '你好'
+//   }
+// ])
+
+const handleUpdate = (val) => {
+  send(val.value, channel.value)
+}
+
+const handleSearch = () => {
+  console.log(searchInputVal.value, 'search')
+}
+</script>
+
+<style scoped lang="scss">
+.message {
+  display: flex;
+  &-left {
+    position: relative;
+    height: 100%;;
+    width: 360px;
+    background-color: #fff;
+    border-radius: 8px;
+    margin-right: 12px;
+    .message-left-search {
+      width: 100%;
+      height: 60px;
+      background: linear-gradient(90deg, #f5fcfc, #fcfbfa);
+      border-radius: 8px 8px 0 0;
+    }
+    .message-chat-box {
+      .chat-item {
+        position: relative;
+        width: 100%;
+        height: 78px;
+        padding: 14px 12px;
+        cursor: pointer;
+        &:hover {
+          background-color: #f8f8f8;
+        }
+        .chat-item-time {
+          position: absolute;
+          right: 12px;
+          top: 50%;
+          transform: translateY(-50%);
+        }
+        .title-box {
+          max-width: 114px;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+          display: inline-block;
+        }
+      }
+    }
+    .message-no-more-text {
+      color: var(--color-999);
+      font-size: 14px;
+      text-align: center
+    }
+    .left-noData {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+    }
+  }
+  &-right {
+    height: 100%;
+    flex: 1;
+    position: relative;
+    background-color: #fff;
+    border-radius: 8px;
+    .right-noData {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+    }
+  }
+}
+</style>

+ 5 - 2
src/views/recruit/enterprise/communication/index.vue

@@ -1,11 +1,14 @@
 <template>
-  <div>沟通</div>
+  <Message></Message>
 </template>
 
 <script setup>
 defineOptions({ name: 'enterprise-communication'})
+import Message from '../../components/message'
+import { provide } from 'vue'
+provide('isEnterprise', true)
 </script>
 
 <style scoped lang="scss">
 
-</style>
+</style>

+ 5 - 204
src/views/recruit/personal/message/index.vue

@@ -1,209 +1,10 @@
 <template>
-  <div class="default-width message pa-3">
-    <div class="message-left">
-      <div class="message-left-search d-flex align-center justify-center">
-        <!-- {{ connected ? '连接成功': '连接失败' }} -->
-        <TextInput v-model="searchInputVal" :item="textItem" @appendInnerClick="handleSearch" @enter="handleSearch"></TextInput>
-      </div>
-      <div class="message-chat-box mt-5">
-        <v-overlay
-          :model-value="!connected"
-          contained
-          class="align-center justify-center"
-        >
-          <v-progress-circular
-            color="primary"
-            size="64"
-            indeterminate
-          ></v-progress-circular>
-        </v-overlay>
-        <div v-if="conversationList.length">
-          <v-list density="compact">
-            <v-list-item
-              v-for="(val, i) in conversationList"
-              :key="i"
-              :value="val"
-              color="primary"
-              :title="val.name + val.enterpriseName + val.postName"
-              :subtitle="val.tip"
-            >
-              <template v-slot:prepend>
-                <v-avatar :image="val.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
-              </template>
-              <template v-slot:append>
-                <v-btn
-                  color="grey-lighten-1"
-                  icon="mdi-information"
-                  variant="text"
-                ></v-btn>
-              </template>
-
-
-              <!-- <v-list-item-title>
-                <span class="font-size-15">{{ val.name }}</span>
-                <span class="ml-3 title-box">{{ val.enterpriseName }}</span>
-                <span class="septal-line"></span>
-                <span>{{ val.postName }}</span>
-                <span>17.25</span>
-              </v-list-item-title> -->
-            </v-list-item>
-          </v-list>
-          <!-- <div class="chat-item d-flex align-center" v-for="(val, i) in chatList" :key="i">
-            <v-avatar :image="val.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
-            <div class="ml-3 font-size-13">
-              <div class="chat-item-time color-999">17.25</div>
-              <div class="d-flex align-center">
-                <span class="font-size-15">{{ val.name }}</span>
-                <span class="ml-3 title-box">{{ val.enterpriseName }}</span>
-                <span class="septal-line"></span>
-                <span>{{ val.postName }}</span>
-              </div>
-              <div class="color-999 mt-1">{{ val.tip }}</div>
-            </div>
-          </div> -->
-          <div class="message-no-more-text mt-3">没有更多了</div>
-        </div>
-        <div v-else class="left-noData">
-          <Empty :elevation="false" message="暂无30天内联系人" width="300" height="150"></Empty>
-        </div>
-      </div>
-    </div>
-
-    <div class="message-right">
-      <div v-if="showRightNoData" class="right-noData">
-        <Empty :elevation="false" message="与您进行过沟通的 Boss 都会在左侧列表中显示"></Empty>
-      </div>
-      <Chatting></Chatting>
-    </div>
-  </div>
+  <Message></Message>
 </template>
 
 <script setup>
 defineOptions({ name: 'personal-message-index'})
-import { ref } from 'vue'
-import Chatting from './components/chatting.vue'
-import { initKey, initConnect } from '@/hooks/web/useIM'
-// 实例
-
-import { useUserStore } from '@/store/user'
-
-const userStore = useUserStore()
-
-if (Object.keys(userStore.IMConfig).length === 0) {
-  await initKey()
-}
-
-const { connected, conversationList } = initConnect()
-
-console.log('connected.value', connected.value)
-
-// const IMStatus = ref(isConnected)
-
-
-const showRightNoData = ref(false)
-const searchInputVal = ref()
-const textItem = ref({
-  type: 'text',
-  value: null,
-  width: 336,
-  clearable: true,
-  hideDetails: true,
-  appendInnerIcon: 'mdi-magnify',
-  label: '搜索30天内的联系人'
-})
-
-// 左侧聊天列表
-// const chatList = ref([
-//   {
-//     id: 1,
-//     name: '钟女士',
-//     avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg',
-//     enterpriseName: '泰康泰康泰康泰康泰康泰康',
-//     postName: '人事HR',
-//     updateTime: 1715337950000,
-//     tip: '你好'
-//   },
-//   {
-//     id: 2,
-//     name: '林先生',
-//     avatar: 'https://minio.citupro.com/dev/menduner/tx1.jpg',
-//     enterpriseName: '众宝联合',
-//     postName: '人力资源主管',
-//     updateTime: 1715337950000,
-//     tip: '你好'
-//   }
-// ])
-
-const handleSearch = () => {
-  console.log(searchInputVal.value, 'search')
-}
-</script>
-
-<style scoped lang="scss">
-.message {
-  display: flex;
-  &-left {
-    position: relative;
-    width: 360px;
-    background-color: #fff;
-    border-radius: 8px;
-    height: calc(100vh - 74px);
-    margin-right: 12px;
-    .message-left-search {
-      width: 100%;
-      height: 60px;
-      background: linear-gradient(90deg, #f5fcfc, #fcfbfa);
-      border-radius: 8px 8px 0 0;
-    }
-    .message-chat-box {
-      .chat-item {
-        position: relative;
-        width: 100%;
-        height: 78px;
-        padding: 14px 12px;
-        cursor: pointer;
-        &:hover {
-          background-color: #f8f8f8;
-        }
-        .chat-item-time {
-          position: absolute;
-          right: 12px;
-          top: 50%;
-          transform: translateY(-50%);
-        }
-        .title-box {
-          max-width: 114px;
-          overflow: hidden;
-          white-space: nowrap;
-          text-overflow: ellipsis;
-          display: inline-block;
-        }
-      }
-    }
-    .message-no-more-text {
-      color: var(--color-999);
-      font-size: 14px;
-      text-align: center
-    }
-    .left-noData {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-    }
-  }
-  &-right {
-    flex: 1;
-    position: relative;
-    background-color: #fff;
-    border-radius: 8px;
-    height: calc(100vh - 74px);
-    .right-noData {
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      transform: translate(-50%, -50%);
-    }
-  }
-}
-</style>
+import Message from '../../components/message'
+import { provide } from 'vue'
+provide('isEnterprise', false)
+</script>

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

@@ -88,7 +88,14 @@
               </div>
             </div>
             <div class="mt-3 text-center">
-              <v-btn class="mr-2 radius button-item" color="success" variant="outlined">{{ $t('position.communicate') }}</v-btn>
+              <v-btn
+                class="mr-2 radius button-item"
+                color="success"
+                variant="outlined"
+                @click="toDetails(info)"
+              >
+                {{ $t('position.communicate') }}
+              </v-btn>
               <v-btn class="radius button-item" :disabled="delivery" color="primary" @click="handleDelivery">{{ delivery ? $t('position.delivered') : $t('position.submitResume') }}</v-btn>
             </div>
           </div>
@@ -299,6 +306,14 @@ const handleSubmit = async (val) =>{
   handleClose()
   deliveryCheck()
 }
+
+const toDetails = (info) => {
+  let url = `/recruit/personal/message?id=${info.contact.userId}&job=${info.id}`
+  if (info.contact.enterpriseId) {
+    url += `&enterprise=${info.contact.enterpriseId}`
+  }
+  router.push(url)
+}
 </script>
 
 <style lang="scss" scoped>