lifanagju_citu 8 месяцев назад
Родитель
Сommit
8c21a10f8f

+ 1 - 0
components.d.ts

@@ -50,6 +50,7 @@ declare module 'vue' {
     PersonalRecharge: typeof import('./src/components/personalRecharge/index.vue')['default']
     Positions: typeof import('./src/components/Enterprise/components/positions.vue')['default']
     PreviewImg: typeof import('./src/components/PreviewImg/index.vue')['default']
+    ProgressBar: typeof import('./src/components/ProgressBar/index.vue')['default']
     QrCode: typeof import('./src/components/QrCode/index.vue')['default']
     RadioGroup: typeof import('./src/components/FormUI/radioGroup/index.vue')['default']
     Recharge: typeof import('./src/components/Recharge/index.vue')['default']

+ 41 - 0
src/components/ProgressBar/index.vue

@@ -0,0 +1,41 @@
+<!-- 进度条 -->
+<template>
+  <div class="pa-3 white-bgc">
+    <div>{{ props.label }}</div>
+    <div class="d-flex align-center cursor-pointer mt-1" @click="handleClick">
+      <v-progress-linear
+        :bg-color="barBgColor"
+        :color="props.color"
+        :height="props.height"
+        min="0"
+        :max="props.total"
+        :model-value="props.num"
+        :rounded="props.rounded"
+      ></v-progress-linear>
+      <div
+        v-if="props.total !== 0"
+        class="ms-4 text-h7"
+        style="font-size: 13px;"
+      >{{ Number.isInteger(props.num/props.total*100) ? (props.num/props.total*100) : (props.num/props.total*100).toFixed(2) }}%</div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({name: 'ProgressBar-index'})
+const props = defineProps({
+  num: { type: Number, default: 1 },
+  total: { type: Number, default: 2 },
+  height: { type: Number, default: 6 },
+  color: { type: String, default: 'var(--v-primary-base)' },
+  barBgColor: { type: String, default: '#92aed9' },
+  rounded: { type: Boolean, default: true },
+  label: { type: String, default: '简历完成度' },
+  list: { type: Array, default: () => [] },
+})
+
+const handleClick = () => {}
+
+</script>
+<style lang="scss" scoped>
+</style>

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

@@ -109,8 +109,9 @@ import { useUserStore } from '@/store/user'
 import { uploadFile } from '@/api/common'
 import { getUserAvatar } from '@/utils/avatar'
 import { useI18n } from '@/hooks/web/useI18n'
-import { nextTick, ref } from 'vue';
+import { ref } from 'vue';
 defineOptions({name: 'resume-components-basicInfo'})
+const emit = defineEmits(['complete'])
 
 const { t } = useI18n()
 const userStore = useUserStore()
@@ -433,11 +434,14 @@ const deal = async (id, cityKey, provinceKey) => {
     province.value = dealReturnObj.pid || ''
   }
 }
