فهرست منبع

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

lifanagju_citu 10 ماه پیش
والد
کامیت
9db2d565ea

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

@@ -205,4 +205,12 @@ export const getUserAccount = async () => {
   return await request.get({
     url: '/app-api/menduner/system/mde-user/get/account'
   })
+}
+
+// 获取聊天秘钥信息
+export const getChatKey = async (data) => {
+  return await request.post({
+    url: 'admin-api/im/user/get',
+    data
+  })
 }

+ 8 - 0
src/api/recruit/enterprise/position/index.js

@@ -0,0 +1,8 @@
+import request from '@/config/axios'
+
+// 众聘比例信息
+export const getPublicRatio = async () => {
+  return await request.get({
+    url: '/admin-api/menduner/system/hire-commission-ratio/get'
+  })
+}

+ 1 - 0
src/components/CtForm/index.vue

@@ -14,6 +14,7 @@
                 v-if="['text', 'password', 'number'].includes(item.type)"
                 v-model="item.value"
                 :item="item"
+                @blur="item.blur"
                 @change="handleChange(item)"
               ></textUI>
               <autocompleteUI

+ 6 - 1
src/components/FormUI/TextInput/index.vue

@@ -29,6 +29,7 @@
       @click:append-inner="appendInnerClick"
       @keyup.enter="handleKeyup"
       @click:clear="handleClear"
+      @blur="handleBlur"
     ></v-text-field>
   </div>
 </template>
@@ -38,7 +39,7 @@ import { defineEmits, ref, watch } from 'vue';
 defineOptions({ name:'FormUI-v-text-field'})
 
 const props = defineProps({item: Object, modelValue: [String, Number]})
-const emit = defineEmits(['update:modelValue', 'change', 'appendClick', 'appendInnerClick', 'enter'])
+const emit = defineEmits(['update:modelValue', 'change', 'appendClick', 'appendInnerClick', 'enter', 'blur'])
 const item = props.item
 const value = ref(props.modelValue)
 const searchDebouncedTime = item?.searchDebouncedTime === 0 ? ref(0) : ref(500)
@@ -72,6 +73,10 @@ const handleKeyup = () => {
   emit('enter', value.value)
 }
 
+const handleBlur = () => {
+  emit('blur', props.item, value.value)
+}
+
 const handleWheel = (event, item) => {
   if (item.type !== 'number') return
   event.preventDefault()

+ 1 - 1
src/components/Position/longStrip.vue

@@ -18,7 +18,7 @@
         <div class="job-info">
           <div class="job-name cursor-pointer">
             <span class="mr-3 info-name">{{ val.job.name }}</span>
-            <span>[{{ val.job.areaName }}]</span>
+            <span v-if="val?.job?.areaName">[{{ val.job.areaName }}]</span>
           </div>
           <div class="job-other">
             <span class="salary">{{ val.job.payFrom }}-{{ val.job.payTo }}/{{ val.job.payName }}</span>

+ 2 - 1
src/layout/index.vue

@@ -4,7 +4,7 @@
     <div class="content">
       <router-view></router-view>
     </div>
-    <Footers class="mt-10"></Footers>
+    <Footers v-if="footerWhiteList.indexOf(router.currentRoute.value.path) === -1" class="mt-10"></Footers>
     <Slider v-if="whiteList.indexOf(router.currentRoute.value.path) === -1" class="slider"></Slider>
   </div>
 </template>
@@ -19,6 +19,7 @@ defineOptions({ name: 'personal-layout-index' })
 
 // 不展示侧边栏名单
 const whiteList = ['/login', '/privacyPolicy', '/userAgreement', '/register']
+const footerWhiteList = ['/recruit/personal/message']
 const router = useRouter()
 
 const sharedState = useSharedState()

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

@@ -84,7 +84,7 @@
           </v-menu>
           <div class="d-flex align-center" v-if="getToken()">
             <span class="cursor-pointer mx-5" @click="router.push({ path: '/recruit/personal/TaskCenter' })">{{ $t('sys.signIn') }}</span>
-            <span class="cursor-pointer">{{ $t('sys.news') }}</span>
+            <span class="cursor-pointer" @click="router.push({ path: '/recruit/personal/message' })">{{ $t('sys.news') }}</span>
           </div>
         </div>
         

+ 2 - 1
src/locales/en.js

@@ -160,7 +160,8 @@ export default {
     positionInformation: 'Basic information of the position',
     requirementDesc: 'We will accurately recommend suitable talents to you based on the following conditions. Please fill in as much detail as possible',
     talentSearch: 'Talent search',
-    refreshTime: 'Refresh time'
+    refreshTime: 'Refresh time',
+    viewTime: 'View time'
   },
   enterprise: {
     companyInfo: 'Company Information',

+ 2 - 1
src/locales/zh-CN.js

@@ -160,7 +160,8 @@ export default {
     positionInformation: '职位基本信息',
     requirementDesc: '我们将通过以下条件,为您精确推荐合适的人才,请尽量详细填写',
     talentSearch: '人才搜索',
-    refreshTime: '刷新时间'
+    refreshTime: '刷新时间',
+    viewTime: '查看时间:'
   },
   enterprise: {
     companyInfo: '公司信息',

+ 17 - 0
src/router/modules/components/recruit/personal.js

@@ -187,5 +187,22 @@ const personal = [
       }
     ]
   },
+  {
+    path: '/recruit/personal/message',
+    component: Layout,
+    name: 'message',
+    meta: {
+      title: '消息'
+    },
+    children: [
+      {
+        path: '/recruit/personal/message',
+        component: () => import('@/views/recruit/personal/message/index'),
+        meta: {
+          title: '消息'
+        }
+      }
+    ]
+  },
 ]
 export default personal

