Преглед на файлове

职位批量操作&文件预览

Xiao_123 преди 11 месеца
родител
ревизия
ac90423ce9

+ 4 - 0
.env.development

@@ -5,6 +5,10 @@ VITE_APP_TITLE = 门墩儿直聘
 # 请求路径
 VITE_BASE_URL = 'http://menduner.citupro.com:7878'
 
+# 预览路径
+VITE_PREVIEW_URL = 'http://192.168.3.91:8012'
+
+# 租户id
 VITE_TENANTCODE = '155'
 
 # 是否删除debugger

+ 4 - 0
.env.localDev

@@ -5,6 +5,10 @@ VITE_APP_TITLE = 门墩儿直聘
 # 请求路径
 VITE_BASE_URL = 'http://192.168.3.80'
 
+# 预览路径
+VITE_PREVIEW_URL = 'http://192.168.3.91:8012'
+
+# 租户id
 VITE_TENANTCODE = '155'
 
 # 是否删除debugger

+ 6 - 0
package-lock.json

@@ -11,6 +11,7 @@
         "@mdi/font": "7.0.96",
         "@vuepic/vue-datepicker": "^8.7.0",
         "axios": "^1.6.8",
+        "js-base64": "^3.7.7",
         "js-cookie": "^3.0.5",
         "lodash": "^4.17.21",
         "nprogress": "^0.2.0",
@@ -2088,6 +2089,11 @@
       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
       "dev": true
     },
+    "node_modules/js-base64": {
+      "version": "3.7.7",
+      "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.7.tgz",
+      "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="
+    },
     "node_modules/js-cookie": {
       "version": "3.0.5",
       "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz",

+ 1 - 0
package.json

@@ -12,6 +12,7 @@
     "@mdi/font": "7.0.96",
     "@vuepic/vue-datepicker": "^8.7.0",
     "axios": "^1.6.8",
+    "js-base64": "^3.7.7",
     "js-cookie": "^3.0.5",
     "lodash": "^4.17.21",
     "nprogress": "^0.2.0",

+ 3 - 3
src/api/position.js

@@ -171,20 +171,20 @@ export const getJobDetails = async (params) => {
 // 招聘端-刷新职位
 export const refreshJobAdvertised = async (id) => {
   return await request.post({
-    url: `/app-admin-api/menduner/system/job-advertised/refresh?id=${id}`
+    url: `/app-admin-api/menduner/system/job-advertised/refresh?ids=${id}`
   })
 }
 
 // 招聘端-开启职位
 export const enableJobAdvertised = async (id) => {
   return await request.post({
-    url: `/app-admin-api/menduner/system/job-advertised/enable?id=${id}`
+    url: `/app-admin-api/menduner/system/job-advertised/enable?ids=${id}`
   })
 }
 
 // 招聘端-关闭职位
 export const closeJobAdvertised = async (id) => {
   return await request.post({
-    url: `/app-admin-api/menduner/system/job-advertised/disable?id=${id}`,
+    url: `/app-admin-api/menduner/system/job-advertised/disable?ids=${id}`,
   })
 }

+ 7 - 0
src/utils/index.js

@@ -35,4 +35,11 @@ export const checkIsImage = (url) => {
   }
 
   return null
+}
+
+// 预览文件
+import { Base64 } from 'js-base64'
+export const previewFile = (url) => {
+  const baseUrl = import.meta.env.VITE_PREVIEW_URL
+  window.open(`${baseUrl}/onlinePreview?url=${encodeURIComponent(Base64.encode(url))}`)
 }

+ 2 - 9
src/views/PersonalCenter/dynamic/right.vue

@@ -27,7 +27,7 @@
       <div class="d-flex attachment-item my-2 cursor-pointer" v-for="k in attachmentList" :key="k.id">
         <v-icon color="primary">mdi-file-account</v-icon>
         <div class="file-name ellipsis ml-2">{{ k.title }}</div>
-        <v-icon color="primary" @click="handlePreview(k.url)">mdi-eye-outline</v-icon>
+        <v-icon color="primary" @click="previewFile(k.url)">mdi-eye-outline</v-icon>
         <v-icon class="mx-2" color="primary" @click="handleDownload(k)">mdi-download-box-outline</v-icon>
         <v-icon color="error" @click="handleDelete(k)">mdi-trash-can-outline</v-icon>
       </div>
@@ -39,6 +39,7 @@
 defineOptions({ name: 'personal-center-right'})
 import { ref } from 'vue'
 import { uploadFile } from '@/api/common'
+import { previewFile } from '@/utils'
 import { getPersonResumeCv, savePersonResumeCv, deletePersonResumeCv } from '@/api/resume'
 import { useI18n } from '@/hooks/web/useI18n'
 import Snackbar from '@/plugins/snackbar'
@@ -126,14 +127,6 @@ const handleDownload = (k) => {
     saveAs(blob, k.title)
   })
 }