+
+let completeStatus = false
 items.value.options.forEach((e, index) => {
   if ((index + 2) % 2 === 0) e.flexStyle = 'mr-3'
   if (e.dictTypeName) getDictData(e.dictTypeName) // 查字典set options
   // formItems回显
   const infoExist = baseInfo.value && Object.keys(baseInfo.value).length
+  if (infoExist) completeStatus = true
   if (infoExist && baseInfo.value[e.key]) e.value = baseInfo.value[e.key]
   // 日期相关
   if (e.type === 'datepicker') e.value = timesTampChange(e.value, 'Y-M-D')
@@ -451,8 +455,11 @@ items.value.options.forEach((e, index) => {
     const id = baseInfo.value[e.key]
     deal(id, e.key, 'regProvinceId')
   }
+  if (e.value === undefined || e.value === null) completeStatus = false
 })
-
+// 完成度展示
+emit('complete', { status: completeStatus, id: 'basicInfo' })
+  
 const provinceChange = (value, val, obj) => {
   let cityKey
   if (val?.key === 'workAreaProvinceId') cityKey = 'areaId'

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

@@ -58,6 +58,7 @@ import Confirm from '@/plugins/confirm'
 import { getText, dealCanBeInputtedSave, dealCanBeInputtedValueAndLabel } from '@/utils/getText'
 import { debounce } from 'lodash'
 import { nextTick, reactive, ref } from 'vue'
+const emit = defineEmits(['complete'])
 const editId = ref(null)
 const CtFormRef = ref()
 const dictItemsObj = reactive({})
@@ -210,6 +211,8 @@ formItems.value.options.forEach((e, index) => {
 const dataList = ref([])
 const getData = async () => {
   const data = await getResumeEduExp()
+  // 完成度展示
+  emit('complete', { status: Boolean(data?.length), id: 'educationExp' })
   dataList.value = data
 }
 getData()

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

@@ -106,6 +106,7 @@ import { saveResumeJobInterested, getResumeJobInterested, deleteResumeJobInteres
 import { dealJobData } from './dict'
 import { getDict } from '@/hooks/web/useDictionaries'
 import { cityToProvince } from '@/utils/areaDeal'
+const emit = defineEmits(['complete'])
 
 const isAdd = ref(false)
 const formPageRef = ref()
@@ -234,6 +235,8 @@ getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
 const interestList = ref([])
 const getJobInterested = async () => {
   const data = await getResumeJobInterested()
+  // 完成度展示
+  emit('complete', { status: Boolean(data?.length), id: 'jobIntention' })
   if (!data.length) return
   interestList.value = data
   interestList.value = dealJobData(data)

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

@@ -44,6 +44,7 @@ import { saveResumeProjectExp, getResumeProjectExp, deleteResumeProjectExp } fro
 import CtForm from '@/components/CtForm'
 import Snackbar from '@/plugins/snackbar'
 import Confirm from '@/plugins/confirm'
+const emit = defineEmits(['complete'])
 
 const isEdit = ref(false)
 const formPageRef = ref()
@@ -122,6 +123,8 @@ const handleCancel = () => {
 const projectExp = ref([])
 const getResumeTrainExpData = async () => {
   const data = await getResumeProjectExp()
+  // 完成度展示
+  emit('complete', { status: Boolean(!data?.length), id: 'projectExperience' })
   projectExp.value = data
 }
 getResumeTrainExpData()

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

@@ -32,6 +32,7 @@ import { ref } from 'vue'
 import { saveResumeAdvantage } from '@/api/recruit/personal/resume'
 import Snackbar from '@/plugins/snackbar'
 import { useUserStore } from '@/store/user'
+const emit = defineEmits(['complete'])
 
 const isEdit = ref(false)
 const advantage = ref('')
@@ -43,6 +44,8 @@ const getData = async () => {
   const baseInfo = JSON.parse(localStorage.getItem('baseInfo'))
   // if (!baseInfo) return
   advantage.value = baseInfo?.advantage || ''
+  // 完成度展示
+  emit('complete', { status: Boolean(advantage.value), id: 'selfEvaluation' })
 }
 getData()
 

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

@@ -50,6 +50,7 @@ import { saveResumeTrainExp, getResumeTrainExp, deleteResumeTrainExp } from '@/a
 import CtForm from '@/components/CtForm'
 import Snackbar from '@/plugins/snackbar'
 import Confirm from '@/plugins/confirm'
+const emit = defineEmits(['complete'])
 
 const isEdit = ref(false)
 const formPageRef = ref()
@@ -132,6 +133,8 @@ const handleCancel = () => {
 const trainExp = ref([])
 const getResumeTrainExpData = async () => {
   const data = await getResumeTrainExp()
+  // 完成度展示
+  emit('complete', { status: Boolean(data?.length), id: 'trainingExperience' })
   trainExp.value = data
 }
 getResumeTrainExpData()

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

@@ -42,6 +42,7 @@ import Snackbar from '@/plugins/snackbar'
 import Confirm from '@/plugins/confirm'
 import { getDict } from '@/hooks/web/useDictionaries'
 import { getText } from '@/utils/getText'
+const emit = defineEmits(['complete'])
 
 const isEdit = ref(false)
 const formPageRef = ref()
@@ -100,6 +101,8 @@ getSkillTreeFunc()
 const dataList = ref([])
 const getData = async () => {
   const data = await getResumePersonSkill()
+  // 完成度展示
+  emit('complete', { status: Boolean(data?.length), id: 'vocationalSkills' })
   dataList.value = data
 }
 getData()

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

@@ -74,6 +74,7 @@ import Confirm from '@/plugins/confirm'
 import { dealCanBeInputtedSave, dealCanBeInputtedValueAndLabel } from '@/utils/getText'
 // import { debounce } from 'lodash'
 import { nextTick, reactive, ref } from 'vue'
+const emit = defineEmits(['complete'])
 const editId = ref(null)
 const CtFormRef = ref()
 const dictItemsObj = reactive({})
@@ -194,6 +195,8 @@ formItems.value.options.forEach((e, index) => {
 const dataList = ref([])
 const getData = async () => {
   const data = await getResumeWorkExp()
+  // 完成度展示
+  emit('complete', { status: Boolean(data?.length), id: 'workExperience' })
   dataList.value = data
 }
 getData()

+ 26 - 7
src/views/recruit/personal/remuse/index.vue

@@ -1,19 +1,27 @@
 <template>
   <div class="default-width d-flex" ref="scrollBox">
     <div class="mr-3 pt-3">
-      <v-card id="leftCadId" width="240" max-width="240" height="440" class="positionSticky">
+      <v-card id="leftCadId" width="240" max-width="240" height="540" class="positionSticky">
         <v-list>
           <v-list-subheader class="title">简历目录</v-list-subheader>
           <v-list-item v-for="(item, i) in items" :key="i" :value="item" :disabled="!item.id" color="primary" @click="handleClick(item)">
             <template v-slot:prepend>
               <v-icon :icon="item.icon"></v-icon>
             </template>
-            <v-list-item-title v-text="item.text"></v-list-item-title>
-            <!-- <template v-slot:append>
-              <v-icon color="primary">mdi-check</v-icon>
-            </template> -->
+            <v-list-item-title>{{ item.text }}</v-list-item-title>
+            <template v-slot:append>
+              <!-- <v-icon v-if="!item.status">mdi-information-outline</v-icon> -->
+              <v-icon v-if="item.status" color="primary">mdi-check</v-icon>
+              <v-icon v-else color="error">mdi-information-outline</v-icon>
+            </template>
           </v-list-item>
         </v-list>
+        <v-divider></v-divider>
+        <ProgressBar
+          class="mt-2"
+          :num="completeNum"
+          :total="items.length"
+        ></ProgressBar>
       </v-card>
     </div>
     <div class=" pt-3">
@@ -24,6 +32,7 @@
         class="mb-3"
         :is="item.path"
         :id="item.domId"
+        @complete="complete"
       />
     </div>
   </div>
@@ -40,6 +49,7 @@ import workExperience from './components/workExperience.vue'
 import projectExperience from './components/projectExperience.vue'
 import vocationalSkills from './components/vocationalSkills.vue'
 import { useI18n } from '@/hooks/web/useI18n'
+import ProgressBar from '@/components/ProgressBar'
 const { t } = useI18n()
 import { ref } from 'vue'
 import { onMounted } from 'vue';
@@ -57,7 +67,7 @@ const comList = [
   { path: vocationalSkills, id: 'resumeVocationalSkills', domId: 'vocationalSkills' },
 ]
 
-const items = [
+const items = ref([
   { text: t('resume.basicInfo'), icon: 'mdi-account-outline', id: 'basicInfo' },
   { text: t('resume.personalAdvantages'), icon: 'mdi-account-star-outline', id: 'selfEvaluation' },
   { text: t('resume.jobIntention'), icon: 'mdi-briefcase-variant-outline', id: 'jobIntention' },
@@ -66,7 +76,7 @@ const items = [
   { text: t('resume.projectExperience'), icon: 'mdi-card-text-outline', id: 'projectExperience' },
   { text: t('resume.trainingExperience'), icon: 'mdi-flag-outline', id: 'trainingExperience' },
   { text: t('resume.vocationalSkills'), icon: 'mdi-star-check-outline', id: 'vocationalSkills' }
-]
+])
 
 onMounted(() => {
   const selector = document.getElementById('leftCadId')
@@ -84,6 +94,15 @@ const handleClick = (item) => {
     })
   }
 }
+
+const completeNum = ref(0)
+const complete = (val) => {
+  if (!val?.id) return
+  const item = items.value.find(e => e.id === val.id)
+  if (!item) return
+  if (val.status) completeNum.value++
+  item.status = val.status || false
+}
 </script>
 
 <style lang="scss" scoped>