+ 25 - 1
src/views/recruit/enterprise/positionManagement/components/baseInfo.vue

@@ -7,6 +7,11 @@
           众聘岗位规则说明
         </span>
       </template>
+      <template #ratio>
+        <div class="color-666 mb-3">
+          众聘岗位分配比例:平台占比{{ ratio.headhuntRate }}%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 推荐人占比{{ ratio.recommendRate }}%&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 投递人占比{{ ratio.cvRate }}%
+        </div>
+      </template>
       <template #positionId="{ item }">
         <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs">
           <template v-slot:activator="{  props }">
@@ -35,12 +40,14 @@ import { reactive, ref, defineExpose, watch } from 'vue'
 import textUI from '@/components/FormUI/TextInput'
 import jobTypeCard from '@/components/jobTypeCard'
 import RulePage from './rule.vue'
+import { getPublicRatio } from '@/api/recruit/enterprise/position'
 
 const props = defineProps({
   itemData: Object
 })
 
 const show = ref(false)
+const ratio = ref({})
 
 const getValue = (key) => {
   return items.value.options.find(e => e.key === key)
@@ -84,6 +91,10 @@ const handleChangePublic = (val) => {
   })
 }
 
+const handleBlur = (item, val) => {
+  console.log(item, val, 'blur')
+}
+
 const formPageRef = ref()
 let query = reactive({})
 
@@ -143,7 +154,8 @@ const items = ref({
           if (value >= 1) return true
           return '赏金金额不得小于1'
         }
-      ]
+      ],
+      blur: handleBlur
     },
     {
       type: 'number',
@@ -165,6 +177,11 @@ const items = ref({
         }
       ]
     },
+    {
+      slotName: 'ratio',
+      noParam: true,
+      hide: false
+    },
     {
       type: 'text',
       key: 'name',
@@ -233,6 +250,13 @@ const items = ref({
   ]
 })
 