-
-// 预览
-const handlePreview = (url) => {
-  var baseUrl = "https://view.xdocin.com/view?src="
-  baseUrl += encodeURIComponent(url)
-  window.open(baseUrl)
-}
-
 </script>
 
 <style scoped lang="scss">

+ 23 - 43
src/views/enterprise/positionManagement/components/item.vue

@@ -2,9 +2,9 @@
   <div>
     <div v-if="props.items.length" class="d-flex align-center mb-1">
       <v-checkbox v-model="selectAll" :label="!selectAll ? '全选' : `已选中${selectList.length}条`" hide-details color="primary" @update:model-value="handleChangeSelectAll"></v-checkbox>
-      <v-btn v-if="tab === 1" class="ml-8" :disabled="!selectAll" @click="handleRefreshAll" color="primary" variant="tonal">刷新</v-btn>
-      <v-btn v-if="tab === 1" class="ml-3" :disabled="!selectAll" color="primary" variant="tonal">关闭</v-btn>
-      <v-btn v-if="tab === 2" class="ml-8" color="primary" variant="tonal">激活</v-btn>
+      <v-btn v-if="tab === 1" class="ml-8" :disabled="!selectAll" @click="handleAction(2, 'batch', {})" color="primary" variant="tonal" size="small">刷新</v-btn>
+      <v-btn v-if="tab === 1" class="ml-3" :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(0, 'close', {})">关闭</v-btn>
+      <v-btn v-if="tab === 2" class="ml-8" :disabled="!selectAll" color="primary" variant="tonal" size="small" @click="handleAction(1, 'activation', {})">激活</v-btn>
     </div>
     <div v-for="val in items" :key="val.id" class="itemBox mb-3">
       <div class="d-flex justify-space-between pa-5">
@@ -29,16 +29,16 @@
           </div>
         </div>
         <div class="d-flex align-center">
-          <div class="resume" v-if="val.count && val.count > '0'" @click="handleToReceiveResume">
+          <!-- <div class="resume" v-if="val.count && val.count > '0'">
             <div class="resume-number">{{ val.count }}</div>
             <div>待筛选简历</div>
-          </div>
+          </div> -->
           <div v-if="tab === 1">
             <v-btn color="primary" variant="tonal">人才搜索</v-btn>
-            <v-btn class="ml-3" color="primary" @click="handleRefresh(val)">刷新职位</v-btn>
+            <v-btn class="ml-3" color="primary" @click="handleAction(2, '', val)">刷新职位</v-btn>
           </div>
           <div v-if="tab === 2">
-            <v-btn color="primary" @click="handleActivation(val)">激活职位</v-btn>
+            <v-btn color="primary" @click="handleAction(1, '', val)">激活职位</v-btn>
           </div>
         </div>
       </div>
@@ -46,7 +46,7 @@
         <div>刷新时间:{{ timesTampChange(val.updateTime).slice(0, 10) }} {{ val.expireDay && Number(val.expireDay) >= 1 ? `(${ val.expireDay }天后到期)` : '' }}</div>
         <div class="d-flex">
           <div class="ml-10">
-            <span class="cursor-pointer" v-if="tab === 1" @click="handleClose(val)">{{ $t('common.close') }}</span>
+            <span class="cursor-pointer" v-if="tab === 1" @click="handleAction(0, '', val)">{{ $t('common.close') }}</span>
             <span class="lines" v-if="tab === 1"></span>
             <span class="cursor-pointer" @click="handleToStatistics">{{ $t('position.recruitmentStatistics') }}</span>
             <span v-if="tab !== 3" class="lines"></span>
