Sfoglia il codice sorgente

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

lifanagju_citu 10 mesi fa
parent
commit
22f5841eff
30 ha cambiato i file con 851 aggiunte e 224 eliminazioni
  1. 1 2
      src/components/Enterprise/details.vue
  2. 98 102
      src/hooks/web/useIM.js
  3. 12 4
      src/layout/company/navBar.vue
  4. 14 5
      src/layout/message.vue
  5. 0 39
      src/layout/personal/message.vue
  6. 13 4
      src/layout/personal/navBar.vue
  7. 42 25
      src/router/modules/components/recruit/enterprise.js
  8. 4 4
      src/store/user.js
  9. 20 10
      src/views/recruit/components/message/components/chatting.vue
  10. 6 1
      src/views/recruit/components/message/index.vue
  11. 41 0
      src/views/recruit/enterprise/elite/components/commonStyle.vue
  12. 102 0
      src/views/recruit/enterprise/elite/components/invite.vue
  13. 145 0
      src/views/recruit/enterprise/elite/components/screen.vue
  14. 183 0
      src/views/recruit/enterprise/elite/components/table.vue
  15. 145 0
      src/views/recruit/enterprise/elite/index.vue
  16. 1 1
      src/views/recruit/enterprise/interview/components/item.vue
  17. 1 1
      src/views/recruit/enterprise/interview/index.vue
  18. 0 3
      src/views/recruit/enterprise/personnelManagement/components/table.vue
  19. 1 1
      src/views/recruit/enterprise/positionManagement/components/item.vue
  20. 2 2
      src/views/recruit/enterprise/talentPool/components/details/baseInfo.vue
  21. 2 2
      src/views/recruit/enterprise/talentPool/components/details/educationExp.vue
  22. 2 2
      src/views/recruit/enterprise/talentPool/components/details/projectExperience.vue
  23. 2 2
      src/views/recruit/enterprise/talentPool/components/details/trainingExperience.vue
  24. 2 2
      src/views/recruit/enterprise/talentPool/components/details/workExperience.vue
  25. 1 1
      src/views/recruit/personal/PersonalCenter/components/interview/item.vue
  26. 1 1
      src/views/recruit/personal/remuse/components/basicInfo.vue
  27. 3 3
      src/views/recruit/personal/remuse/components/educationExp.vue
  28. 2 2
      src/views/recruit/personal/remuse/components/projectExperience.vue
  29. 2 2
      src/views/recruit/personal/remuse/components/trainingExperience.vue
  30. 3 3
      src/views/recruit/personal/remuse/components/workExperience.vue

+ 1 - 2
src/components/Enterprise/details.vue

@@ -107,8 +107,7 @@ const getDetails = async () => {
   if (!props.id) return
   const data = await getEnterpriseDetails({ id: props.id })
   // 成立日期
-  const time = timesTampChange(data.business.establishmentTime)
-  data.business.establishmentTime = time.slice(0, 10)
+  data.business.establishmentTime = timesTampChange(data.business.establishmentTime, 'Y-M-D')
 
   info.value = { ...data, ...dealDictObjData({}, data.enterprise) }
   getCollectionStatus(props.id)

+ 98 - 102
src/hooks/web/useIM.js

@@ -40,65 +40,61 @@ export function useDataSource () {
   const userStore = useUserStore() 
   const loginType = useLoginType()
   // 最近会话数据源
-  if (!WKSDK.shared().config.provider.syncConversationsCallback) {
-    WKSDK.shared().config.provider.syncConversationsCallback  = async () => {
-      const query = {
-        msg_count: 100
-      }
-      if (loginType.loginType === 'enterprise') {
-        Object.assign(query, { enterpriseId: userStore.baseInfo.enterpriseId })
-      }
-      const resultConversations = []
-      const resp = await getConversationSync(query)
-      // console.log(resp)
-      const conversationList = resp
-      if (conversationList) {
-        conversationList.forEach(conversation => {
-          conversation.channel = new Channel(conversation.channel_id, conversation.channel_type)
-          conversation.unread = +(conversation.unread || 0)
-          resultConversations.push(conversation)
-        })
-      }
-      return resultConversations
+  WKSDK.shared().config.provider.syncConversationsCallback  = async () => {
+    const query = {
+      msg_count: 100
     }
+    if (loginType.loginType === 'enterprise') {
+      Object.assign(query, { enterpriseId: userStore.baseInfo.enterpriseId })
+    }
+    const resultConversations = []
+    const resp = await getConversationSync(query)
+    // console.log(resp)
+    const conversationList = resp
+    if (conversationList) {
+      conversationList.forEach(conversation => {
+        conversation.channel = new Channel(conversation.channel_id, conversation.channel_type)
+        conversation.unread = +(conversation.unread || 0)
+        resultConversations.push(conversation)
+      })
+    }
+    return resultConversations
   }
-  if (!WKSDK.shared().config.provider.syncMessagesCallback) {
     // 同步频道消息数据源
-    WKSDK.shared().config.provider.syncMessagesCallback = async function(channel) {
-      // 后端提供的获取频道消息列表的接口数据 然后构建成 Message对象数组返回
-      let resultMessages  = new Array()
-      const {
-        startMessageSeq: start_message_seq,
-        endMessageSeq: end_message_seq,
-        limit,
-        pullMode: pull_mode
-      } = HISTORY_QUERY
-      const query = {
-        channel_id: channel.channelID,
-        channel_type: channel.channelType,
-        start_message_seq,
-        end_message_seq,
-        limit,
-        pull_mode,
-      }
-      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);
-              // msg.channel = new Channel(msg.channel_id, msg.channel_type)
-              msg.payload = JSON.parse(Base64.decode(msg.payload))
-              resultMessages.push(msg);
-          });
-      }
-      const more = resp.more === 1
-      return {
-        more,
-        resultMessages
-      }
+  WKSDK.shared().config.provider.syncMessagesCallback = async function(channel) {
+    // 后端提供的获取频道消息列表的接口数据 然后构建成 Message对象数组返回
+    let resultMessages  = new Array()
+    const {
+      startMessageSeq: start_message_seq,
+      endMessageSeq: end_message_seq,
+      limit,
+      pullMode: pull_mode
+    } = HISTORY_QUERY
+    const query = {
+      channel_id: channel.channelID,
+      channel_type: channel.channelType,
+      start_message_seq,
+      end_message_seq,
+      limit,
+      pull_mode,
+    }
+    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);
+            // msg.channel = new Channel(msg.channel_id, msg.channel_type)
+            msg.payload = JSON.parse(Base64.decode(msg.payload))
+            resultMessages.push(msg);
+        });
+    }
+    const more = resp.more === 1
+    return {
+      more,
+      resultMessages
     }
   }
 }
@@ -193,30 +189,30 @@ async function getKey () {
   }
 }
 
