Selaa lähdekoodia

应聘简历分析

Xiao_123 9 kuukautta sitten
vanhempi
commit
a97923963d

+ 33 - 0
src/api/recruit/enterprise/statistics/index.js

@@ -0,0 +1,33 @@
+import request from '@/config/axios'
+
+// 获取投递简历的年龄分布
+export const getJobCvAgeCount = async (params) => {
+  return await request.get({
+    url: '/app-admin-api/menduner/system/analysis/get/job/cv/age/count',
+    params
+  })
+}
+
+// 获取投递简历的学历分布
+export const getJobCvEduCount = async (params) => {
+  return await request.get({
+    url: '/app-admin-api/menduner/system/analysis/get/job/cv/edu/count',
+    params
+  })
+}
+
+// 获取投递简历的工作经验分布
+export const getJobCvExpCount = async (params) => {
+  return await request.get({
+    url: '/app-admin-api/menduner/system/analysis/get/job/cv/exp/count',
+    params
+  })
+}
+
+// 获取投递简历的性别分布
+export const getJobCvSexCount = async (params) => {
+  return await request.get({
+    url: '/app-admin-api/menduner/system/analysis/get/job/cv/sex/count',
+    params
+  })
+}

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

@@ -17,6 +17,7 @@
       :clearable="options.clearable ?? true"
       :day-names="['一', '二', '三', '四', '五', '六', '七']"
       v-bind="$attrs"
+      @update:model-value="options?.change"
     ></VueDatePicker>
   </div>
 </template>

+ 6 - 5
src/components/Echarts/index.vue

@@ -16,7 +16,6 @@ const props = defineProps({
     type: [Number, String],
     default: 485
   },
-  // 附加数据
   option: {
     type: [Object, Array],
     default: () => {}
@@ -32,13 +31,15 @@ const initMap = () => {
   chart.value.on('click', (param) => {
     emit('click', param)
   })
-  window.addEventListener('resize',()=>{
-    chart.value.resize()
-  })
+  // window.addEventListener('resize',()=>{
+  //   chart.value.resize()
+  // })
 }
 
 onMounted(() => {
-  initMap()
+  nextTick(()=>{
+    initMap()
+  })
 })
 
 watch(

+ 25 - 0
src/utils/date.js

@@ -60,4 +60,29 @@ export const getStartAndEndOfDay = (dateString) => {
   }
 
   return [formatDate(startDate), formatDate(endDate)]
+}
+
+// 传入一组时间戳,返回 [最早时间点,最晚时间点]
+export const  convertTimestampsToDayRange = (timestamps) => {
+  if (timestamps.length < 2) {
+    throw new Error('Timestamps array must contain at least two elements')
+  }
+  function formatDate(date) {
+    const year = date.getFullYear();
+    const month = String(date.getMonth() + 1).padStart(2, '0')
+    const day = String(date.getDate()).padStart(2, '0')
+    const hours = String(date.getHours()).padStart(2, '0')
+    const minutes = String(date.getMinutes()).padStart(2, '0')
+    const seconds = String(date.getSeconds()).padStart(2, '0')
+    return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+  }
+
+  const startDate = new Date(timestamps[0])
+  const endDate = new Date(timestamps[1])
+
+  const startOfDay = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate())
+
+  const endOfDay = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate() + 1, 0, 0, 0, -1)
+
+  return [formatDate(startOfDay), formatDate(endOfDay)]
 }

+ 0 - 174
src/views/recruit/enterprise/statistics/components/data.js