@@ -64,7 +64,6 @@ import { defineEmits, ref, watch } from 'vue'
 import { useRouter } from 'vue-router'
 import { timesTampChange } from '@/utils/date'
 import { closeJobAdvertised, enableJobAdvertised, refreshJobAdvertised } from '@/api/position'
-import Confirm from '@/plugins/confirm'
 import Snackbar from '@/plugins/snackbar'
 
 const emit = defineEmits(['refresh'])
@@ -121,37 +120,22 @@ watch(
   { deep: true }
 )
 
-// 批量刷新
-const handleRefreshAll = () => {
-  // const selectList = props.items.filter(e => e.select).map(k => k.id)
-  // console.log(selectList, 'list')
-}
-
-// 职位关闭
-const handleClose = ({ id }) => {
-  Confirm('系统提示', '是否确认关闭此职位?').then(async () => {
-    await closeJobAdvertised(id)
-    Snackbar.success('关闭成功')
-    emit('refresh')
-  })
-}
-
-// 职位激活
-const handleActivation = ({ id }) => {
-  Confirm('系统提示', '是否确认激活此职位?').then(async () => {
-    await enableJobAdvertised(id)
-    Snackbar.success('激活成功')
-    emit('refresh')
-  })
-}
+const apiList = [
+  { api: closeJobAdvertised, desc: '关闭成功' },
+  { api: enableJobAdvertised, desc: '激活成功' },
+  { api: refreshJobAdvertised, desc: '刷新成功' }
+]
 