-export const useIM = async () => {
+export const useIM = () => {
+  useDataSource()
+  const key = ref(0)
   const IM = useIMStore()
   const unreadCount = ref(0)
   const connected = ref(0)
-  // 通过自身userId和企业id获取token和uid
-  const { uid, wsUrl, token } = await getKey()
-  IM.setUid(uid)
-  // 单机模式可以直接设置地址
-  WKSDK.shared().config.addr = 'ws://' + wsUrl // 默认端口为5200
-  // 认证信息
-  WKSDK.shared().config.uid = uid // 用户uid(需要在悟空通讯端注册过)
-  WKSDK.shared().config.token = token // 用户token (需要在悟空通讯端注册过)
   
-  // onMounted(() => {
-    // console.log('1')
+  onMounted( async () => {
+    
+    // 通过自身userId和企业id获取token和uid
+    const { uid, wsUrl, token } = await getKey()
+    IM.setUid(uid)
+    // 单机模式可以直接设置地址
+    WKSDK.shared().config.addr = 'ws://' + wsUrl // 默认端口为5200
+    // 认证信息
+    WKSDK.shared().config.uid = uid // 用户uid(需要在悟空通讯端注册过)
+    WKSDK.shared().config.token = token // 用户token (需要在悟空通讯端注册过)
     // 连接状态监听
     WKSDK.shared().connectManager.addConnectStatusListener(connectStatusListener)
-    // console.log('2')
     // 常规消息监听
     WKSDK.shared().chatManager.addMessageListener(messageListen)
     // 连接
-    // console.log('连接')
     WKSDK.shared().connectManager.connect()
-  // })
+  })
   onUnmounted(() => {
     WKSDK.shared().connectManager.removeConnectStatusListener(connectStatusListener)
     // 常规消息监听移除
@@ -224,19 +220,33 @@ export const useIM = async () => {
     // 连接状态监听移除
     WKSDK.shared().connectManager.disconnect()
   })
-
+  
   async function messageListen (message) {
     console.log('收到消息', message)
     IM.setFromChannel(message.channel.channelID)
-    const count = WKSDK.shared().conversationManager.getAllUnreadCount()
-    IM.setNewMsg(count)
-    unreadCount.value = count
-    // console.log('未读消息数', count)
-    // 创建通道
+    setUnreadCount()
   }
+
   async function connectStatusListener (status) {
     // console.log('连接状态', status === ConnectStatus.Connected)
+    // 连接成功 获取点击数
     connected.value = status === ConnectStatus.Connected
+    if (connected.value) {
+      await syncConversation()
+      setUnreadCount()
+      // setTimeout(() => {
+      //   setUnreadCount()
+      //   console.log('连接成功, 获取总数')
+      // }, 0)
+    }
+  }
+
+  function setUnreadCount () {
+    const count = WKSDK.shared().conversationManager.getAllUnreadCount()
+    key.value++
+    IM.setNewMsg(key.value)
+    unreadCount.value = count
+    console.log('未读消息总数', count)
   }
 
   return {
@@ -246,17 +256,18 @@ export const useIM = async () => {
 }
 
 export function initConnect (callback = () => {}) {
+  useDataSource()
   const IM = useIMStore()
   const conversationList = ref([])
   const messageItems = ref([])
 
   watch(
     () => IM.newMsg,
-    () => {
+    async () => {
       // 未读消息变化
-      syncConversation()
+      const res = await syncConversation()
+      conversationList.value = res
       // 拉取最新消息 查看是否是自己的数据
-
     },
     {
       deep: true,
@@ -282,35 +293,14 @@ export function initConnect (callback = () => {}) {
       // 发送成功
       console.log('发送成功')
       // 添加一组成功数据
-      callback()
+      callback(true)
     } else {
       // 发送失败
       console.log('发送失败')
       // 添加一组失败数据
-      // callback()
+      callback(false)
     }
   }
-
-  // 常规消息监听
-  // async function messageListen (message) {
-  //   console.log('收到消息', message)
-  //   syncConversation()
-  // }
-  // 同步最近会话
-  async function syncConversation () {
-    const res = await WKSDK.shared().conversationManager.sync()
-    conversationList.value = res
-    return res
-    // return new Promise((resolve, reject) => {
-    //   WKSDK.shared().conversationManager.sync().then(res => {
-    //     conversationList.value = res
-    //     resolve(res)
-    //   }).catch(error => {
-    //     reject(error)
-    //   })
-    // })
-  }
-
   return {
     // connected,
     conversationList,
@@ -319,6 +309,12 @@ export function initConnect (callback = () => {}) {
   }
 }
 
+// 同步最近会话
+async function syncConversation () {
+  const res = await WKSDK.shared().conversationManager.sync()
+  return res
+}
+
 // export async function getRecentMessages (channel) {
 //   const { resultMessages, more } = await WKSDK.shared().chatManager.syncMessages(channel, HISTORY_QUERY)
 //   // console.log(res)

+ 12 - 4
src/layout/company/navBar.vue

@@ -27,7 +27,7 @@
           <div class="line"></div>
           
           <!-- 头像用户名 -->
-          <div class="d-flex align-center" v-if="getToken()">
+          <div class="d-flex align-center" v-if="showBall">
             <v-menu open-on-hover>
               <template v-slot:activator="{ props }">
                 <div class="d-flex ml-5 pl-2 align-center cursor-pointer" v-bind="props">
@@ -75,7 +75,7 @@
           </v-menu> -->
 
           <!-- <v-btn size="small" icon="mdi-bell-outline" @click="router.push('/recruit/enterprise/communication')"></v-btn> -->
-          <MessageNotification></MessageNotification>
+          <MessageNotification path="/recruit/enterprise/communication"></MessageNotification>
         </div>
       </div>
     </v-toolbar>
@@ -92,13 +92,13 @@ import {
   getUserBindEnterpriseList,
   getUserRegisterEnterpriseApply
 } from '@/api/personal/user'
-import { computed, ref } from 'vue'
+import { computed, ref, onMounted } from 'vue'
 import { getToken } from '@/utils/auth'
 import { useUserStore } from '@/store/user'; const userStore = useUserStore()
 // import { useLocaleStore } from '@/store/locale'; const localeStore = useLocaleStore()
 import { useRouter } from 'vue-router'; const router = useRouter()
 import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
-import MessageNotification from './message.vue'
+import MessageNotification from '../message.vue'
 defineOptions({ name: 'personal-navbar' })
 
 defineProps({
@@ -108,6 +108,14 @@ defineProps({
   }
 })
 
+const showBall = ref(false)
+
+onMounted(() => {
+  if (getToken()) {
+    showBall.value = true
+  }
+})
+
 const handleLogoClick = () => { router.push({ path: '/recruit/enterprise'}) }
 
 const enterpriseClick = (tabKey = 1) => {

+ 14 - 5
src/layout/company/message.vue → src/layout/message.vue

@@ -3,26 +3,35 @@
     color="error"
     :content="unreadCount"
     :model-value="unreadCount > 0"
+    offset-y="10"
   >
-    <v-btn class="ml-1" size="small" icon="mdi-bell-outline" @click="router.push({ path: '/recruit/enterprise/communication' })"></v-btn>
+    <v-btn class="ml-1" size="small" icon="mdi-bell-outline" @click="router.push(path)"></v-btn>
   </v-badge>
 </template>
 
 <script setup>
-defineOptions({ name: 'personal-message' })
+defineOptions({ name: 'layout-message' })
 import { watch } from 'vue'
-import { useIM, useDataSource } from '@/hooks/web/useIM'
+import { useIM } from '@/hooks/web/useIM'
 import { useIMStore } from '@/store/im'
 import { useRouter } from 'vue-router'
+
+defineProps({
+  path: {
+    type: String,
+    default: '/recruit/personal/message'
+  }
+})
+
 const router = useRouter()
 
 const _im = useIMStore()
-useDataSource()
+// useDataSource()
 
 const {
   unreadCount,
   connected
-} = await useIM()
+} = useIM()
 
 watch(
   () => connected.value,

+ 0 - 39
src/layout/personal/message.vue

@@ -1,39 +0,0 @@
-<template>
-  <v-badge
-    color="error"
-    :content="unreadCount"
-    :model-value="unreadCount > 0"
-  >
-    <v-btn class="ml-1" size="small" icon="mdi-bell-outline" @click="router.push({ path: '/recruit/personal/message' })"></v-btn>
-  </v-badge>
-</template>
-
-<script setup>
-defineOptions({ name: 'personal-message' })
-import { watch } from 'vue'
-import { useIM, useDataSource } from '@/hooks/web/useIM'
-import { useIMStore } from '@/store/im'
-import { useRouter } from 'vue-router'
-const router = useRouter()
-
-const _im = useIMStore()
-useDataSource()
-
-const {
-  unreadCount,
-  connected
-} = await useIM()
-
-watch(
-  () => connected.value,
-  (val) => {
-    console.log('connected变化', val)
-    _im.setConnected(val)
-
-  }
-)
-</script>
-
-<style lang="scss" scoped>
-
-</style>

+ 13 - 4
src/layout/personal/navBar.vue

@@ -83,7 +83,7 @@
               </v-list-item>
             </v-list>
           </v-menu> -->
-          <div class="d-flex align-center" v-if="getToken()">
+          <div class="d-flex align-center" v-if="showBall">
             <!-- <span class="cursor-pointer mx-5" @click="router.push({ path: '/recruit/personal/TaskCenter' })">{{ $t('sys.signIn') }}</span> -->
             <!-- <span class="cursor-pointer" @click="router.push({ path: '/recruit/personal/message' })">{{ $t('sys.news') }}</span> -->
             <!-- <v-badge
@@ -93,7 +93,7 @@
             >
               <v-btn class="ml-1" size="small" icon="mdi-bell-outline" @click="router.push({ path: '/recruit/personal/message' })"></v-btn>
             </v-badge> -->
-            <message-notification v-if="getToken()"></message-notification>
+            <message-notification v-if="showBall" path="/recruit/personal/message"></message-notification>
             
           </div>
         </div>
@@ -110,7 +110,7 @@
 </template>
 
 <script setup>
-import { ref } from 'vue'
+import { onMounted, ref } from 'vue'
 import { getToken } from '@/utils/auth'
 import { useUserStore } from '@/store/user'
 // import { useLocaleStore } from '@/store/locale'
@@ -118,7 +118,8 @@ import { useI18n } from '@/hooks/web/useI18n'
 import CtDialog from '@/components/CtDialog'
 import { useRouter } from 'vue-router'; const router = useRouter()
 import { getUserBindEnterpriseList, getUserRegisterEnterpriseApply } from '@/api/personal/user'
-import MessageNotification from './message.vue'
+import MessageNotification from '../message.vue'
+
 // import { useIMStore } from '@/store/im'
 defineOptions({ name: 'personal-navbar' })
 
@@ -129,6 +130,14 @@ defineProps({
   }
 })
 
+const showBall = ref(false)
+
+onMounted(() => {
+  if (getToken()) {
+    showBall.value = true
+  }
+})
+
 // const useIM = useIMStore()
 
 const { t } = useI18n()

+ 42 - 25
src/router/modules/components/recruit/enterprise.js

@@ -140,9 +140,9 @@ const enterprise = [
     ]
   },
   {
-    path: '/recruit/enterprise/personnelManagement',
+    path: '/recruit/enterprise/elite',
     component: Layout,
-    name: 'personnelManagement',
+    name: 'eliteManagement',
     meta: {
       title: '精英管理',
       enName: 'Meritocracy',
@@ -150,31 +150,48 @@ const enterprise = [
     },
     children: [
       {
-        path: '/recruit/enterprise/personnelManagement',
+        path: '/recruit/enterprise/elite',
         show: true,
-        component: () => import('@/views/recruit/enterprise/personnelManagement/index.vue')
+        component: () => import('@/views/recruit/enterprise/elite/index.vue')
       }
     ]
   },
-  {
-    path: '/recruit/enterprise/publicRecruitmentManagement',
-    component: Layout,
-    redirect: '',
-    name: 'publicRecruitmentManagement',
-    meta: {
-      title: '众聘管理',
-      enName: 'Crowdsourcing management',
-      icon: 'mdi-calendar-blank-multiple'
-    },
-    children: [
-      {
-        path: '/recruit/enterprise/publicRecruitmentManagement/deliver',
-        meta: {
-          title: '投递管理',
-          enName: 'Delivery Management'
-        },
-        component: () => import('@/views/recruit/enterprise/publicRecruitmentManagement/deliver')
-      },
+  // {
+  //   path: '/recruit/enterprise/personnelManagement',
+  //   component: Layout,
+  //   name: 'personnelManagement',
+  //   meta: {
+  //     title: '精英管理',
+  //     enName: 'Meritocracy',
+  //     icon: 'mdi-account-settings-outline'
+  //   },
+  //   children: [
+  //     {
+  //       path: '/recruit/enterprise/personnelManagement',
+  //       show: true,
+  //       component: () => import('@/views/recruit/enterprise/personnelManagement/index.vue')
+  //     }
+  //   ]
+  // },
+  // {
+    // path: '/recruit/enterprise/publicRecruitmentManagement',
+    // component: Layout,
+    // redirect: '',
+    // name: 'publicRecruitmentManagement',
+    // meta: {
+    //   title: '众聘管理',
+    //   enName: 'Crowdsourcing management',
+    //   icon: 'mdi-calendar-blank-multiple'
+    // },
+    // children: [
+      // {
+      //   path: '/recruit/enterprise/publicRecruitmentManagement/deliver',
+      //   meta: {
+      //     title: '投递管理',
+      //     enName: 'Delivery Management'
+      //   },
+      //   component: () => import('@/views/recruit/enterprise/publicRecruitmentManagement/deliver')
+      // },
       // {
       //   path: '/recruit/enterprise/publicRecruitmentManagement/commission',
       //   meta: {
@@ -183,8 +200,8 @@ const enterprise = [
       //   },
       //   component: () => import('@/views/recruit/enterprise/publicRecruitmentManagement/commission')
       // }
-    ]
-  },
+    // ]
+  // },
   {
     path: '/recruit/enterprise/informationManagement',
     component: Layout,

+ 4 - 4
src/store/user.js

@@ -37,7 +37,7 @@ export const useUserStore = defineStore('user',
       handleSmsLogin (data) {
         return new Promise((resolve, reject) => {
           const loginApi = data.loginType === 330 ? smsLoginOfEnterprise : smsLogin
-          loginApi(data).then(res => {
+          loginApi(data).then(async res => {
             // this.loginType = data.loginType
             setToken(res.accessToken)
             setRefreshToken(res.refreshToken)
@@ -45,7 +45,7 @@ export const useUserStore = defineStore('user',
             localStorage.setItem('accountInfo', JSON.stringify(res))
             localStorage.setItem('expiresTime', res.expiresTime) // token过期时间
             localStorage.setItem('loginType', 'personal')
-            this.getUserInfos()
+            await this.getUserInfos()
             this.getUserBaseInfos()
             resolve()
           }).catch(err => { reject(err) })
@@ -100,8 +100,8 @@ export const useUserStore = defineStore('user',
       },
       // 字典对应中文
       async getFieldText (data) {
-        if (data.birthday && data.birthday !== 0) data.birthdayText = timesTampChange(data.birthday).slice(0, 10) // 出生日期
-        if (data.firstWorkTime && data.firstWorkTime !== 0) data.firstWorkTimeText = timesTampChange(data.firstWorkTime).slice(0, 10) // 首次工作时间
+        if (data.birthday && data.birthday !== 0) data.birthdayText = timesTampChange(data.birthday, 'Y-M-D') // 出生日期
+        if (data.firstWorkTime && data.firstWorkTime !== 0) data.firstWorkTimeText = timesTampChange(data.firstWorkTime, 'Y-M-D') // 首次工作时间
         if (data.areaId && data.areaId !== 0) await getBaseInfoDictOfName(0, data, data.areaId, 'areaName') // 现居住地text
         if (data.eduType && data.eduType !== 0) await getBaseInfoDictOfName(1, data, data.eduType, 'eduTypeText') // 学历
         if (data.expType && data.expType !== 0) await getBaseInfoDictOfName(2, data, data.expType, 'expTypeText') // 工作经验

+ 20 - 10
src/views/recruit/components/message/components/chatting.vue

@@ -60,7 +60,7 @@
                 ></v-img>
               </v-avatar>
             </div>
-            <div class="message-text">{{ val.payload.content }}</div>
+            <div class="message-text" :class="{ active: val.from_uid === IM.uid}">{{ val.payload.content }}</div>
           </div>
         </div>
       </div>
@@ -71,12 +71,13 @@
         <v-textarea
           v-model="inputVal"
           label="请输入消息"
-          placeholder="请输入消息"
+          placeholder="请输入消息 按Ctrl+Enter换行"
           hide-details
           no-resize
           bg-color="white"
           variant="plain"
           :disabled="Object.keys(info).length === 0"
+          @keydown="handleKeyDown"
         >
           <!-- @keydown.stop.prevent="handleKeyDown" -->
           <template #append-inner>
@@ -130,7 +131,7 @@ const loading = ref(false)
 const mAvatar = userStore.baseInfo?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'
 
 const chatRef = ref()
-const inputVal = ref()
+const inputVal = ref('')
 const pullDowning = ref(false) // 下拉中
 const pulldownFinished = ref(false) // 下拉完成
 
@@ -215,12 +216,18 @@ const changeOverlay = (val) => {
 const changeLoading = (val) => {
   loading.value = val
 }
-// const handleKeyDown = (event) => {
-//   if (event.keyCode === 13) {
-    
-//     console.log(event)
-//   } 
-// }
+const handleKeyDown = (event) => {
+  if (event.keyCode === 13) {
+    event.preventDefault()
+    if(event.ctrlKey) {
+      // 换行
+      inputVal.value += '\n'
+      return
+    }
+    // 发送
+    emits('handleSend', inputVal)
+  } 
+}
 const reset = () => {
   inputVal.value = ''
 }
@@ -295,7 +302,7 @@ defineExpose({
   .message-view_item {
     display: flex;
     flex-direction: row;
-    align-items: center;
+    align-items: flex-start;
     margin: 8px 0;
     position: relative;
     .message-text {
@@ -303,6 +310,9 @@ defineExpose({
       border-radius: 6px;
       max-width: 85%;
       padding: 10px;
+      &.active {
+        background: #d5e6e8;
+      }
     }
   }
   .is-self {

+ 6 - 1
src/views/recruit/components/message/index.vue

@@ -72,6 +72,7 @@ import { useRoute } from 'vue-router'
 import { getPositionDetails } from '@/api/position'
 import { getUserInfo } from '@/api/personal/user'
 import { useIMStore } from '@/store/im'
+import Snackbar from '@/plugins/snackbar'
 
 const chatRef = ref()
 
@@ -108,7 +109,11 @@ if (!IM) {
   console.log('IM is disconnected')
 }
 
-const { conversationList } = initConnect(async () => {
+const { conversationList } = initConnect(async (successful) => {
+  if (!successful) {
+    Snackbar.error('发送失败')
+    return
+  }
   // 发送成功
   const { list } = await getMoreMessages(1, channelItem.value)
   messageItems.value = list.value

+ 41 - 0
src/views/recruit/enterprise/elite/components/commonStyle.vue

@@ -0,0 +1,41 @@
+<template>
+  <v-menu 
+    open-on-hover 
+    :close-delay="1" 
+    :open-delay="0" 
+    v-bind="$attrs" 
+    location="bottom" 
+    max-height="400"
+    :close-on-content-click="closeOnContentClick"
+  >
+    <template v-slot:activator="{ isActive, props }">
+      <v-btn
+        class="mr-3 py-0 px-2"
+        density="comfortable"
+        :append-icon="isActive ? 'mdi mdi-menu-up' : 'mdi mdi-menu-down'"
+        color="primary" variant="tonal"
+        v-bind="props"
+      >
+        {{ defineProps.btnTitle }}
+      </v-btn>
+    </template>
+    <slot></slot>
+  </v-menu>
+</template>
+<script setup>
+
+defineOptions({name: 'conditionFilter-index-page'})
+const defineProps = defineProps({
+  btnTitle: {
+    type: String,
+    default: 'Text'
+  },
+  closeOnContentClick: {
+    type: Boolean,
+    default: true
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 102 - 0
src/views/recruit/enterprise/elite/components/invite.vue

@@ -0,0 +1,102 @@
+<template>
+  <CtForm ref="CtFormRef" :items="formItems" style="height: 420px;">
+    <template #time="{ item }">
+      <VueDatePicker 
+        v-model="item.value"
+        placeholder="面试时间 *"
+        class="mb-4"
+        model-type="timestamp"
+        :text-input="{ format: 'MM.dd.yyyy HH:mm' }" />
+    </template>
+  </CtForm>
+</template>
+
+<script setup>
+defineOptions({ name: 'formPage'})
+import { ref } from 'vue'
+
+const props = defineProps({
+  itemData: {
+    type: Object,
+    default: () => {}
+  }
+})
+
+const CtFormRef = ref()
+const formItems = ref({
+  options: [
+    {
+      slotName: 'time',
+      key: 'time',
+      value: null,
+      rules: [v => !!v || '请选择面试时间'],
+    },
+    {
+      type: 'text',
+      key: 'position',
+      value: '',
+      noParam: true,
+      disabled: true,
+      label: '面试岗位'
+    },
+    {
+      type: 'text',
+      key: 'address',
+      value: '',
+      label: '面试地点 *',
+      rules: [v => !!v || '请输入面试地点'],
+    },
+    {
+      type: 'text',
+      key: 'invitePhone',
+      value: null,
+      label: '联系电话 *',
+      outlined: true,
+      rules: [v => !!v || '请填写联系电话']
+    },
+    {
+      type: 'textarea',
+      key: 'remark',
+      value: '',
+      label: '备注事项',
+      counter: 140,
+      rules: [
+        value => {
+          if (value?.length <= 140) return true
+          return '请输入备注事项,最多140字'
+        }
+      ]
+    }
+  ]
+})
+
+if (Object.keys(props.itemData).length) {
+  const obj = formItems.value.options.find(e => e.key === 'position')
+  obj.value = `${props.itemData?.job?.name}${props.itemData?.job?.areaName ? '_' + props.itemData?.job?.areaName : ''} ${props.itemData?.job?.payFrom}-${props.itemData?.job?.payTo}/${props.itemData?.job?.payName}`
+  formItems.value.options.find(e => e.key === 'address').value = props.itemData.job?.address
+}
+
+const getQuery = () => {
+  const obj = {
+    type: 1,
+    jobId: props.itemData.job.id,
+    userId: props.itemData.userId,
+    latitude: props.itemData.job?.latitude,
+    longitude: props.itemData.job?.longitude
+  }
+  formItems.value.options.forEach(item => {
+    if (item.noParam) return
+    obj[item.key] = item.value
+  })
+  return obj
+}
+
+defineExpose({
+  CtFormRef,
+  getQuery
+})
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 145 - 0
src/views/recruit/enterprise/elite/components/screen.vue

@@ -0,0 +1,145 @@
+<template>
+  <div class="d-flex align-center">
+    <CommonStyle v-for="(val, i) in list" :key="i" :btnTitle="val.title">
+      <v-list>
+        <v-list-item
+          v-for="(item, index) in val.items"
+          :key="index"
+          :active="val.selected.includes(item.value)"
+          color="primary"
+          :value="item.value"
+          @click="handleClick(item, val)"
+        >
+          <v-list-item-title>{{ item.label }}</v-list-item-title>
+        </v-list-item>
+      </v-list>
+    </CommonStyle>
+    <div class="mr-5 d-flex align-center" v-if="props.tab === 0">
+      <v-radio-group v-model="selected" inline style="height: 28px;" @update:modelValue="handleChangeSelected">
+        <v-radio v-model="selected" label="新投递" value="0" color="primary" hide-details density="compact" class="mr-3"></v-radio>
+        <v-radio v-model="selected" label="已查看" value="1" color="primary" hide-details density="compact"></v-radio>
+      </v-radio-group>
+      <v-checkbox class="ml-3" v-model="bounty" label="赏金职位" color="primary" hide-details density="compact" @update:model-value="handleChangeBounty"></v-checkbox>
+    </div>
+    <span class="reset-text cursor-pointer ml-3" @click="handleReset">重置</span>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'screen-page'})
+import { ref, watch } from 'vue'
+import { getJobAdvertised } from '@/api/enterprise'
+import { getDict } from '@/hooks/web/useDictionaries'
+import { dealDictArrayData } from '@/utils/position'
+import CommonStyle from './commonStyle.vue'
+
+const emit = defineEmits(['search', 'reset', 'select', 'change'])
+const props = defineProps({
+  tab: Number
+})
+
+const selected = ref()
+const bounty = ref(false)
+
+const list = ref([
+  {
+    title: '应聘岗位',
+    defaultTitle: '应聘岗位',
+    key: 'jobId',
+    selected: [],
+    api: getJobAdvertised,
+    items: []
+  },
+  {
+    title: '学历',
+    defaultTitle: '学历',
+    key: 'eduType',
+    dictTypeName: 'menduner_education_type',
+    selected: [],
+    items: []
+  },
+  {
+    title: '工作经验',
+    defaultTitle: '工作经验',
+    key: 'expType',
+    dictTypeName: 'menduner_exp_type',
+    selected: [],
+    items: []
+  },
+  {
+    title: '求职状态',
+    defaultTitle: '求职状态',
+    key: 'jobStatus',
+    dictTypeName: 'menduner_job_status',
+    selected: [],
+    items: []
+  }
+])
+
+// 获取字典数据
+list.value.forEach(k => {
+  if (k.dictTypeName) {
+    getDict(k.dictTypeName).then(({ data }) => {
+      data = data?.length && data || []
+      k.items = data
+    })
+  }
+  if (k.api) {
+    k.api({ hire: false }).then(data => {
+      if (data.length) {
+        const list = dealDictArrayData([], data)
+        k.items = list.map(e => {
+          return { label: `${e.name}${e.areaName ? '_' + e.areaName : ''} ${e.payFrom}-${e.payTo}/${e.payName}`, value: e.id }
+        })
+      }
+    })
+  }
+})
+
+// 单击
+const handleClick = (item, val) => {
+  const obj = val.selected.includes(item.value)
+  val.selected = obj ? val.selected.filter(i => i !== item.value) : [item.value]
+  val.title = obj ? val.defaultTitle : item.label
+  emit('search', { key: val.key, value: obj ? '' : item.value })
+}
+
+// 重置
+const handleReset = () => {
+  list.value.map(e => {
+    e.selected = []
+    e.title = e.defaultTitle
+    return e
+  })
+  selected.value = ''
+  bounty.value = false
+  emit('reset')
+}
+
+// 新投递&已查看选择
+const handleChangeSelected = (e) => {
+  emit('select', e)
+}
+
+// 赏金职位
+const handleChangeBounty = (e) => {
+  emit('change', e)
+}
+
+watch(
+  () => props.tab,
+  () => {
+    handleReset()
+  }
+)
+</script>
+
+<style scoped lang="scss">
+.reset-text {
+  font-size: 14px;
+  color: var(--color-666);
+  &:hover {
+    color: var(--v-primary-base);
+  }
+}
+</style>

+ 183 - 0
src/views/recruit/enterprise/elite/components/table.vue

@@ -0,0 +1,183 @@
+<template>
+  <div>
+    <v-data-table
+      class="mt-3"
+      :items="items"
+      :headers="headers"
+      hover
+      :disable-sort="true"
+      height="60vh"
+      item-value="id"
+    >
+      <template #bottom></template>
+      <template v-slot:item.name="{ item }">
+        <div class="d-flex align-center cursor-pointer" @click="handleToPersonDetail(item)">
+          <v-badge
+            bordered
+            offset-y="6"
+            :color="badgeColor(item)"
+            :icon="badgeIcon(item)">
+            <v-avatar size="40" :image="item.person.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+          </v-badge>
+          <span class="defaultLink ml-3">{{ item?.person?.name }}</span>
+        </div>
+      </template>
+      <template v-slot:item.status="{ item }">
+        <span v-if="tab === 0">{{ item.status && item.status === '0' ? '未查看' : '已查看' }}</span>
+        <span v-else>{{ item.status ? props.statusList.find(i => i.value === item.status).label : '' }}</span>
+      </template>
+      <template v-slot:item.actions="{ item }">
+        <div v-if="tab === 0">
+          <v-btn color="primary" variant="text" @click="handlePreviewResume(item)">查看附件</v-btn>
+          <v-btn color="primary" variant="text" @click="handleInterviewInvite(item)">邀请面试</v-btn>
+        </div>
+        <v-btn v-if="tab === 0 || tab === 1" color="primary" variant="text" @click="handleEliminate(item)">不合适</v-btn>
+        <div v-if="tab === 1">
+          <v-btn color="primary" variant="text" @click="handleEnterByEnterprise(item)">入职</v-btn>
+        </div>
+        <v-btn v-if="tab === 4" color="primary" variant="text" @click="handleCancelEliminate(item)">取消不合适</v-btn>
+      </template>
+    </v-data-table>
+
+    <!-- 邀请面试 -->
+    <CtDialog :visible="showInvite" :widthType="2" titleClass="text-h6" title="面试信息" @close="handleEditClose" @submit="handleEditSubmit">
+      <InvitePage v-if="showInvite" ref="inviteRef" :itemData="itemData"></InvitePage>
+    </CtDialog>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'table-page'})
+import { ref, computed, watch } from 'vue'
+import { previewFile } from '@/utils'
+import { personJobCvLook, joinEliminate, personEntryByEnterprise, personCvUnfitCancel } from '@/api/recruit/enterprise/personnel'
+import { saveInterviewInvite } from '@/api/recruit/enterprise/interview'
+import { useI18n } from '@/hooks/web/useI18n'
+import Snackbar from '@/plugins/snackbar'
+import InvitePage from './invite.vue'
+
+const { t } = useI18n()
+const emit = defineEmits(['refresh'])
+const props = defineProps({
+  tab: Number,
+  items: Array,
+  statusList: Array
+})
+const badgeColor = computed(() => (item) => {
+  return (item.person && item.person.sex) ? (item.person.sex === '1' ? '#1867c0' : 'error') : 'error'
+})
+
+const badgeIcon = computed(() => (item) => {
+  return (item.person && item.person.sex) ? (item.person.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'
+})
+
+const inviteRef = ref()
+const showInvite = ref(false)
+const headers = ref([
+  { title: '姓名', value: 'name', sortable: false },
+  { title: '应聘职位', value: 'job.name', sortable: false },
+  { title: '求职状态', key: 'person.jobStatusName', sortable: false },
+  { title: '工作经验', key: 'person.expName', sortable: false },
+  { title: '最高学历', key: 'person.eduName', sortable: false },
+  { title: '岗位薪资', key: 'job', value: item => `${item.job.payFrom}-${item.job.payTo}/${item.job.payName}`, sortable: false },
+  { title: '状态', key: 'status', sortable: false },
+  { title: '操作', value: 'actions', sortable: false }
+])
+const unfit = { title: '类型', key: 'unfitType', sortable: false, value: item => item.type === '0' ? '简历不合适' : '面试不合适' }
+const delivery = { title: '类型', key: 'deliveryType', sortable: false, value: item => item.type === '0' ? '普通职位' : '赏金职位' }
+
+const list = [0, 4]
+watch(
+  () => props.tab,
+  (val) => {
+    if (list.indexOf(val) !== -1) {
+      headers.value.splice(-1, 0, val === 0 ? delivery : unfit)
+    } else {
+      const index = headers.value.indexOf(item => item.key === val === 0 ? 'deliveryType' : 'unfitType')
+      if (index !== -1) headers.value.splice(index, 1)
+    }
+  // 不合适不需要展示状态
+    if (val === 4) {
+      const obj = headers.value.find(e => e.key === 'status')
+      const i = headers.value.indexOf(obj)
+      if (i !== -1) headers.value.splice(i, 1)
+    }
+  },
+  { immediate: true }
+)
+
+// 人才详情
+const handleToPersonDetail = ({ userId, id }) => {
+  if (!userId || !id) return
+  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
+}
+
+// 入职
+const handleEnterByEnterprise = async (item) => {
+  if (!item.id) return
+  await personEntryByEnterprise(item.id)
+  Snackbar.success(t('common.operationSuccessful'))
+  emit('refresh')
+}
+
+// 不合适
+const handleEliminate = async (item) => {
+  if (!item.id || !item?.job?.id) return
+  const query = {
+    bizId: item.id,
+    jobId: item.job.id,
+    userId: item.userId,
+    type: props.tab === 0 ? '0' : '1' // 投递简历0 已邀约1
+  }
+  await joinEliminate(query)
+  Snackbar.success(t('common.operationSuccessful'))
+  emit('refresh')
+}
+
+// 取消不合适
+const handleCancelEliminate = async (item) => {
+  if (!item.id) return
+  await personCvUnfitCancel(item.id)
+  Snackbar.success(t('common.operationSuccessful'))
+  emit('refresh')
+}
+
+// 查看简历
+const handlePreviewResume = async ({ url, id }) => {
+  if (!url || !id) return
+  await personJobCvLook(id)
+  previewFile(url)
+}
+
+// 邀请面试
+const itemData = ref({})
+const handleInterviewInvite = (item) => {
+  itemData.value = item
+  showInvite.value = true
+}
+
+const handleEditClose = () => {
+  showInvite.value = false
+  itemData.value = {}
+}
+
+const handleEditSubmit = async () => {
+  const { valid } = await inviteRef.value.CtFormRef.formRef.validate()
+  if (!valid) return
+  const query = inviteRef.value.getQuery()
+  if (!query?.time) return Snackbar.warning('请选择面试时间')
+  await saveInterviewInvite(query)
+  Snackbar.success(t('common.operationSuccessful'))
+  handleEditClose()
+  emit('refresh')
+}
+</script>
+
+<style scoped lang="scss">
+:deep(.v-table > .v-table__wrapper > table > thead) {
+  background-color: #f7f8fa !important;
+}
+:deep(.v-selection-control__input) {
+  color: var(--v-primary-base) !important;
+}
+</style>

+ 145 - 0
src/views/recruit/enterprise/elite/index.vue

@@ -0,0 +1,145 @@
+<!-- 精英管理 -->
+<template>
+  <v-card class="pa-3 card-box">
+    <div class="d-flex justify-space-between">
+      <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa">
+        <v-tab v-for="k in tabList" :value="k.value" :key="k.value">{{ k.label }}</v-tab>
+      </v-tabs>
+      <TextInput v-model="textItems.value" :item="textItems" @appendInnerClick="handleSearch" @enter="handleSearch"></TextInput>
+    </div>
+    <Screen :tab="tab" @search="handleScreen" @reset="handleScreenReset" @select="handleSelect" @change="handleChangeBounty"></Screen>
+
+    <v-window v-model="tab" class="mt-1">
+      <v-window-item v-for="k in tabList" :value="k.value" :key="k.value">
+        <TablePage :items="items" :tab="k.value" :statusList="statusList" @refresh="getList"></TablePage>
+        <CtPagination
+          v-if="total > 0"
+          :total="total"
+          :page="query.pageNo"
+          :limit="query.pageSize"
+          @handleChange="handleChangePage"
+        ></CtPagination>
+      </v-window-item>
+    </v-window>
+  </v-card>
+</template>
+
+<script setup>
+defineOptions({ name: 'enterprise-elite-management'})
+import { ref } from 'vue'
+import { getPersonCvPage } from '@/api/enterprise'
+import { personCvUnfitPage } from '@/api/recruit/enterprise/personnel'
+import { dealDictObjData } from '@/utils/position'
+import { getDict } from '@/hooks/web/useDictionaries'
+import { getInterviewInvitePage } from '@/api/recruit/enterprise/interview'
+import TablePage from './components/table.vue'
+import Screen from './components/screen.vue'
+
+const total = ref(0)
+const query = ref({
+  pageNo: 1,
+  pageSize: 10,
+  status: null,
+  type: 0
+})
+const tab = ref(0)
+const tabList = ref([
+  { label: '投递简历', value: 0, api: getPersonCvPage, status: null },
+  { label: '已邀约', value: 1, api: getInterviewInvitePage, status: '0' },
+  { label: '已入职', value: 2, api: getInterviewInvitePage, status: '1' },
+  { label: '已结算', value: 3, api: getInterviewInvitePage, status: '2' },
+  { label: '不合适', value: 4, api: personCvUnfitPage },
+])
+const textItems = ref({
+  type: 'text',
+  value: '',
+  width: 250,
+  label: '搜索姓名',
+  clearable: true,
+  appendInnerIcon: 'mdi-magnify'
+})
+
+// 状态字典
+const statusList = ref([])
+getDict('menduner_interview_invite_status').then(({data}) => {
+  if (data && data.length) statusList.value = data
+})
+
+// 获取牛人列表
+const items = ref([])
+const getList = async () => {
+  const api = tabList.value[tab.value].api
+  if (tab.value !== 0) {
+    query.value.conversationStatus = tabList.value[tab.value].status
+    delete query.value.status
+    delete query.value.type
+  }
+
+  const { list, total: number } = await api(query.value)
+  if (!list.length) {
+    items.value = []
+    total.value = 0
+    return
+  }
+  total.value = number
+  items.value = list.map(e => {
+    let obj = e
+    obj.person = Object.assign(e.person, dealDictObjData({}, e.person))
+    obj.job = Object.assign(e.job, dealDictObjData({}, e.job))
+    return obj
+  })
+}
+getList()
+
+// 分页
+const handleChangePage = (i) => {
+  query.value.pageNo = i
+  getList()
+}
+
+// 牛人姓名检索
+const handleSearch = () => {
+  if (textItems.value.value) query.value.name = textItems.value.value
+  else delete query.value.name
+  query.value.pageNo = 1
+  getList()
+}
+
+// 下拉筛选
+const handleScreen = ({ value, key }) => {
+  if (value) query.value[key] = value
+  else delete query.value[key]
+  getList()
+}
+
+// 下拉筛选重置
+const handleScreenReset = () => {
+  query.value = {
+    pageSize: 10,
+    pageNo: 1,
+    status: tab.value
+  }
+  if (tab.value === 0) {
+    query.value.status = null
+    query.value.type = 0
+  }
+  if (textItems.value.value) query.value.name = textItems.value.value
+  getList()
+}
+
+const handleSelect = (e) => {
+  query.value.pageNo = 1
+  query.value.status = e
+  getList()
+}
+
+// 赏金职位
+const handleChangeBounty = (e) => {
+  query.value.type = e ? 1 : 0
+  query.value.pageNo = 1
+  getList()
+}
+</script>
+
+<style scoped lang="scss">
+</style>

+ 1 - 1
src/views/recruit/enterprise/interview/components/item.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="listItem d-flex align-center pa-3 mb-3" v-for="(item, index) in items" :key="'item_' + index">
     <div class="d-flex align-center">
-      <div class="mr-5 font-size-16" style="color: orange; width: 96px;">{{ timesTampChange(item.time) }}</div>
+      <div class="mr-5 font-size-16" style="color: orange; width: 96px;">{{ timesTampChange(item.time, 'Y-M-D h:m') }}</div>
       <v-avatar class="mr-2" size=40 :image="item?.person?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
       <div class="d-flex flex-column mr-3" style="width: 110px;">
         <span class="ellipsis mb-1">{{ item?.person?.name }}</span>

+ 1 - 1
src/views/recruit/enterprise/interview/index.vue

@@ -40,7 +40,7 @@
       <div>
         <div class="d-flex justify-space-between px-5">
           <div v-if="selectDateValue">
-            <span>{{ timesTampChange(selectDateValue).slice(0, 10) }}</span>
+            <span>{{ timesTampChange(selectDateValue, 'Y-M-D') }}</span>
             <span class="ml-2" style="cursor: pointer;" @click="handleClear">{{ $t('common.cleanUp') }}</span>
           </div>
           <div v-else class="color-999">{{ $t('interview.noDateSelected') }}</div>

+ 0 - 3
src/views/recruit/enterprise/personnelManagement/components/table.vue

@@ -2,11 +2,9 @@
   <div>
     <v-data-table
       class="mt-3"
-      v-model="selected"
       :items="items"
       :headers="headers"
       hover
-      show-select
       :disable-sort="true"
       height="60vh"
       item-value="id"
@@ -71,7 +69,6 @@ const badgeIcon = computed(() => (item) => {
 
 const inviteRef = ref()
 const showInvite = ref(false)
-const selected = ref([])
 const headers = ref([
   { title: '姓名', value: 'name', sortable: false },
   { title: '应聘职位', value: 'job.name', sortable: false },

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

@@ -47,7 +47,7 @@
         </div>
       </div>
       <div class="bottom pa-5 d-flex justify-space-between align-center">
-        <div>{{ $t('position.refreshTime') }} :{{ timesTampChange(val.updateTime).slice(0, 10) }} {{ val.expireDay && Number(val.expireDay) >= 1 ? `(${ val.expireDay }天后到期)` : '' }}</div>
+        <div>{{ $t('position.refreshTime') }} :{{ timesTampChange(val.updateTime, 'Y-M-D') }} {{ val.expireDay && Number(val.expireDay) >= 1 ? `(${ val.expireDay }天后到期)` : '' }}</div>
         <div class="d-flex">
           <div class="ml-10 d-flex">
             <div v-if="tab === 1">

+ 2 - 2
src/views/recruit/enterprise/talentPool/components/details/baseInfo.vue

@@ -43,7 +43,7 @@
           </div>
           <div>
             <span class="mdi mdi-cake-variant-outline"></span>
-            <span>{{ timesTampChange(info?.birthday).slice(0, 10) }}</span>
+            <span>{{ timesTampChange(info?.birthday, 'Y-M-D') }}</span>
           </div>
           <div>
             <span class="mdi mdi-account-heart"></span>
@@ -51,7 +51,7 @@
           </div>
           <div>
             <span>{{ $t('resume.firstWorkTime') }}:</span>
-            <span>{{ timesTampChange(info?.firstWorkTime).slice(0, 10) }}</span>
+            <span>{{ timesTampChange(info?.firstWorkTime, 'Y-M-D') }}</span>
           </div>
         </div>
       </div>

+ 2 - 2
src/views/recruit/enterprise/talentPool/components/details/educationExp.vue

@@ -9,9 +9,9 @@
           <div>
             <span style="font-size: 16px;">{{ item.schoolName }}</span>
             <span class="color6 font15 ml-5">
-              <span>{{ timesTampChange(item.startTime).slice(0, 10) }}</span>
+              <span>{{ timesTampChange(item.startTime, 'Y-M-D') }}</span>
               <span class="mx-1">至</span>
-              <span>{{ timesTampChange(item.endTime).slice(0, 10) }}</span>
+              <span>{{ timesTampChange(item.endTime, 'Y-M-D') }}</span>
             </span>
           </div>
           <div v-if="item.active">

+ 2 - 2
src/views/recruit/enterprise/talentPool/components/details/projectExperience.vue

@@ -5,9 +5,9 @@
         <div>
           <span style="font-size: 16px">{{ k.name }}</span>
           <span class="label-title ml-5">
-            <span>{{ timesTampChange(k.startTime).slice(0, 7) }}</span>
+            <span>{{ timesTampChange(k.startTime, 'Y-M') }}</span>
             <span class="mx-1">至</span>
-            <span>{{ timesTampChange(k.endTime).slice(0, 7) }}</span>
+            <span>{{ timesTampChange(k.endTime, 'Y-M') }}</span>
           </span>
         </div>
       </div>

+ 2 - 2
src/views/recruit/enterprise/talentPool/components/details/trainingExperience.vue

@@ -5,9 +5,9 @@
         <div>
           <span style="font-size: 16px;">{{ k.orgName }}</span>
           <span class="label-title ml-5">
-            <span>{{ timesTampChange(k.startTime).slice(0, 7) }}</span>
+            <span>{{ timesTampChange(k.startTime, 'Y-M') }}</span>
             <span class="mx-1">至</span>
-            <span>{{ timesTampChange(k.endTime).slice(0, 7) }}</span>
+            <span>{{ timesTampChange(k.endTime, 'Y-M') }}</span>
           </span>
         </div>
       </div>

+ 2 - 2
src/views/recruit/enterprise/talentPool/components/details/workExperience.vue

@@ -10,9 +10,9 @@
           <div>
             <span>{{ item.enterpriseName }}</span>
             <span class="color6 font15 ml-5">
-              <span>{{ timesTampChange(item.startTime).slice(0, 10) }}</span>
+              <span>{{ timesTampChange(item.startTime, 'Y-M-D') }}</span>
               <span class="mx-1">至</span>
-              <span>{{ timesTampChange(item.endTime).slice(0, 10) }}</span>
+              <span>{{ timesTampChange(item.endTime, 'Y-M-D') }}</span>
             </span>
             <span class="color6 font15 ml-5">{{ item.positionName }}</span>
           </div>

+ 1 - 1
src/views/recruit/personal/PersonalCenter/components/interview/item.vue

@@ -36,7 +36,7 @@
           </div>
         </div>
         <div class="font-size-14 color-666" style="width: 322px;">
-          <div>面试时间:{{ timesTampChange(val.time).slice(0, 16) }}</div>
+          <div>面试时间:{{ timesTampChange(val.time, 'Y-M-D h:m') }}</div>
           <div class="mt-3 ellipsis" style="max-width: 322px;">面试地点:{{ val.address }}</div>
         </div>
       </div>

+ 1 - 1
src/views/recruit/personal/remuse/components/basicInfo.vue

@@ -396,7 +396,7 @@ items.value.options.forEach((e, index) => {
   const infoExist = baseInfo.value && Object.keys(baseInfo.value).length
   if (infoExist && baseInfo.value[e.key]) e.value = baseInfo.value[e.key]
   // 日期相关
-  if (e.type === 'datepicker') e.value = timesTampChange(e.value).slice(0, 10)
+  if (e.type === 'datepicker') e.value = timesTampChange(e.value, 'Y-M-D')
   // 所在城市回显
   // if (infoExist && e.nameKey) e[e.nameKey] = baseInfo.value[e.nameKey]
   if (infoExist && e.key === 'areaId' && baseInfo.value[e.key]) {

+ 3 - 3
src/views/recruit/personal/remuse/components/educationExp.vue

@@ -25,9 +25,9 @@
           <div>
             <span style="font-size: 18px; font-weight: bold;">{{ item.schoolName }}</span>
             <span class="color6 font15 ml-5">
-              <span>{{ timesTampChange(item.startTime).slice(0, 7) }}</span>
+              <span>{{ timesTampChange(item.startTime, 'Y-M') }}</span>
               <span class="mx-1">至</span>
-              <span>{{ timesTampChange(item.endTime).slice(0, 7) }}</span>
+              <span>{{ timesTampChange(item.endTime, 'Y-M') }}</span>
             </span>
           </div>
           <div v-if="item.active">
@@ -217,7 +217,7 @@ const handle = (item) => {
         // if (item[e.key] && item[e.itemTextName]) { e.search(item[e.itemTextName], '触发下拉框内容'); e.value = item[e.key] }
         // else { e.value = item[e.itemTextName]; e[e.itemTextName] = item[e.itemTextName] }
       }
-      else if (e.type === 'datepicker') e.value = timesTampChange(item[e.key]).slice(0, 7)
+      else if (e.type === 'datepicker') e.value = timesTampChange(item[e.key], 'Y-M')
       else if (item[e.key]) e.value = item[e.key]
     })
   } else { // 新增

+ 2 - 2
src/views/recruit/personal/remuse/components/projectExperience.vue

@@ -23,9 +23,9 @@
         <div>
           <span style="font-size: 18px; font-weight: bold;">{{ k.name }}</span>
           <span class="label-title ml-5">
-            <span>{{ timesTampChange(k.startTime).slice(0, 7) }}</span>
+            <span>{{ timesTampChange(k.startTime, 'Y-M') }}</span>
             <span class="mx-1">至</span>
-            <span>{{ timesTampChange(k.endTime).slice(0, 7) }}</span>
+            <span>{{ timesTampChange(k.endTime, 'Y-M') }}</span>
           </span>
         </div>
       </div>

+ 2 - 2
src/views/recruit/personal/remuse/components/trainingExperience.vue

@@ -23,9 +23,9 @@
             <div>
               <span style="font-size: 18px; font-weight: bold;">{{ k.orgName }}</span>
               <span class="label-title ml-5">
-                <span>{{ timesTampChange(k.startTime).slice(0, 7) }}</span>
+                <span>{{ timesTampChange(k.startTime, 'Y-M') }}</span>
                 <span class="mx-1">至</span>
-                <span>{{ timesTampChange(k.endTime).slice(0, 7) }}</span>
+                <span>{{ timesTampChange(k.endTime, 'Y-M') }}</span>
               </span>
             </div>
           </div>

+ 3 - 3
src/views/recruit/personal/remuse/components/workExperience.vue

@@ -25,9 +25,9 @@
           <div>
             <span style="font-size: 18px; font-weight: bold;">{{ item.enterpriseName }}</span>
             <span class="color6 font15 ml-5">
-              <span>{{ timesTampChange(item.startTime).slice(0, 7) }}</span>
+              <span>{{ timesTampChange(item.startTime, 'Y-M') }}</span>
               <span class="mx-1">至</span>
-              <span>{{ timesTampChange(item.endTime).slice(0, 7) }}</span>
+              <span>{{ timesTampChange(item.endTime, 'Y-M') }}</span>
             </span>
           </div>
           <div v-if="item.active">
@@ -184,7 +184,7 @@ const handle = (item) => {
       if (e.canBeInputted) { // 特殊处理可输入下拉框
         dealCanBeInputtedValueAndLabel(e, item)
       }
-      else if (e.type === 'datepicker') e.value = timesTampChange(item[e.key]).slice(0, 7)
+      else if (e.type === 'datepicker') e.value = timesTampChange(item[e.key], 'Y-M')
       else if (item[e.key]) e.value = item[e.key]
     })
   } else { // 新增