@@ -1,174 +0,0 @@
-export const list = [
-  {
-    col: 6,
-    option: {
-      title: {
-        text: '性别比例'
-      },
-      tooltip: {
-        trigger: 'item'
-      },
-      legend: {
-        top: '5%',
-        left: 'center'
-      },
-      series: [
-        {
-          name: 'Access From',
-          type: 'pie',
-          radius: ['40%', '70%'],
-          avoidLabelOverlap: false,
-          itemStyle: {
-            borderRadius: 10,
-            borderColor: '#fff',
-            borderWidth: 2
-          },
-          label: {
-            show: true,
-            formatter: e => {
-              return e.name + ': ' + e.value + '%'
-            }
-          },
-          labelLine: {
-            show: true
-          },
-          data: [
-            { value: 65, name: '男' },
-            { value: 35, name: '女' }
-          ]
-        }
-      ]
-    }
-  },
-  {
-    col: 6,
-    option: {
-      title: {
-        text: '年龄分布'
-      },
-      xAxis: {
-        type: 'category',
-        name: '范围',
-        data: ['18-22岁', '22-30岁', '30-39岁', '40-49岁', '50-59岁']
-      },
-      yAxis: {
-        type: 'value'
-      },
-      grid: {
-        left: '0',
-        top: '60',
-        right: '50',
-        bottom: '10',
-        containLabel: true
-      },
-      series: [
-        {
-          data: [120, 200, 150, 80, 70],
-          type: 'bar',
-          barWidth: 40,
-          label: {
-            show: true
-          }
-        }
-      ]
-    }
-  },
-  {
-    col: 6,
-    option: {
-      title: {
-        text: '工作年限分布'
-      },
-      xAxis: {
-        type: 'category',
-        name: '范围',
-        data: ['应届毕业生', '1年以上', '2年以上', '3年以上', '5年以上', '8年以上', '10年以上']
-      },
-      yAxis: {
-        type: 'value'
-      },
-      grid: {
-        left: '0',
-        top: '60',
-        right: '50',
-        bottom: '10',
-        containLabel: true
-      },
-      series: [
-        {
-          data: [120, 200, 150, 80, 70, 110, 130],
-          type: 'bar',
-          barWidth: 40,
-          label: {
-            show: true
-          }
-        }
-      ]
-    }
-  },
-  {
-    col: 6,
-    option: {
-      title: {
-        text: '学历分布'
-      },
-      xAxis: {
-        type: 'category',
-        name: '范围',
-        data: ['本科以上', '大专', '中专', '中技', '高中', '初中']
-      },
-      yAxis: {
-        type: 'value'
-      },
-      grid: {
-        left: '0',
-        top: '60',
-        right: '50',
-        bottom: '10',
-        containLabel: true
-      },
-      series: [
-        {
-          data: [120, 200, 150, 80, 70, 110],
-          type: 'bar',
-          barWidth: 40,
-          label: {
-            show: true
-          }
-        }
-      ]
-    }
-  },
-  // {
-  //   col: 12,
-  //   option: {
-  //     title: {
-  //       text: '期望月薪'
-  //     },
-  //     xAxis: {
-  //       type: 'category',
-  //       data: ['3-5k', '5-8k', '8-12k', '12-15k', '15-20k', '20-30k', '面议']
-  //     },
-  //     yAxis: {
-  //       type: 'value'
-  //     },
-  //     grid: {
-  //       left: '0',
-  //       top: '50',
-  //       right: '0',
-  //       bottom: 0,
-  //       containLabel: true
-  //     },
-  //     series: [
-  //       {
-  //         data: [120, 200, 150, 80, 70, 110, 130],
-  //         type: 'bar',
-  //         barWidth: 40,
-  //         label: {
-  //           show: true
-  //         }
-  //       }
-  //     ]
-  //   }
-  // }
-]

+ 133 - 17
src/views/recruit/enterprise/statistics/components/resume.vue

@@ -1,47 +1,48 @@
 <template>
-  <!-- <v-container>
-    <v-row>
-      <v-col class="bgc" v-for="(val, i) in list" :key="i" :md="val.col">
-        <Echarts :height="400" :option="val.option"></Echarts>
-      </v-col>
-    </v-row>
-  </v-container> -->
   <div class="chart-box">
     <div class="chart-item" v-for="(val, i) in list" :key="i">
       <Echarts :height="400" :option="val.option"></Echarts>
     </div>
-    <div class="fullChart">
+    <!-- <div class="fullChart">
       <Echarts :height="400" :option="option"></Echarts>
-    </div>
+    </div> -->
   </div>
 </template>
 
 <script setup>
 defineOptions({ name: 'resume-analysis'})
-import { list } from './data.js'
+import { ref, onMounted, watch, nextTick } from 'vue'
+import cloneDeep from 'lodash/cloneDeep'
+import { getJobCvAgeCount, getJobCvEduCount, getJobCvSexCount, getJobCvExpCount } from '@/api/recruit/enterprise/statistics'
+
+const props = defineProps({
+  query: Object
+})
 
