瀏覽代碼

骐骥之才

lifanagju_citu 3 周之前
父節點
當前提交
c337f0db91

+ 84 - 89
src/components/Knowledge/index.vue

@@ -21,7 +21,7 @@
       style="z-index: 400"
     >
       <m-talk v-if="show" :title="title" :type="type" v-bind="$attrs">
-        <template v-for="name in Object.keys(this.$scopedSlots)" v-slot:[`${name}`]="slotProps">
+        <template v-for="name in Object.keys($slots)" v-slot:[`${name}`]="slotProps">
           <slot :name="name" v-bind="slotProps"></slot>
         </template>
       </m-talk>
@@ -29,106 +29,101 @@
   </div>
 </template>
 
-<script>
+<script setup>
+import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue'
 import MTalk from './talk'
-export default {
-  name: 'knowledge-component',
-  components: {
-    MTalk
-  },
-  props: {
-    // 1 手册探索 2 图表实验室 3 产品知识库
-    type: {
-      type: Number,
-      default: 1
-    },
-    title: {
-      type: String,
-      default: '数据探索'
-    },
-    handle: Function
-  },
-  data () {
-    return {
-      show: false,
-      lock: false,
-      isDragging: false,
-      info: {}
-    }
+
+const props = defineProps({
+  // 1 手册探索 2 图表实验室 3 产品知识库
+  type: {
+    type: Number,
+    default: 1
   },
-  mounted () {
-    this.$nextTick(() => {
-      document.addEventListener('mouseup', this.handleMouseup)
-      document.addEventListener('mousemove', this.handleMousemove)
-      this.$once('hook:beforeDestroy', () => {
-        document.removeEventListener('mouseup', this.handleMouseup)
-        document.removeEventListener('mousemove', this.handleMousemove)
-      })
-    })
+  title: {
+    type: String,
+    default: '数据探索'
   },
-  methods: {
-    handleClick () {
-      if (this.isDragging) {
-        return
-      }
-      if (this.handle) {
-        this.handle()
-        return
-      }
-      this.show = !this.show
-    },
-    // 记录当前位置
-    handleMousedown (e) {
-      this.lock = true
-      const { left, top, height, width } = this.$refs.box.$el.getBoundingClientRect()
-      this.info = {
-        click: {
-          x: e.clientX,
-          y: e.clientY
-        },
-        left,
-        top,
-        height,
-        width,
-        clientX: e.clientX,
-        clientY: e.clientY
-      }
-    },
-    // 移动位置
-    handleMousemove (e) {
-      if (!this.lock) return
-      const xLen = Math.abs(this.info.click?.x - e.clientX)
-      const yLen = Math.abs(this.info.click?.y - e.clientY)
-      // 拖动距离触发
-      if (xLen < 20 && yLen < 20) {
-        return
-      }
+  handle: Function
+})
+
+const show = ref(false)
+const lock = ref(false)
+const isDragging = ref(false)
+const info = ref({})
+const box = ref(null)
 
-      // 计算移动距离
-      const clientX = e.clientX - this.info.clientX
-      const clientY = e.clientY - this.info.clientY
-      this.isDragging = true
-      this.info.left += clientX
-      this.info.top += clientY
-      this.$refs.box.$el.style.left = this.info.left + 'px'
-      this.$refs.box.$el.style.top = this.info.top + 'px'
-      this.info.clientX = e.clientX
-      this.info.clientY = e.clientY
+const handleClick = () => {
+  if (isDragging.value) {
+    return
+  }
+  if (props.handle) {
+    props.handle()
+    return
+  }
+  show.value = !show.value
+}
+
+const handleMousedown = (e) => {
+  lock.value = true
+  const { left, top, height, width } = box.value.$el.getBoundingClientRect()
+  info.value = {
+    click: {
+      x: e.clientX,
+      y: e.clientY
     },
-    handleMouseup (e) {
-      this.lock = false
-      setTimeout(() => {
-        this.isDragging = false
-      })
-    }
+    left,
+    top,
+    height,
+    width,
+    clientX: e.clientX,
+    clientY: e.clientY
   }
 }
+
+const handleMousemove = (e) => {
+  if (!lock.value) return
+  const xLen = Math.abs(info.value.click?.x - e.clientX)
+  const yLen = Math.abs(info.value.click?.y - e.clientY)
+  // 拖动距离触发
+  if (xLen < 20 && yLen < 20) {
+    return
+  }
+
+  // 计算移动距离
+  const clientX = e.clientX - info.value.clientX
+  const clientY = e.clientY - info.value.clientY
+  isDragging.value = true
+  info.value.left += clientX
+  info.value.top += clientY
+  box.value.$el.style.left = info.value.left + 'px'
+  box.value.$el.style.top = info.value.top + 'px'
+  info.value.clientX = e.clientX
+  info.value.clientY = e.clientY
+}
+
+const handleMouseup = () => {
+  lock.value = false
+  setTimeout(() => {
+    isDragging.value = false
+  })
+}
+
+onMounted(() => {
+  nextTick(() => {
+    document.addEventListener('mouseup', handleMouseup)
+    document.addEventListener('mousemove', handleMousemove)
+  })
+})
+
+onBeforeUnmount(() => {
+  document.removeEventListener('mouseup', handleMouseup)
+  document.removeEventListener('mousemove', handleMousemove)
+})
 </script>
 
 <style lang="scss" scoped>
 .point {
   cursor: pointer;
-
 }
 .box {
   z-index: 9;

+ 175 - 204
src/components/Knowledge/talk.vue

@@ -35,7 +35,7 @@
                 `${talk.type === 'question' ? 'justify-end ' + user.nameColor + '--text' : system.nameColor + '--text'}`
                 "
               >
-                {{ talk.type === 'question' ? $store.getters.userInfo.username : '' }}
+                {{ talk.type === 'question' ? formatName(baseInfo?.name ?? baseInfo?.phone) : '' }}
                 <template v-if="talk.type === 'question'">
                   <v-chip v-for="tag in talk.tags" :key="tag" x-small class="ml-2">{{ tag }}</v-chip>
                 </template>
@@ -75,7 +75,6 @@
                   </template>
                   <template v-else>
                     <div v-for="(content, index) in talk.content" :key="index + content">{{ content }}</div>
-                    <!-- {{ talk.content }} -->
                   </template>
                 </div>
               </v-list-item-subtitle>
@@ -125,222 +124,194 @@
   </div>
 </template>
 
-<script>
+<script setup>
+import { ref, watch, nextTick } from 'vue'
 import { api } from '@/api/dataGovernance'
-export default {
-  name: 'knowledge-talk',
-  props: {
-    // 1 手册探索 2 图表实验室 3 产品知识库 (RAG + Graph) 4 产品知识库 (RAG)
-    type: {
-      type: Number,
-      default: 1
-    },
-    title: {
-      type: String,
-      default: '数据探索'
-    },
-    welcome: {
-      type: String,
-      default: '您好,我是您的智能助手,有什么问题可以问我哦!'
-    }
+import { formatName } from '@/utils/getText'
+import Snackbar from '@/plugins/snackbar'
+
+const props = defineProps({
+  // 1 手册探索 2 图表实验室 3 产品知识库 (RAG + Graph) 4 产品知识库 (RAG)
+  type: {
+    type: Number,
+    default: 1
   },
-  data () {
-    return {
-      // 系统端信息配置
-      system: {
-        name: '智能助手',
-        nameColor: 'indigo',
-        icon: 'mdi-face-agent',
-        iconColor: 'indigo',
-        bgColor: 'indigo',
-        welcome: this.welcome
-      },
-      // 用户端信息配置
-      user: {
-        name: this.$store.getters.userInfo.username,
-        nameColor: 'blue',
-        icon: 'mdi-account-circle',
-        iconColor: 'blue',
-        bgColor: 'blue'
-      },
-      loading: false,
-      talks: [],
-      question: '',
-      list: [],
-      id: 0
-    }
+  title: {
+    type: String,
+    default: '数据探索'
   },
-  watch: {
-    // 长度变化自动滑动至底部
-    talks: {
-      handler () {
-        this.$nextTick(() => {
-          this.$refs.box.scrollTo({
-            top: this.$refs.box.scrollHeight,
-            behavior: 'smooth'
-          })
-        })
-      },
-      deep: true,
-      immediate: true
+  welcome: {
+    type: String,
+    default: '您好,我是您的智能助手,有什么问题可以问我哦!'
+  }
+})
+
+// 企业基本信息
+const baseInfo = ref(JSON.parse(localStorage.getItem('entBaseInfo')) || {})
+
+const box = ref(null)
+const loading = ref(false)
+const talks = ref([])
+const question = ref('')
+const list = ref([])
+const id = ref(0)
+
+// 系统端信息配置
+const system = ref({
+  name: '智能助手',
+  nameColor: 'indigo',
+  icon: 'mdi-face-agent',
+  iconColor: 'indigo',
+  bgColor: 'indigo',
+  welcome: props.welcome
+})
+
+// 用户端信息配置
+const user = ref({
+  name: formatName(baseInfo.value?.name ?? baseInfo.value?.phone),
+  nameColor: 'blue',
+  icon: 'mdi-account-circle',
+  iconColor: 'blue',
+  bgColor: 'blue'
+})
+
+// 监听talks变化,自动滚动到底部
+watch(talks, () => {
+  nextTick(() => {
+    box.value.scrollTo({
+      top: box.value.scrollHeight,
+      behavior: 'smooth'
+    })
+  })
+}, { deep: true, immediate: true })
+
+const handleSendMsg = () => {
+  if (!question.value) {
+    return
+  }
+  switch (props.type) {
+    case 2:
+      getSql()
+      break
+    case 4:
+      talkTo(3, true)
+      break
+    default:
+      talkTo(props.type)
+      break
+  }
+}
+
+const handleKeyCode = (event) => {
+  if (event.keyCode === 13) {
+    if (!event.ctrlKey) {
+      event.preventDefault()
+      handleSendMsg()
+    } else {
+      question.value += '\n'
     }
-  },
-  methods: {
-    handleSendMsg () {
-      if (!this.question) {
-        return
-      }
-      switch (this.type) {
-        case 2:
-          this.getSql()
-          break
-        case 4:
-          this.talkTo(3, true)
-          break
-        default:
-          this.talkTo(this.type)
-          break
-      }
-    },
-    handleKeyCode (event) {
-      if (event.keyCode === 13) {
-        if (!event.ctrlKey) {
-          event.preventDefault()
-          this.handleSendMsg()
-        } else {
-          this.question += '\n'
-        }
-      }
-    },
-    async getSql () {
-      this.loading = true
-      this.talks.push({
-        id: this.id++,
-        type: 'question',
-        tags: [],
-        content: this.question.split('\n')
-      })
-      const params = {
-        question: this.question
-      }
-      this.question = ''
-      try {
-        const { data } = await api.getSql(params)
-        this.talks.push({
-          id: this.id++,
-          type: 'response',
-          tags: [],
-          content: data.text.split('\n')
-        })
-        this.getTable(data.id)
-      } catch (error) {
-        this.$snackbar.error(error)
-      } finally {
-        this.loading = false
-      }
-    },
-    async getTable (id) {
-      this.loading = true
-      const params = { id }
-      try {
-        const { data } = await api.getToTable(params)
-        if (data.type === 'error') {
-          return
-        }
-        const _table = JSON.parse(data.df)
-        if (!_table.length) {
-          return
-        }
-        // console.log(_table)
-        const _headers = Object.keys(_table[0])
-        this.talks.push({
-          id: this.id++,
-          type: 'response',
-          tags: [],
-          content: {
-            headers: _headers,
-            body: _table
-          },
-          view: 'table'
-        })
-        this.$emit('change', _headers, _table)
-      } catch (error) {
-        this.$snackbar.error(error)
-      } finally {
-        this.loading = false
-      }
-    },
-    async talkTo (useType, rag) {
-      this.talks.push({
-        id: this.id++,
-        type: 'question',
-        tags: [],
-        content: this.question.split('\n')
-      })
-      const param = {
-        question: this.question,
-        chat_history: this.list
-      }
-      this.question = ''
-      this.loading = true
-      const askApi = useType === 3 ? api.askToUnstructured : api.ask
-      try {
-        const { data } = await askApi(param)
-        this.talks.push({
-          id: this.id++,
-          type: 'response',
-          tags: useType === 3 ? ['RAG + Graph'] : [],
-          content: data.response.split('\n')
-        })
-        // 多获取一个图谱+RAG
-        if (rag) {
-          const { data: _data } = await api.askToProduct(param)
-          this.talks.push({
-            id: this.id++,
-            type: 'response',
-            tags: ['RAG'],
-            content: _data.response.split('\n')
-          })
-        }
-      } catch (error) {
-        this.$snackbar.error(error)
-      } finally {
-        this.loading = false
-      }
+  }
+}
+
+const getSql = async () => {
+  loading.value = true
+  talks.value.push({
+    id: id.value++,
+    type: 'question',
+    tags: [],
+    content: question.value.split('\n')
+  })
+  const params = {
+    question: question.value
+  }
+  question.value = ''
+  try {
+    const { data } = await api.getSql(params)
+    talks.value.push({
+      id: id.value++,
+      type: 'response',
+      tags: [],
+      content: data.text.split('\n')
+    })
+    getTable(data.id)
+  } catch (error) {
+    Snackbar.error(error)
+  } finally {
+    loading.value = false
+  }
+}
+
+const getTable = async (id) => {
+  loading.value = true
+  const params = { id }
+  try {
+    const { data } = await api.getToTable(params)
+    if (data.type === 'error') {
+      return
     }
+    const _table = JSON.parse(data.df)
+    talks.value.push({
+      id: id.value++,
+      type: 'response',
+      tags: [],
+      view: 'table',
+      content: _table
+    })
+  } catch (error) {
+    Snackbar.error(error)
+  } finally {
+    loading.value = false
+  }
+}
+
+const talkTo = async (type, isRag = false) => {
+  loading.value = true
+  talks.value.push({
+    id: id.value++,
+    type: 'question',
+    tags: [],
+    content: question.value.split('\n')
+  })
+  const params = {
+    question: question.value,
+    type,
+    isRag
+  }
+  question.value = ''
+  try {
+    const { data } = await api.talkTo(params)
+    talks.value.push({
+      id: id.value++,
+      type: 'response',
+      tags: data.tags || [],
+      content: data.text.split('\n')
+    })
+  } catch (error) {
+    Snackbar.error(error)
+  } finally {
+    loading.value = false
   }
 }
 </script>
 
 <style lang="scss" scoped>
-.round {
-  border-radius: 5px;
-  max-width: 80%;
-}
 .send {
-  // height: 130px;
-  // margin: 20px 0;
-  padding: 20px;
-  &-box {
+  padding: 10px;
+  border-top: 1px solid #e0e0e0;
+  .send-box {
     width: 100%;
-    // max-width: 800px;
-    position: relative;
-    .btn {
-      position: absolute;
-      right: 20px;
-      bottom: 12px;
+    display: flex;
+    align-items: center;
+    .send-box-area {
+      flex: 1;
+      margin-right: 10px;
     }
-    &-area {
-      position: relative;
-      bottom: 0;
-      ::v-deep textarea {
-        padding: 15px 70px 15px 0 !important;
-        max-height: 300px;
-        min-height: 60px;
-        overflow: auto;
-        margin: 0 !important;
-      }
+    .btn {
+      flex-shrink: 0;
     }
   }
 }
+.round {
+  border-radius: 8px;
+}
 </style>

+ 8 - 0
src/router/modules/components/recruit/enterprise.js

@@ -428,6 +428,14 @@ const enterprise = [
         },
         component: () => import('@/views/recruit/enterprise/newTalentMap/newlyAppointed/index.vue')
       },
+      {
+        path: '/recruit/enterprise/talentMap/talentMatching',
+        meta: {
+          title:'骐骥之才',
+          enName: 'Talent matching'
+        },
+        component: () => import('@/views/recruit/enterprise/newTalentMap/talentMatching/index.vue')
+      },
       {
         path: '/recruit/enterprise/talentMap/tagManagement',
         meta: {

+ 122 - 0
src/views/recruit/enterprise/newTalentMap/talentMatching/index.vue

@@ -0,0 +1,122 @@
+<!-- 人才匹配 -->
+<template>
+  <div>
+		<v-card elevation="5" class="mt-3">
+			<CtTable
+				:items="items"
+				class="pa-3"
+				:headers="headers"
+				:loading="loading"
+				:disable-sort="true"
+				:elevation="0"
+				:isTools="false"
+				height="calc(100vh - 400px)"
+				:showPage="true"
+				:showFixedLastItem="true"
+				:total="total"
+				:pageInfo="query"
+				itemKey="id"
+				@pageHandleChange="handleChangePage"
+			>
+				<template #workTerritory="{ item }">
+					<div class="ellipsis" style="max-width: 150px;">
+						{{ item.workTerritory }}
+						<v-tooltip activator="parent" location="top">{{ item.workTerritory }}</v-tooltip>
+					</div>
+				</template>
+				<template #workHistory="{ item }">
+					<div class="ellipsis" style="max-width: 150px;">
+						{{ item.workHistory }}
+						<v-tooltip activator="parent" location="top">{{ item.workHistory }}</v-tooltip>
+					</div>
+				</template>
+				<template #actions="{ item }">
+					<v-btn variant="text" color="primary" @click.stop="handleDetail(item)">详 情</v-btn>
+				</template>
+			</CtTable>
+		</v-card>
+
+    <v-navigation-drawer v-model="showDetail" absolute location="right" rounded temporary width="700" class="pa-5">
+      <div style="width: 300px; height: 300px; margin: 0 auto;">
+        <v-img :src="detail?.picUrl" width="300" height="300" />
+      </div>
+      <div class="mt-10" v-if="detail?.detailIntroduction" v-html="detail?.detailIntroduction.replace(/\n/g, '</br>')"></div>
+    </v-navigation-drawer>
+    
+    <knowledge
+      :type="switchValue ? 4 : 3"
+      welcome="我是您的产品助手。请问您需要了解什么内容?"
+      title="产品知识库"
+    >
+      <template #title>
+        <v-switch
+          v-model="switchValue"
+          class="mt-0"
+          hide-details
+          label="RAG对比"
+        ></v-switch>
+      </template>
+    </knowledge>
+
+  </div>
+</template>
+
+<script setup>
+defineOptions({name: 'talent-matching'})
+import { getNewAppointmentsPage, getNewAppointmentsDetail } from '@/api/recruit/enterprise/newlyAppointed'
+import Knowledge from '@/components/Knowledge'
+import { ref } from 'vue'
+
+const loading = ref(false)
+const total = ref(10)
+const query = ref({
+  pageSize: 10,
+	pageNo: 1
+})
+const items = ref([])
+const headers = [
+  { title: '中文名', key: 'nameChinese', sortable: false },
+  { title: '英文名', key: 'nameEnglish', sortable: false },
+  { title: '性别', key: 'gender', sortable: false },
+  { title: '职位', key: 'position', sortable: false },
+  { title: '任职酒店', key: 'inaugurationHotel', sortable: false },
+  { title: '酒店品牌', key: 'hotelBrand', sortable: false },
+  { title: '工作地域', key: 'workTerritory', sortable: false },
+  { title: '过往工作酒店品牌', key: 'workHistory', sortable: false },
+  { title: '操作', key: 'actions', sortable: false, align: 'center' }
+]
+
+// 查看详情
+const showDetail = ref(false)
+const detail = ref({})
+const handleDetail = async (item) => {
+	const result = await getNewAppointmentsDetail(item.id)
+	if (!result || !Object.keys(result).length) return Snackbar.warning('暂无详细信息,去查看其他人的信息吧~') 
+	detail.value = result
+	showDetail.value = true
+}
+
+const getList = async () => {
+	loading.value = true
+	try {
+		const result = await getNewAppointmentsPage(query.value)
+		items.value = result.list
+		total.value = result.total
+	} finally {
+		loading.value = false
+	}
+}
+getList()
+
+// 分页切换
+const handleChangePage = async (e) => {
+	query.value.pageNo = e
+	getList()
+}
+
+// 知识库对话
+const switchValue = ref(false)
+
+</script>
+<style lang="scss" scoped>
+</style>