lifanagju_citu 8 hónapja
szülő
commit
cb61a4fc8f

+ 9 - 0
src/api/recruit/enterprise/resumeManagement/talentMap/index.js

@@ -0,0 +1,9 @@
+import request from '@/config/axios'
+
+// 分页查询人才地图
+export const getRecruitPersonMapPage = async (params) => {
+  return await request.get({
+    url: '/app-api/menduner/system/recruit/person-map/page',
+    params
+  })
+}

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

@@ -81,6 +81,12 @@
                 :item="item"
                 @change="handleChange(item)"
               ></wangEditorUI>
+              <areaSelectUI
+                v-if="item.type === 'areaSelect'"
+                v-model="item.value"
+                :item="item"
+                @change="handleChange(item)"
+              ></areaSelectUI>
               <DatePicker v-if="item.type === 'vueDatePicker'" v-model="item.value" :options="item.options" :width="item.width" :class="item.class"></DatePicker>
               <v-file-input
                 v-if="item.type === 'upload'"
@@ -135,6 +141,7 @@ import nestedListGroupUI from './../FormUI/nestedListGroup'
 import datePickerUI from './../FormUI/datePicker'
 import DatePicker from '@/components/DatePicker'
 import wangEditorUI from './../FormUI/wangEditor'
+import areaSelectUI from './../FormUI/areaSelect'
 import { ref } from 'vue'
 const emit = defineEmits(['change', 'inputUpdateAutocomplete'])// 定义一个或多个自定义事件
 const props = defineProps({items: Object})

+ 61 - 0
src/components/FormUI/areaSelect/index.vue

@@ -0,0 +1,61 @@
+<!--  -->
+<template>
+  <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs" :close-on-content-click="false">
+    <template v-slot:activator="{ props }">
+      <TextInput
+        v-model="txt"
+        :item="defineProp.item"
+        v-bind="props"
+        style="position: relative;"
+      >
+        <template #default>
+          <v-chip v-for="k in areaSelect" :key="k.id" class="mr-1" closable @click:close="handleAreaClear(k)">{{ k.name }}</v-chip>
+        </template>
+      </TextInput>
+    </template>
+    <AreaSelect :select="value.value" :currentData="areaSelect" :limit="defineProp.item.limit" showSelect @handleClick="handleArea"></AreaSelect>
+  </v-menu>
+</template>
+
+<script setup>
+import TextInput from '@/components/FormUI/TextInput'
+import { ref, watch } from 'vue'
+defineOptions({ name:'FormUI-areaSelect'})
+const emit = defineEmits(['update:modelValue', 'change', 'search'])
+const defineProp = defineProps({item: Object, modelValue: [String, Array]})
+
+const txt = ''
+const value = ref()
+let areaSelect = ref([])
+watch(
+  () => defineProp.modelValue,
+  (newVal) => {
+    value.value = newVal ? (typeof newVal === 'string') ? [newVal] : newVal : [] // 转数组
+    if (value.value?.length === 0) areaSelect.value = []
+  },
+  { immediate: true },
+  { deep: true }
+)
+const modelValueUpDate = (val) => {
+  value.value = val
+  const limit1 = defineProp.item?.limit === 1
+  emit('update:modelValue', limit1 ? value.value[0] : value.value)
+  emit('change', limit1 ? value.value[0] : value.value)
+}
+
+// 其它感兴趣的城市
+const handleArea = (list, arr) => {
+  modelValueUpDate(list?.length ? list : '')
+  areaSelect.value = arr
+}
+
+const handleAreaClear = (k) => {
+  const val = value.value.filter(i => i !== k.id)
+  modelValueUpDate(val)
+  const index = areaSelect.value.findIndex(i => i.id === k.id)
+  if (index !== -1) areaSelect.value.splice(index, 1)
+}
+
+</script>
+<style lang="scss" scoped>
+</style>

+ 1 - 1
src/components/FormUI/autocomplete/index.vue