-// 职位刷新
-const handleRefresh = ({ id }) => {
-  Confirm('系统提示', '是否确认刷新此职位?').then(async () => {
-    await refreshJobAdvertised(id)
-    Snackbar.success('刷新成功')
-    emit('refresh')
-  })
+// 职位关闭、激活、刷新
+const handleAction = async (index, type, { id }) => {
+  const ids = type ? props.items.filter(e => e.select).map(k => k.id) : [id]
+  if (!ids.length && !index) return
+  await apiList[index].api(ids)
+  Snackbar.success(apiList[index].desc)
+  // 清空选项
+  selectList.value = []
+  selectAll.value = false
+  emit('refresh')
 }
 
 const router = useRouter()
@@ -165,10 +149,6 @@ const handleDetails = (val) => {
   window.open(`/enterprise/position/details/${val.id}`)
 }
 
-// 跳转简历管理-收到的简历
-const handleToReceiveResume = () => {
-  router.push('/enterprise/resumeManagement/receive')
-}
 // 跳转招聘统计
 const handleToStatistics = () => {
   router.push('/enterprise/statistics/overallAnalysis')

+ 3 - 2
src/views/enterprise/positionManagement/index.vue

@@ -72,8 +72,9 @@ const handleAdd = () => {
 // 获取职位列表
 const getPositionList = async () => {
   query.value.hasExpiredData = tab.value === 3 ? true : false
-  if (tab.value === 2) query.value.status = 1
-  else delete query.value.status
+  if (tab.value !== 3) {
+    query.value.status = tab.value === 1 ? 0 : 1
+  } else delete query.value.status
   const { list, total: number } = await getJobAdvertisedList(query.value)
   if (!list.length) {
     if (query.value.name) tipsText.value = '暂无数据,请更换关键词后再试'

+ 190 - 0
src/views/enterprise/talentPool/components/filter.vue

@@ -0,0 +1,190 @@
+<template>
+  <div class="px-5 py-3" style="position: relative;">
+    <h3 style="color: #00897B;">条件筛选</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 #areaIds="{ item }">
+        <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs">
+          <template v-slot:activator="{  props }">
+            <TextInput
+              v-model="item.value"
+              :item="item"
+              v-bind="props"
+              style="position: relative;"
+            ></TextInput>
+          </template>
+          <AreaSelect :select="[query.areaIds].filter(Boolean)" @handleAreaClick="handleArea" class="jobTypeCardBox" isSingle></AreaSelect>
+        </v-menu>
+      </template>
+      <template #residence="{ item }">
+        <v-menu :close-delay="1" :open-delay="0" v-bind="$attrs">
+          <template v-slot:activator="{  props }">
+            <TextInput
+              v-model="item.value"
+              :item="item"
+              v-bind="props"
+              style="position: relative;"
+            ></TextInput>
+          </template>
+          <AreaSelect :select="[query.residence].filter(Boolean)" @handleAreaClick="handleResidence" class="jobTypeCardBox" isSingle></AreaSelect>
+        </v-menu>
+      </template>
+    </CtForm>
+    <div class="bottom">
+      <v-divider></v-divider>
+      <div class="text-end mt-3">
+        <v-btn class="half-button mr-3" variant="tonal">取消</v-btn>
+        <v-btn class="half-button" color="primary">确定</v-btn>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'talent-filter'})
+import { ref } from 'vue'
+
+const CtFormRef = ref()
+const query = ref({})
+const formItems = ref({
+  options: [
+    {
+      type: 'ifRadio',
+      key: 'sex',
+      value: '0',
+      default: 0,
+      label: '性别',
+      width: 90,
+      dictTypeName: 'system_user_sex',
+      items: [
+        { label: '不限', value: '0' },
+        { label: '男', value: '1' },
+        { label: '女', value: '1' }
+      ]
+    },
+    {
+      type: 'ifRadio',
+      key: 'sex',
+      value: '0',
+      default: 0,
+      label: '人才分类',
+      width: 90,
+      items: [
+        { label: '不限', value: '0' },
+        { label: '默认分类', value: '1' }
+      ]
+    },
+    {
+      type: 'ifRadio',
+      key: 'type',
+      value: '0',
+      default: 0,
+      label: '简历状态',
+      width: 120,
+      items: [
+        { label: '不限', value: '0' },
+        { label: '已查看', value: '1' },
+        { label: '未查看', value: '2' },
+        { label: '已导出', value: '3' },
+        { label: '已邀请', value: '4' },
+        { label: '已回复', value: '5' },
+        { label: '有评语', value: '6' }
+      ],
+    },
+    {
+      type: 'autocomplete',
+      key: 'expType',
+      value: null,
+      default: null,
+      label: '学历',
+      col: 6,
+      outlined: true,
+      itemText: 'label',
+      flexStyle: 'mr-3',
+      itemValue: 'value',
+      dictTypeName: 'menduner_education_type',
+      items: []
+    },
+    {
+      type: 'autocomplete',
+      key: 'expType',
+      value: null,
+      default: null,
+      label: '工作经验',
+      col: 6,
+      outlined: true,
+      itemText: 'label',
+      itemValue: 'value',
+      dictTypeName: 'menduner_exp_type',
+      items: []
+    },
+    {
+      slotName: 'residence',
+      key: 'residence',
+      value: null,
+      col: 6,
+      flexStyle: 'mr-3',
+      label: '户籍地'
+    },
+    {
+      slotName: 'areaIds',
+      key: 'areaIds',
+      value: null,
+      col: 6,
+      label: '期望城市'
+    },
+    {
+      type: 'datePicker',
+      key: 'firstWorkTime',
+      value: null,
+      default: null,
+      class: 'mb-3',
+      options: {
+        type: '',
+        range: true,
+        format: 'timestamp',
+        placeholder: '加入日期',
+      }
+    },
+  ]
+})
+
+const handleReset = () => {}
+
+// 期望城市
+const handleArea = (list, name) => {
+  if (!list.length) return
+  query.value.workAreaId = list[0]
+  formItems.value.options.find(e => e.key === 'areaIds').value = name
+}
+
+// 户籍地
+const handleResidence = (list, name) => {
+  if (!list.length) return
+  query.value.residence = list[0]
+  formItems.value.options.find(e => e.key === 'residence').value = name
+}
+</script>
+
+<style scoped lang="scss">
+.reset-text {
+  font-size: 14px;
+  color: #777;
+  &: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>

+ 293 - 2
src/views/enterprise/talentPool/index.vue

@@ -1,11 +1,302 @@
 <template>
-  <div>人才库</div>
+  <v-card class="card-box pa-5">
+    <div class="d-flex justify-space-between">
+      <TextUI :item="textItem" @enter="handleEnter" @appendInnerClick="handleEnter"></TextUI>
+      <v-btn color="primary" prependIcon="mdi-filter-multiple-outline" class="half-button" variant="tonal" @click="screen = true">筛选</v-btn>
+    </div>
+    <div v-if="items.length" class="d-flex align-center" style="margin-left: 14px;">
+      <v-checkbox v-model="selectAll" :label="!selectAll ? '全选' : `已选中${selectList.length}条`" hide-details color="primary" @update:model-value="handleChangeSelectAll"></v-checkbox>
+      <v-btn class="ml-8" :disabled="!selectAll" color="primary" variant="tonal" size="small">邀请面试</v-btn>
+      <v-btn class="mx-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">简历回复</v-btn>
+      <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small">批量导出</v-btn>
+      <v-btn class="mx-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">踢出人才库</v-btn>
+      <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small">移动到回收站</v-btn>
+      <v-btn class="ml-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">加入黑名单</v-btn>
+    </div>
+    <div v-if="items.length">
+      <div v-for="val in items" :key="val.id" class="list-item mb-3">
+        <div class="top">
+          <v-checkbox class="mr-5" v-model="val.select" color="primary" density="compact" hide-details @update:model-value="handleChangeSelect"></v-checkbox>
+          <span>应聘/意向职位:{{ val.job.name }}</span>
+          <span class="mx-10">加入时间:{{ val.createTime }}</span>
+          <span>人才分类:{{ val.type }}</span>
+        </div>
+        <div class="px-5 py-3 d-flex justify-space-between align-center">
+          <div class="d-flex">
+            <v-badge
+              bordered
+              offset-x="6"
+              offset-y="44"
+              :color="val.sex ? (val.sex === '1' ? '#1867c0' : 'error') : 'error'"
+              :icon="val.sex ? (val.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'">
+              <v-avatar size="large" :image="val.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+            </v-badge>
+            <div class="ml-5">
+              <div class="user-name">{{ val.name }}</div>
+              <div class="mt-2 user-info">
+                <span v-for="(k, i) in dict" :key="k">
+                  {{ val[k] }}
+                  <span v-if="i !== dict.length - 1" class="mx-3">|</span>
+                </span>
+              </div>
+            </div>
+          </div>
+          <div>
+            <v-btn color="primary" variant="tonal">和TA聊聊</v-btn>
+            <v-btn class="ml-3" color="primary">邀请面试</v-btn>
+          </div>
+        </div>
+        <div class="d-flex mx-5 bottom">
+          <div class="experience" v-if="val.exp.length">
+            <div class="second-title">工作经验</div>
+            <v-timeline density="compact" align="start" side="end" truncate-line="both">
+              <v-timeline-item v-for="(j, i) in val.exp" :key="i" dot-color="primary" size="small">
+                <div class="timeline-item mt-1">
+                  <div>{{ j.startTime }}-{{ j.endTime }} ({{ j.year }})</div>
+                  <div class="timeline-item-name ellipsis">{{ j.name }}</div>
+                  <div class="timeline-item-name ellipsis">{{ j.jobName }}</div>
+                </div>
+              </v-timeline-item>
+            </v-timeline>
+          </div>
+          <div class="edu" v-if="val.edu.length">
+            <div class="second-title">教育经历</div>
+            <v-timeline density="compact" align="start" side="end" truncate-line="both">
+              <v-timeline-item v-for="(j, i) in val.edu" :key="i" dot-color="primary" size="small">
+                <div class="timeline-item mt-1">
+                  <div>{{ j.startTime }}-{{ j.endTime }}</div>
+                  <div class="timeline-item-name ellipsis">{{ j.name }}</div>
+                  <div class="timeline-item-name ellipsis">{{ j.major }}</div>
+                </div>
+              </v-timeline-item>
+            </v-timeline>
+          </div>
+        </div>
+      </div>
+      <CtPagination
+        :total="total"
+        :page="query.pageNo"
+        :limit="query.pageSize"
+        @handleChange="handleChangePage"
+      ></CtPagination>
+    </div>
+    <Empty v-else :message="tipsText" :elevation="false"></Empty>
+
+    <v-navigation-drawer v-model="screen" location="right" absolute temporary width="700">
+      <FilterPage></FilterPage>
+    </v-navigation-drawer>
+  </v-card>
 </template>
 
 <script setup>
 defineOptions({ name: 'enterprise-talent-pool'})
+import { ref } from 'vue'
+import TextUI from '@/components/FormUI/TextInput'
+import FilterPage from './components/filter.vue'
+
+const screen = ref(false)
+const selectAll = ref(false)
+const selectList = ref([])
+const items = ref([
+  {
+    job: {
+      name: '客房服务员'
+    },
+    createTime: '2026-11-12',
+    type: '默认分类',
+    name: '花城',
+    age: '27岁',
+    expName: '3年经验',
+    areaName: '广州',
+    id: 1,
+    sex: '2',
+    select: false,
+    eduName: '本科',
+    payName: '薪资面议',
+    avatar: 'https://cdn.vuetifyjs.com/images/john.jpg',
+    exp: [
+      {
+        startTime: '2016.05',
+        endTime: '2018.05',
+        year: '2年',
+        name: '广州辞图科技有限公司',
+        jobName: '前台'
+      },
+      {
+        startTime: '2016.05',
+        endTime: '2018.05',
+        year: '2年',
+        name: '广州辞图科技有限公司',
+        jobName: '前台'
+      }
+    ],
+    edu: [
+      {
+        startTime: '2016.05',
+        endTime: '2018.05',
+        name: '广州大学',
+        major: '酒店管理'
+      }
+    ]
+  },
+  {
+    job: {
+      name: '客房服务员'
+    },
+    createTime: '2026-11-12',
+    type: '默认分类',
+    name: '花城',
+    age: '27岁',
+    expName: '3年经验',
+    areaName: '广州',
+    id: 1,
+    sex: '2',
+    select: false,
+    eduName: '本科',
+    payName: '薪资面议',
+    avatar: 'https://cdn.vuetifyjs.com/images/john.jpg',
+    exp: [
+      {
+        startTime: '2016.05',
+        endTime: '2018.05',
+        year: '2年',
+        name: '广州辞图科技有限公司',
+        jobName: '前台'
+      },
+      {
+        startTime: '2016.05',
+        endTime: '2018.05',
+        year: '2年',
+        name: '广州辞图科技有限公司',
+        jobName: '前台'
+      }
+    ],
+    edu: [
+      {
+        startTime: '2016.05',
+        endTime: '2018.05',
+        name: '广州大学',
+        major: '酒店管理'
+      }
+    ]
+  }
+])
+const tipsText = ref('暂无数据')
+const total = ref(2)
+const query = ref({
+  pageNo: 1,
+  pageSize: 10
+})
+
+const textItem = ref({
+  type: 'text',
+  width: 600,
+  value: '',
+  label: '请输入简历姓名/职位名称',
+  appendInnerIcon: 'mdi-magnify'
+})
+const dict = ['age', 'expName', 'areaName', 'eduName', 'payName']
+
+const handleEnter = (e) => {
+  console.log(e, 'enter')
+}
+
+const dealSelect = () => {
+  selectList.value = items.value.filter(e => e.select).map(k => k.id)
+}
+
+// 全选
+const handleChangeSelectAll = () => {
+  items.value.map(k => {
+    k.select = selectAll.value
+    return k
+  })
+  dealSelect()
+}
+// 单选
+const handleChangeSelect = () => {
+  const length = items.value.filter(k => k.select).length
+  selectAll.value = length > 0 ? true : false
+  dealSelect()
+}
+
+const handleChangePage = () => {
+  // 分页获取新数据后勾选
+  selectList.value.forEach(e => {
+    const obj = items.value.find(k => k.id === e)
+    if (obj) obj.select = true
+  })
+}
 </script>
 
 <style scoped lang="scss">
-
+.card-box {
+  width: 100%;
+  height: 100%;
+}
+.list-item {
+  border: 1px solid #e5e6eb;
+}
+.top {
+  display: flex;
+  background-color: #f7f8fa;
+  height: 50px;
+  line-height: 50px;
+  font-size: 14px;
+  color: #666;
+  padding: 0 20px;
+}
+.user-name {
+  font-size: 18px;
+  font-weight: 700;
+  color: #555;
+}
+.user-info {
+  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: #666;
+  font-size: 15px;
+}
+.timeline-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+  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>