-const option = {
+// 柱状图公共option
+const barCommonOption = {
   title: {
-    text: '期望月薪'
+    text: ''
   },
   xAxis: {
     type: 'category',
     name: '范围',
-    data: ['3-5k', '5-8k', '8-12k', '12-15k', '15-20k', '20-30k', '面议']
+    data: []
   },
   yAxis: {
-    type: 'value'
+    type: 'value',
+    name: '数量(人)'
   },
   grid: {
-    left: '0',
-    top: '60',
+    left: '20',
+    top: '70',
     right: '50',
     bottom: '10',
     containLabel: true
   },
   series: [
     {
-      data: [120, 200, 150, 80, 70, 110, 130],
+      data: [],
       type: 'bar',
       barWidth: 40,
       label: {
@@ -51,6 +52,121 @@ const option = {
   ]
 }
 
+// 期望月薪柱状图
+// const option = {
+//   title: {
+//     text: '期望月薪'
+//   },
+//   xAxis: {
+//     type: 'category',
+//     name: '范围',
+//     data: ['3-5k', '5-8k', '8-12k', '12-15k', '15-20k', '20-30k', '面议']
+//   },
+//   yAxis: {
+//     type: 'value'
+//   },
+//   grid: {
+//     left: '0',
+//     top: '60',
+//     right: '50',
+//     bottom: '10',
+//     containLabel: true
+//   },
+//   series: [
+//     {
+//       data: [120, 200, 150, 80, 70, 110, 130],
+//       type: 'bar',
+//       barWidth: 40,
+//       label: {
+//         show: true
+//       }
+//     }
+//   ]
+// }
+
+const list = ref([
+  {
+    api: getJobCvSexCount,
+    isPie: true,
+    option: {
+      title: {
+        text: '性别分布'
+      },
+      tooltip: {
+        trigger: 'item'
+      },
+      legend: {
+        top: '5%',
+        left: 'center'
+      },
+      series: [
+        {
+          name: '性别分布',
+          type: 'pie',
+          radius: ['40%', '70%'],
+          avoidLabelOverlap: false,
+          itemStyle: {
+            borderRadius: 10,
+            borderColor: '#fff',
+            borderWidth: 2
+          },
+          label: {
+            show: true,
+            formatter: e => {
+              return e.data.key + ': ' + e.value + '人'
+            }
+          },
+          labelLine: {
+            show: true
+          },
+          data: []
+        }
+      ]
+    }
+  },
+  {
+    api: getJobCvAgeCount,
+    title: '年龄分布',
+    option: cloneDeep(barCommonOption)
+  },
+  {
+    api: getJobCvExpCount,
+    title: '工作年限分布',
+    option: cloneDeep(barCommonOption)
+  },
+  {
+    api: getJobCvEduCount,
+    title: '学历分布',
+    option: cloneDeep(barCommonOption)
+  }
+])
+
+const getStatistics = () => {
+  list.value.forEach(async (e) => {
+    const data = await e.api(props.query)
+    if (e.isPie) {
+      e.option.series[0].data = data
+    } else {
+      e.option.title.text = e.title
+      e.option.xAxis.data = data.x
+      e.option.series[0].data = data.y
+    }
+  })
+}
+
+onMounted(() => {
+  nextTick(() => {
+    getStatistics()
+  })
+})
+
+watch(
+  () => props.query,
+  (val) => {
+    if (val) getStatistics()
+  },
+  { deep: true }
+)
 </script>
 
 <style scoped lang="scss">

+ 35 - 6
src/views/recruit/enterprise/statistics/overallAnalysis.vue

@@ -4,13 +4,13 @@
       <div class="d-flex align-center">
         <span>选择时间</span>
         <div class="ml-5 after">
-          <span v-for="(k, i) in list" :key="i" :class="['item', { 'active': current === (i + 1) }]" @click="current = i + 1 ">{{ k }}</span>
+          <span v-for="(k, i) in list" :key="i" :class="['item', { 'active': current === i }]" @click="handleClickType(i)">{{ k.label }}</span>
         </div>
       </div>
       <div class="d-flex align-center ml-15">
         <span>自定义日期</span>
         <div class="ml-5">
-          <date-picker v-model="date" :options="{ range: true, clearable: true, placeholder: '请选择要查看的时间范围' }"></date-picker>
+          <date-picker v-model="date" :options="{ range: true, clearable: true, placeholder: '请选择要查看的时间范围', change: handleChangeDate, format: 'timestamp' }"></date-picker>
         </div>
       </div>
     </div>
@@ -24,7 +24,7 @@
       <v-tabs class="mb-5" v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa">
         <v-tab :value="1">应聘简历分析</v-tab>
       </v-tabs>
-      <ResumeAnalysis></ResumeAnalysis>
+      <ResumeAnalysis :query="query"></ResumeAnalysis>
     </div>
     <div>
       <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa">
@@ -37,15 +37,44 @@
 
 <script setup>
 defineOptions({ name: 'overallAnalysis'})
-import { ref } from 'vue'
+import { reactive, ref } from 'vue'
 import Overview from './components/overview.vue'
 import DailyPage from './components/daily.vue'
 import ResumeAnalysis from './components/resume.vue'
+import { convertTimestampsToDayRange } from '@/utils/date'
 
 const tab = ref(1)
 const date = ref(null)
-const current = ref(1)
-const list = ['最近7天', '上个月', '上季度']
+const current = ref(0)
+const query = reactive({
+  type: 0,
+  time: null
+})
+const list = [
+  { label: '最近7天', value: 0 },
+  { label: '上个月', value: 1 },
+  { label: '上季度', value: 2 }
+]
+
+// 类型选择
+const handleClickType = (i) => {
+  current.value = i
+  query.time = null
+  date.value = null
+  query.type = list[i].value
+}
+
+// 时间范围选择
+const handleChangeDate = (time) => {
+  if (!time) {
+    query.type = 0
+    query.time = null
+    return
+  }
+  current.value = null
+  query.type = 99
+  query.time = convertTimestampsToDayRange(time)
+}
 
 </script>