@@ -31,7 +31,7 @@ import { debounce } from 'lodash'
 import { ref, watch } from 'vue';
 defineOptions({ name:'FormUI-v-autocomplete'})
 
-const props = defineProps({item: Object, modelValue: [String, Number, Boolean]})
+const props = defineProps({item: Object, modelValue: [String, Array, Number, Boolean]})
 
 const value = ref()
 watch(

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

@@ -49,6 +49,14 @@ const enterprise = [
           hideSide: true
         }
       },
+      {
+        path: '/recruit/enterprise/resumeManagement/talentMap',
+        meta: {
+          title: '人才地图',
+          enName: 'Talent Map'
+        },
+        component: () => import('@/views/recruit/enterprise/resumeManagement/talentMap/index.vue')
+      },
       {
         path: '/recruit/enterprise/resumeManagement/elite',
         meta: {

+ 1 - 0
src/utils/position.js

@@ -23,6 +23,7 @@ const dictList = ref([
   { type: 'menduner_education_type', value: 'edu', key: 'eduType', label: 'eduName' },
   { type: 'menduner_exp_type', value: 'exp', key: 'expType', label: 'expName' },
   { type: 'menduner_area_type', value: 'area', key: 'areaId', label: 'areaName', params: {}, apiType: 'areaList', nameKey: 'name', valueKey: 'id' },
+  { type: 'menduner_area_type', value: 'reg', key: 'regId', label: 'regName', params: {}, apiType: 'areaList', nameKey: 'name', valueKey: 'id' },
   { type: 'positionData', value: 'position', key: 'positionId', label: 'positionName', params: {}, apiType: 'positionData', nameKey: 'nameCn', valueKey: 'id' }
 ])
 

+ 215 - 0
src/views/recruit/enterprise/resumeManagement/talentMap/components/data.js

@@ -0,0 +1,215 @@
+export const dataTest = {
+  list: [
+    {
+      id: "1793583467288223745", 
+      userId: "1", 
+      name: "无牙仔", 
+      sex: "1", 
+      avatar: "http://menduner.citupro.com:6868/admin-api/infra/file/24/get/241e594d4473872eabb312673f42241a2e9598298cb7d9d791cc9c8cb65fb058.jpg", 
+      phone: "13229740092", 
+      email: "citupro.com", 
+      wxCode: null, 
+      birthday: 863625600000, 
+      maritalStatus: "3", 
+      areaId: "340400", 
+      regId: "460100", 
+      jobType: "0", 
+      jobStatus: "0", 
+      firstWorkTime: 1020182400000, 
+      advantage: "advantage", 
+      expType: "0", 
+      eduType: "0", 
+      createTime: 1716487396000
+    }, 
+    {
+      id: "1793583467288223746", 
+      userId: "2", 
+      name: "垃圾数据", 
+      sex: "1", 
+      avatar: "http://menduner.citupro.com:6868/admin-api/infra/file/24/get/241e594d4473872eabb312673f42241a2e9598298cb7d9d791cc9c8cb65fb058.jpg", 
+      phone: "13229740091", 
+      email: "citupro.com", 
+      wxCode: null, 
+      birthday: 946656000000, 
+      maritalStatus: "3", 
+      areaId: "110101", 
+      regId: null, 
+      jobType: "1", 
+      jobStatus: "1", 
+      firstWorkTime: 1020182400000, 
+      advantage: "advantage", 
+      expType: "5", 
+      eduType: "2", 
+      createTime: 1716487396000
+    }, 
+    {
+      id: "1802554979923980290", 
+      userId: "3", 
+      name: "蔡", 
+      sex: "1", 
+      avatar: "http://menduner.citupro.com:6868/admin-api/infra/file/24/get/b74a994d9368418cfad012a089fe8e7352f2fe3c2974bf7a9df126b9d6c2b8be.jpeg", 
+      phone: "15774563630", 
+      email: "11@qq.com", 
+      wxCode: null, 
+      birthday: 1718553600000, 
+      maritalStatus: "3", 
+      areaId: "110101", 
+      regId: null, 
+      jobType: "0", 
+      jobStatus: "2", 
+      firstWorkTime: 1732982400000, 
+      advantage: null, 
+      expType: "7", 
+      eduType: "6", 
+      createTime: 1718597572000
+    }, 
+    {
+      id: "1810882841609445377", 
+      userId: "1802537861731995650", 
+      name: "雪糕", 
+      sex: "0", 
+      avatar: "http://menduner.citupro.com:6868/admin-api/infra/file/24/get/3ddfc91539fa8edf0337be724f112d8b1ba3fbf5b5267f83208842909e08bc62.jpg", 
+      phone: "1840000003", 
+      email: "15246666@qq.com", 
+      wxCode: null, 
+      birthday: 1720627200000, 
+      maritalStatus: "0", 
+      areaId: "110105", 
+      regId: null, 
+      jobType: "0", 
+      jobStatus: "0", 
+      firstWorkTime: 1704038400000, 
+      advantage: null, 
+      expType: "0", 
+      eduType: "0", 
+      createTime: 1720583088674
+    }, 
+    {
+      id: "1810894386980671490", 
+      userId: "1802587750646571010", 
+      name: "姓名", 
+      sex: "0", 
+      avatar: "", 
+      phone: "1840000003", 
+      email: "", 
+      wxCode: null, 
+      birthday: null, 
+      maritalStatus: null, 
+      areaId: null, 
+      regId: null, 
+      jobType: null, 
+      jobStatus: "0", 
+      firstWorkTime: null, 
+      advantage: "测试账号", 
+      expType: "0", 
+      eduType: "0", 
+      createTime: 1720585841311
+    }, 
+    {
+      id: "1810895855691714562", 
+      userId: "1810895787827875842", 
+      name: "123", 
+      sex: "0", 
+      avatar: "", 
+      phone: "18400000004", 
+      email: "", 
+      wxCode: null, 
+      birthday: null, 
+      maritalStatus: null, 
+      areaId: null, 
+      regId: null, 
+      jobType: null, 
+      jobStatus: "0", 
+      firstWorkTime: null, 
+      advantage: null, 
+      expType: "0", 
+      eduType: "5", 
+      createTime: 1720586191484
+    }, 
+    {
+      id: "1810897010463297537", 
+      userId: "1810857066626179074", 
+      name: "安琪", 
+      sex: "2", 
+      avatar: "http://menduner.citupro.com:6868/admin-api/infra/file/24/get/933de4dc9eb28e2bdf79a1da518a8e1a0e14bf4b78358ea9452fb8e8fdd2f7f6.jpeg", 
+      phone: "13229740092", 
+      email: "0092@qq.com", 
+      wxCode: null, 
+      birthday: 647622000000, 
+      maritalStatus: "0", 
+      areaId: "440100", 
+      regId: null, 
+      jobType: "0", 
+      jobStatus: "0", 
+      firstWorkTime: 1462032000000, 
+      advantage: "advantage", 
+      expType: "4", 
+      eduType: "4", 
+      createTime: 1720586466794
+    }, 
+    {
+      id: "1810946471751229441", 
+      userId: "1810946290024620033", 
+      name: "林女士", 
+      sex: "0", 
+      avatar: "", 
+      phone: "13229740099", 
+      email: "", 
+      wxCode: null, 
+      birthday: null, 
+      maritalStatus: null, 
+      areaId: null, 
+      regId: null, 
+      jobType: null, 
+      jobStatus: "0", 
+      firstWorkTime: null, 
+      advantage: null, 
+      expType: "5", 
+      eduType: "3", 
+      createTime: 1720598259290
+    }, 
+    {
+      id: "1810966328299397121", 
+      userId: "1810966001873494017", 
+      name: "小七", 
+      sex: "0", 
+      avatar: "", 
+      phone: "13229742258", 
+      email: "", 
+      wxCode: null, 
+      birthday: null, 
+      maritalStatus: null, 
+      areaId: null, 
+      regId: null, 
+      jobType: null, 
+      jobStatus: "0", 
+      firstWorkTime: null, 
+      advantage: null, 
+      expType: "0", 
+      eduType: "4", 
+      createTime: 1720602993456
+    }, 
+    {
+      id: "1811241432220610562", 
+      userId: "1811241235713273857", 
+      name: "18400000006", 
+      sex: "0", 
+      avatar: "", 
+      phone: "18400000006", 
+      email: "", 
+      wxCode: null, 
+      birthday: null, 
+      maritalStatus: null, 
+      areaId: null, 
+      regId: null, 
+      jobType: null, 
+      jobStatus: "0", 
+      firstWorkTime: null, 
+      advantage: null, 
+      expType: "1", 
+      eduType: "4", 
+      createTime: 1720668583351
+    }
+  ], 
+  total: "56"
+}

+ 292 - 0
src/views/recruit/enterprise/resumeManagement/talentMap/components/filter.vue

@@ -0,0 +1,292 @@
+<template>
+  <div class="px-5 py-3" style="position: relative;">
+    <h3 class="my-3" style="color: var(--v-primary-base);">条件筛选</h3>
+    <!-- <v-divider class="my-3"></v-divider>
+    <div class="text-right reset-text cursor-pointer" @click="handleReset">重置筛选</div> -->
+    <CtForm ref="CtFormRef" :items="formItems" style="width: 100%;">
+      <!-- 期望岗位 -->
+      <template #positionId="{ item }">
+        <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs">
+          <template v-slot:activator="{  props }">
+            <textUI
+              :modelValue="item.value"
+              :item="item"
+              v-bind="props"
+              style="position: relative;"
+              @handleClear="handleJobClickItem"
+            ></textUI>
+          </template>
+          <jobTypeCard class="jobTypeCardBox" :select="[positionId].filter(Boolean)" :isSingle="true" @handleJobClick="handleJobClickItem"></jobTypeCard>
+        </v-menu>
+      </template>
+    </CtForm>
+    <div class="bottom">
+      <v-divider></v-divider>
+      <div class="d-flex justify-space-between mt-3">
+        <div class="ml-3">
+          <v-btn class="half-button mr-3" color="primary" @click="confirm">筛选</v-btn>
+          <v-btn class="half-button mr-3" variant="tonal" @click="emit('cancel')">取消</v-btn>
+        </div>
+        <v-btn class="half-button ml-3" variant="tonal" color="orange" @click="handleReset">重置</v-btn>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'talent-map-filter'})
+import jobTypeCard from '@/components/jobTypeCard'
+import textUI from '@/components/FormUI/TextInput'
+import { getDict } from '@/hooks/web/useDictionaries'
+import { ref } from 'vue'
+const emit = defineEmits(['cancel', 'confirm'])
+
+
+const CtFormRef = ref()
+const formItems = ref({
+  options: [
+    {
+      type: 'text',
+      key: 'name',
+      value: '',
+      label: '姓名 ',
+    },
+    {
+      type: 'autocomplete',
+      key: 'labels',
+      value: null,
+      label: '人员标签 ',
+      multiple: true,
+      outlined: true,
+      itemText: 'label',
+      itemValue: 'value',
+      items: [
+        { label: '标签', value: '0' },
+      ]
+    },
+    {
+      type: 'number',
+      key: 'age1',
+      value: null,
+      label: '年龄区间:起始',
+    },
+    {
+      type: 'number',
+      key: 'age2',
+      value: null,
+      label: '年龄区间:结束',
+    },
+    {
+      type: 'number',
+      key: 'pay1',
+      value: null,
+      suffix: '元',
+      label: '期望薪资:起始',
+    },
+    {
+      type: 'number',
+      key: 'pay2',
+      value: null,
+      suffix: '元',
+      label: '期望薪资:结束',
+    },
+    {
+      type: 'areaSelect',
+      key: 'areaIds',
+      value: '',
+      // label: ' ',
+      placeholder: '所在城市', // 暂时只能使用placeholder
+      readonly: true,
+      limit: 1,
+    },
+    {
+      type: 'areaSelect',
+      key: 'regIds',
+      value: '',
+      // label: ' ',
+      placeholder: '户籍所在地', // 暂时只能使用placeholder
+      readonly: true,
+      limit: 1,
+    },
+    {
+      type: 'autocomplete',
+      key: 'eduType',
+      value: null,
+      default: null,
+      label: '学历',
+      outlined: true,
+      itemText: 'label',
+      itemValue: 'value',
+      dictTypeName: 'menduner_education_type',
+      items: []
+    },
+    {
+      type: 'autocomplete',
+      key: 'expType',
+      value: null,
+      default: null,
+      label: '工作经验',
+      outlined: true,
+      itemText: 'label',
+      itemValue: 'value',
+      dictTypeName: 'menduner_exp_type',
+      items: []
+    },
+    {
+      slotName: 'positionId',
+      key: 'positionName',
+      value: '',
+      label: '职位类型 ',
+      valueKey: 'positionId', 
+      hideDetails: true,
+      readonly: true,
+      outlined: true,
+    },
+    {
+      type: 'autocomplete',
+      key: 'jobType',
+      value: null,
+      label: '求职类型 ',
+      outlined: true,
+      itemText: 'label',
+      itemValue: 'value',
+      items: [
+        { label: '全职', value: '0' },
+        { label: '兼职', value: '1' },
+        { label: '临时', value: '2' },
+        { label: '实习', value: '3' }
+      ]
+    },
+    {
+      type: 'autocomplete',
+      key: 'jobStatus',
+      value: null,
+      default: null,
+      label: '求职状态 ',
+      outlined: true,
+      itemText: 'label',
+      itemValue: 'value',
+      dictTypeName: 'menduner_job_status',
+      items: []
+    },
+    {
+      type: 'autocomplete',
+      key: 'maritalStatus',
+      value: null,
+      default: null,
+      label: '婚姻状况 ',
+      outlined: true,
+      itemText: 'label',
+      itemValue: 'value',
+      dictTypeName: 'menduner_marital_status',
+      items: []
+    },
+  ]
+})
+
+
+// 获取字典内容
+const getDictData = async (item) => {
+  if (item) {
+    const { data } = await getDict(item.dictTypeName)
+    item.items = data
+  }
+}
+formItems.value.options.forEach((e, index) => {
+  // 查字典set options
+  if (e.dictTypeName) getDictData(e)
+  // 样式
+  e.col = 6
+  e.clearable = true
+  if ((index + 2) % 2 === 0 && !e.flexStyle) e.flexStyle = 'mr-3'
+})
+
+// const setOneValue = (key, value) => {
+//   formItems.value.options.find(e => e.key === key).value = value
+// }
+
+// 期望职位
+let positionId = ''
+let positionName = ''
+const handleJobClickItem = (list, name) => {
+  const positionItem = formItems.value.options.find(f => f.key === 'positionName')
+  if (positionItem) {
+    if (list?.length) {
+      positionItem.value = positionName = name || ''
+      positionItem[positionItem.valueKey] = positionId = list?.length ? list[0] : ''
+    } else {
+      positionItem.value = name || ''
+      positionItem[positionItem.valueKey] = list?.length ? list[0] : ''
+    }
+  }
+}
+
+const confirm = () => {
+  const obj = {}
+  formItems.value.options.forEach(e => {
+    if (e.key === 'positionName') {
+      if (e.value) {
+        obj[e.valueKey] = positionId ? positionId : ''
+        obj[e.key] = positionName ? positionName : ''
+      }
+    } else if (e.value !== null && e.value !== '' && e.value !== undefined) obj[e.key] = e.value
+  })
+  if (obj.age1 || obj.age2) obj.age = [obj.age1 || null, obj.age2 || null]
+  if (obj.pay1 || obj.pay2) obj.pay = [obj.pay1 || null, obj.pay2 || null]
+  if (!obj.labels?.length) delete obj.labels
+  emit('confirm', obj)
+}
+
+const resetValue = () => {
+  formItems.value.options.forEach(e => {
+    if (e.key === 'positionName') {
+      e[e.valueKey] = ''
+      e.value = e[e.valueKey] = positionId = positionName = ''
+    } else {
+      e.value = null
+    }
+  })
+}
+const setValue = (query) => {
+  formItems.value.options.forEach(e => {
+    if (e.key === 'positionName') {
+      e.value = query[e.key]
+      e[e.valueKey] = query[e.valueKey]
+    } else {
+      if (query[e.key] !== undefined) e.value = query[e.key]
+    }
+  })
+}
+
+const handleReset = () => {
+  resetValue()
+  emit('confirm', {})
+}
+
+defineExpose({
+  setValue,
+  resetValue,
+})
+</script>
+
+<style scoped lang="scss">
+.reset-text {
+  font-size: 14px;
+  color: var(--color-666);
+  &:hover {
+    color: var(--v-primary-base);
+  }
+}
+.jobTypeCardBox {
+  position: absolute;
+  // top: -22px;
+  left: 0;
+}
+.bottom {
+  position: fixed;
+  width: 96%;
+  margin: 0 12px;
+  bottom: 14px;
+  left: 0;
+}
+</style>