+// 获取众聘分配比例
+const getRatio = async () => {
+  const data = await getPublicRatio()
+  ratio.value = data
+}
+getRatio()
+
 // 编辑回显
 watch(
   () => props.itemData,

+ 66 - 79
src/views/recruit/personal/PersonalCenter/components/seenMe.vue

@@ -1,32 +1,30 @@
 <template>
   <div>
     <div v-if="items.length" class="mt-3">
-      <div class="sub-li mb-3" v-for="item in items" :key="item.id" @mouseenter="item.active = true" @mouseleave="item.active = false">
-        <div class="company-info-top" @click="handleClickEnterprise(item, 'briefIntroduction')">
-          <div class="company-info">
-            <div class="float-left mr-5">
-              <v-img :src="item.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :alt="item.enterprise.anotherName" :width="40" style="height: 40px;border-radius: 4px;"/>
+      <div class="positionItem" v-for="(item, index) in items" :key="index" @mouseenter="item.active = true" @mouseleave="item.active = false">
+        <div class="position-and-company">
+          <div class="position">
+            <div class="float-left">
+              <v-img :src="item?.contact.avatar || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :width="40" style="height: 40px;border-radius: 4px;"/>
             </div>
-            <h3 :class="{'default-active': item.active }">{{ item.enterprise.anotherName }}</h3>
-            <p>{{ item.enterprise.financingName }}<span class="mx-2">|</span>{{ item.enterprise.industryName }}<span class="mx-2">|</span>{{ item.enterprise.scaleName }}</p>
-          </div>
-        </div>
-        <div class="company-info-bottom">
-          <div class="chipBox">
-            <div class="d-inline-block" v-for="(val, i) in item.enterprise.welfareList" :key="i">
-              <span>{{ val }}</span>
-              <span class="mx-1" v-if="i !== item.enterprise.welfareList.length - 1">|</span>
+            <div class="company-info">
+              <h3 :class="{'default-active': item.active }" class="title1">{{ item.contact.name }}</h3>
+              <p class="mt-2">{{ item?.post?.nameCn }}</p>
             </div>
           </div>
-          <div class="footer-right">
-            <v-avatar size="x-small" :image="item.contact.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
-            <span class="mx-2 textColor666">
-              {{ item.contact.name }}
-              <span v-if="item?.contact?.name && item?.post?.nameCn">|</span>
-              {{ item?.post?.nameCn }}
-            </span>
+          <div class="company" @click="handleEnterprise(item)">
+            <div class="float-left">
+              <v-img :src="item?.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :alt="item.enterprise.anotherName" :width="40" style="height: 40px;border-radius: 4px;"/>
+            </div>
+            <div class="company-info">
+              <h3 :class="{'default-active': item.active }" class="title1 cursor-pointer">{{ item.enterprise.anotherName }}</h3>
+              <p class="mt-2">{{ item.enterprise.financingName }}<span class="mx-2">|</span>{{ item.enterprise.scaleName }}<span class="mx-2">|</span>{{ item.enterprise.industryName }}</p>
+            </div>
           </div>
         </div>
+        <div class="footer">
+          {{ $t('position.viewTime') + timesTampChange(item.updateTime)  }}
+        </div>
       </div>
       <CtPagination
         :total="total"
@@ -45,6 +43,7 @@ defineOptions({ name: 'position-seen-me' })
 import { ref } from 'vue'
 import { getInterestedMePage } from '@/api/recruit/personal/personalCenter'
 import { dealDictObjData } from '@/utils/position'
+import { timesTampChange } from '@/utils/date'
 import Empty from '@/components/Empty'
 
 const total = ref(0)
@@ -65,87 +64,75 @@ const getData = async () => {
 }
 getData()
 
-const handleClickEnterprise = (item, key) => {
-  window.open(`/recruit/personal/company/details/${item.enterprise.id}?key=${key}`)
+const handleChangePage = (e) => {
+  page.value.pageNo = e
+  getData()
+}
+
+const handleEnterprise = (item) => {
+  const id = item.enterprise.id
+  if (!id) return
+  window.open(`/recruit/personal/company/details/${id}?key=briefIntroduction`)
 }
 
 </script>
 
 <style scoped lang="scss">
-.sub-li {
+.title1 {
+  color: var(--color-666);
   position: relative;
-  height: 130px;
-  border-radius: 12px;
-  padding: 0;
+  max-width: 200px;
+  margin-right: 8px;
   overflow: hidden;
-  transition: all .2s linear;
-  background-color: #fff;
-  cursor: pointer;
-  &:nth-child(4n) {
-    margin-right: 0;
-  }
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  font-size: 16px;
   &:hover {
-    box-shadow: 0 16px 40px 0 rgba(153, 153, 153, .3);
+    color: var(--v-primary-base);
   }
 }
-.textColor666 { 
-  color: var(--color-666);
-  font-size: 14px;
+.company {
+  flex: 1;
+  display: flex;
+  justify-content: end;
 }
 .company-info {
   float: left;
   margin-left: 16px;
-  width: 282px;
-}
-.company-info-top {
-  display: flex;
-  height: 76px;
-  padding: 16px 20px;
-  overflow: hidden;
-  justify-content: space-between;
-}
-.company-info h3 {
-  height: 22px;
-  font-size: 16px;
-  font-weight: 400;
-  color: var(--color-222);
-  line-height: 22px;
-  margin: 0 0 4px 0;
-  padding: 0;
-  max-width: 100%;
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
 }
 .company-info p {
   height: 18px;
   font-size: 13px;
   font-weight: 400;
   color: var(--color-999);
-  line-height: 18px;
 }
-.company-info-bottom {
-  display: flex;
-  width: 100%;
-  padding: 16px 20px;
-  justify-content: space-between;
-  background-color: #f8fcfb;
-  .chipBox {
-    width: 70%;
-    min-width: 70%;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    font-size: 13px; 
-    color: var(--color-999);
+.textColor666 { color: var(--color-666); }
+.positionItem {
+  margin-bottom: 12px;
+  border-radius: 12px;
+  padding: 0;
+  overflow: hidden;
+  transition: all .2s linear;
+  background-color: #fff;
+  &:hover {
+    box-shadow: 0 16px 40px 0 rgba(153, 153, 153, .3);
   }
-  .position {
-    color: var(--color-666);
-    font-size: 14px;
-    cursor: pointer;
-    &:hover {
-      color: var(--v-primary-base);
+  .position-and-company {
+    display: flex;
+    padding: 16px 20px;
+    width: 100%;
+    .position {
+      width: 484px;
+      padding-right: 12px;
     }
   }
 }
+.footer {
+  height: 36px;
+  line-height: 36px;
+  padding: 0px 20px;
+  font-size: 14px;
+  color: var(--color-999);
+  background: linear-gradient(90deg, #f5fcfc 0, #fcfbfa 100%);
+}
 </style>

+ 252 - 0
src/views/recruit/personal/message/components/chatting.vue

@@ -0,0 +1,252 @@
+<template>
+  <div class="chatting">
+    <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="septal-line"></span>
+          <span>负责人</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>
+    </div>
+    <div class="chat-record mt-3">
+      <div class="message-box" @scroll="handleScroll" ref="chatRef">
+        <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 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>
+        </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="color-ccc font-size-14 mr-5">按Enter键发送</div>
+          <v-btn color="primary" size="small" :disabled="!inputVal">发送</v-btn>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'message-chatting'})
+import { ref, nextTick, onMounted } from 'vue'
+
+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 = () => {
+  const chat = chatRef.value
+  if (chat) {
+    nextTick(function () {
+      chat.scrollTop = chat.scrollHeight
+    })
+  }
+}
+
+onMounted(() => {
+  nextTick(() => {
+    scrollBottom()
+  })
+})
+
+// const pullDown = async () => {
+//   if (messages.value.length == 0) {
+//     return
+//   }
+//   const firstMsg = messages.value[0]
+//   if (firstMsg.messageSeq == 1) {
+//     pulldownFinished.value = true
+//     return
+//   }
+//   const limit = 15
+//   const msgs = await WKSDK.shared().chatManager.syncMessages(to.value, {
+//     limit: limit,
+//     startMessageSeq: firstMsg.messageSeq - 1,
+//     endMessageSeq: 0,
+//     pullMode: PullMode.Down,
+//   })
+//   if (msgs.length < limit) {
+//     pulldownFinished.value = true;
+//   }
+//   if (msgs && msgs.length > 0) {
+//     msgs.reverse().forEach((m) => {
+//       messages.value.unshift(m)
+//     })
+//   }
+//   nextTick(function () {
+//     const chat = chatRef.value
+//     const firstMsgEl = document.getElementById(firstMsg.clientMsgNo)
+//     if (firstMsgEl) {
+//       chat.scrollTop = firstMsgEl.offsetTop
+//     }
+//   })
+// }
+
+const handleScroll = (e) => {
+  const targetScrollTop = e.target.scrollTop;
+  if (targetScrollTop <= 250) {
+    // 下拉
+    if (pullDowning.value || pulldownFinished.value) {
+      return
+    }
+    console.log('下拉')
+    pullDowning.value = true
+    // pullDown().then(() => {
+    //   pullDowning.value = false
+    // }).catch(() => {
+    //   pullDowning.value = false
+    // })
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.chatting {
+  position: relative;
+  height: 100%;
+}
+.top-info {
+  height: 104px;
+  .user-info {
+    position: relative;
+    height: 48px;
+    justify-content: space-between;
+    padding: 0 30px;
+    span {
+      font-size: 14px;
+      font-weight: 400;
+      color: var(--color-666);
+      line-height: 22px;
+    }
+    .name {
+      line-height: 25px;
+      margin-right: 20px;
+    }
+  }
+  .position-content {
+    position: relative;
+    padding: 16px;
+    border-radius: 12px;
+    margin: auto;
+    width: 760px;
+    background: linear-gradient(90deg, #f5fcfc, #fcfbfa);
+    .salary {
+      font-size: 20px;
+      font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
+      color: var(--v-error-base);
+      line-height: 24px;
+    }
+  }
+}
+.chat-record {
+  
+  padding-bottom: 30px;
+  .message-box {
+    height: 525px;
+    padding: 0 30px 20px;
+    overflow-y: auto;
+    .time-box {
+      user-select: none;
+      position: relative;
+      top: 8px;
+      margin: 20px 0;
+      max-height: 20px;
+      text-align: center;
+      font-weight: 400;
+      font-size: 12px;
+      color: var(--color-time-divider);
+    }
+    .message-view_item {
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      margin: 8px 0;
+      position: relative;
+      .message-text {
+        background-color: #f0f2f5;
+        border-radius: 6px;
+        max-width: 85%;
+        padding: 10px;
+      }
+    }
+    .is-self {
+      flex-direction: row-reverse;
+      display: flex;
+      .message-text {
+        margin-right: 10px;
+      }
+    }
+    .is-other {
+      .message-text {
+        margin-left: 10px;
+      }
+    }
+  }
+}
+.bottom-info {
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+  height: 160px;
+}
+input {
+  outline: none;
+  width: 748px;
+  height: 60px; 
+  max-width: 748px; 
+  overflow: auto;
+  white-space: pre-wrap;
+  &:focus {
+    border: none;
+  }
+}
+
+/* 滚动条样式 */
+::-webkit-scrollbar {
+  -webkit-appearance: none;
+  width: 0px;
+  height: 0px;
+}
+/* 滚动条内的轨道 */
+::-webkit-scrollbar-track {
+  background: rgba(0, 0, 0, 0.1);
+  border-radius: 0;
+}
+/* 滚动条内的滑块 */
+::-webkit-scrollbar-thumb {
+  cursor: pointer;
+  border-radius: 5px;
+  background: rgba(0, 0, 0, 0.15);
+  transition: color 0.2s ease;
+}
+</style>

+ 157 - 0
src/views/recruit/personal/message/index.vue

@@ -0,0 +1,157 @@
+<template>
+  <div class="default-width message pa-3">
+    <div class="message-left">
+      <div class="message-left-search d-flex align-center justify-center">
+        <TextInput v-model="searchInputVal" :item="textItem" @appendInnerClick="handleSearch" @enter="handleSearch"></TextInput>
+      </div>
+      <div class="message-chat-box mt-5">
+        <div v-if="chatList.length">
+          <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>
+</template>
+
+<script setup>
+defineOptions({ name: 'personal-message-index'})
+import { ref } from 'vue'
+import Chatting from './components/chatting.vue'
+// import { getChatKey } from '@/api/common'
+
+// const getKey = async () => {
+//   const data = await getChatKey({ userId: '1' })
+//   console.log(data, 'key')
+// }
+// getKey()
+
+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>