+ 198 - 0
src/views/recruit/enterprise/resumeManagement/talentMap/index.vue

@@ -0,0 +1,198 @@
+<template>
+  <v-card class="card-box pa-5" style="height: 100%;">
+    <div class="d-flex justify-space-between">
+      <div></div>
+      <v-btn color="primary" prependIcon="mdi-filter-multiple-outline" class="half-button" variant="tonal" @click="openDrawer">筛选</v-btn>
+    </div>
+    <!-- 人员信息表单 -->
+    <v-data-table
+      class="mt-3"
+      :items="dataList"
+      :headers="headers"
+      hover
+      :disable-sort="true"
+      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="getUserAvatar(item.avatar, item.sex)"></v-avatar>
+          </v-badge>
+          <span class="defaultLink ml-3">{{ item?.name }}</span>
+        </div>
+      </template>
+    </v-data-table>
+    <CtPagination
+      v-if="total > 0"
+      :total="total"
+      :page="pageInfo.pageNo"
+      :limit="pageInfo.pageSize"
+      @handleChange="handleChangePage"
+    ></CtPagination>
+    <!-- <Empty v-else :message="tipsText" :elevation="false" class="mt-15"></Empty> -->
+    <v-navigation-drawer v-model="screen" location="right" absolute temporary width="1000">
+      <FilterPage
+        ref="FilterPageRef"
+        @confirm="handleConfirm"
+        @cancel="screen = false"
+      ></FilterPage>
+    </v-navigation-drawer>
+  </v-card>
+</template>
+
+<script setup>
+defineOptions({ name: 'enterprise-talent-map'})
+import { getRecruitPersonMapPage } from '@/api/recruit/enterprise/resumeManagement/talentMap'
+import { dealDictArrayData } from '@/utils/position'
+import { getUserAvatar } from '@/utils/avatar'
+import { timesTampChange } from '@/utils/date'
+import FilterPage from './components/filter.vue'
+import { dataTest } from './components/data.js'
+import { computed, reactive, ref } from 'vue'
+
+const screen = ref(false)
+const tipsText = ref('暂无数据')
+
+let query = {}
+const pageInfo = reactive({ pageNo: 1, pageSize: 10 })
+const dataList = ref([])
+const total = ref(0)
+const headers = [
+  { title: '姓名', key: 'name', sortable: false },
+  { title: '求职状态', key: 'jobStatusName', sortable: false },
+  // { title: '求职类型', key: 'jobName', sortable: false },
+  { title: '电话号码', key: 'phone', sortable: false },
+  { title: '常用邮箱', key: 'email', sortable: false },
+  // { title: '微信二维码', key: 'wxCode', sortable: false },
+  { title: '出生日期', key: 'birthday', sortable: false, value: item =>  timesTampChange(item.birthday, 'Y-M-D') },
+  { title: '婚姻状况', key: 'maritalStatusName', sortable: false },
+  { title: '所在城市', key: 'areaName', sortable: false },
+  { title: '户籍地', key: 'regName', sortable: false },
+  { title: '首次工作时间', key: 'firstWorkTime', sortable: false, value: item =>  timesTampChange(item.firstWorkTime, 'Y-M-D') },
+  { title: '个人优势', key: 'advantage', sortable: false },
+  { title: '工作年限', key: 'expName', sortable: false },
+  { title: '最高学历', key: 'eduName', sortable: false },
+]
+
+// 获取数据
+const getData = async () => {
+  const obj = { ...pageInfo, ...query }
+  console.log('getData-obj', obj)
+  // const { list, total: number } = await getRecruitPersonMapPage(query.value)
+  // console.log('dataTest', dataTest)
+  const { list, total: number } = dataTest
+  if (!list.length) {
+    if (query.value.name) tipsText.value = '暂无数据,请更换关键词后再试'
+  }
+  total.value = number
+  dataList.value = list.length ? dealDictArrayData([], list) : []
+}
+getData()
+
+const handleChangePage = (e) => {
+  pageInfo.pageNo = e
+  getData()
+}
+
+// 筛选
+const handleConfirm = (params) => {
+  screen.value = false
+  pageInfo.pageNo = 1
+  query = { ...params }
+  getData()
+}
+
+const FilterPageRef = ref()
+const openDrawer = () => {
+  screen.value = true
+  if (Object.keys(query).length) FilterPageRef.value?.setValue(query)
+  else FilterPageRef.value?.resetValue()
+}
+
+const badgeColor = computed(() => (item) => {
+  return (item && item.sex) ? (item.sex === '1' ? '#1867c0' : 'error') : 'error'
+})
+
+const badgeIcon = computed(() => (item) => {
+  return (item && item.sex) ? (item.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'
+})
+</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;
+}
+.list-item {
+  border: 1px solid #e5e6eb;
+}
+.top {
+  display: flex;
+  background-color: #f7f8fa;
+  height: 50px;
+  line-height: 50px;
+  font-size: 14px;
+  color: var(--color-666);
+  padding: 0 20px;
+}
+.user-name {
+  font-size: 18px;
+  font-weight: 700;
+  color: #555;
+}
+.user-info {
+  color: var(--color-666);
+  font-size: 14px;
+  font-weight: 500;
+}
+:deep(.v-timeline-divider__dot--size-small) {
+  width: 10px !important;
+  height: 10px !important;
+  margin-top: 10px !important;
+}
+:deep(.v-timeline-divider__inner-dot) {
+  width: 10px !important;
+  height: 10px !important;
+}
+.bottom {
+  display: flex;
+  justify-content: space-between;
+  padding-bottom: 12px;
+  .experience {
+    width: 54%;
+    height: 100%;
+  }
+  .edu {
+    width: 40%;
+    height: 100%;
+  }
+}
+.second-title {
+  color: var(--color-666);
+  font-size: 15px;
+}
+.timeline-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+  color: var(--color-666);
+  font-size: 13px;
+  .timeline-item-name {
+    width: 26%;
+  }
+}
+:deep(.v-timeline-item__body) {
+  width: 100%;
+}
+:deep(.v-timeline--vertical.v-timeline) {
+  row-gap: 0;
+}
+</style>