瀏覽代碼

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

lifanagju_citu 1 年之前
父節點
當前提交
bdf1ab9532

+ 22 - 0
src/api/resume.js

@@ -28,4 +28,26 @@ export const getResumeTrainExp = async () => {
   return await request.get({
     url: '/app-api/menduner/system/person/resume/get/train/exp'
   })
+}
+
+// 保存项目经历
+export const saveResumeProjectExp = async (data) => {
+  return await request.post({
+    url: '/app-api/menduner/system/person/resume/save/project/exp',
+    data
+  })
+}
+
+// 删除项目经历
+export const deleteResumeProjectExp = async (id) => {
+  return await request.delete({
+    url: '/app-api/menduner/system/person/resume/remove/project/exp?id=' + id
+  })
+}
+
+// 获取项目经历
+export const getResumeProjectExp = async () => {
+  return await request.get({
+    url: '/app-api/menduner/system/person/resume/get/project/exp'
+  })
 }

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

@@ -5,7 +5,7 @@
       :rules="item.rules"
       variant="outlined"
       :label="item.label"
-      :counter="item.counter"
+      :counter="item.counter || 200"
       :bg-color="item.bgColor"
       :color="item.color || 'primary'"
       :validate-on="item.validateOn"

+ 3 - 3
src/layout/personal/navBar.vue

@@ -42,9 +42,9 @@
               <template v-slot:activator="{ props }">
                 <div class="d-flex ml-5 pl-2 align-center cursor-pointer" v-bind="props" @click="handleToPersonalCenter">
                   <v-avatar>
-                    <v-img alt="John" :src="baseInfo.avatar ?? 'https://cdn.vuetifyjs.com/images/john.jpg'"></v-img>
+                    <v-img alt="John" :src="baseInfo?.avatar ?? 'https://cdn.vuetifyjs.com/images/john.jpg'"></v-img>
                   </v-avatar>
-                  <div class="ml-2">{{ baseInfo.name ?? $t('sys.tourist') }}</div>
+                  <div class="ml-2">{{ baseInfo?.name ?? $t('sys.tourist') }}</div>
                 </div>
               </template>
 
@@ -111,7 +111,6 @@ defineProps({
 
 const localeStore = useLocaleStore()
 const userStore = useUserStore()
-const baseInfo = userStore.baseInfo // 人才信息
 
 const list = ref([
   { text: '首页', path: '/home' },
@@ -153,6 +152,7 @@ const items = ref([
   { title: '切换为招聘者', icon: 'mdi-swap-horizontal', change: changeRole },
   { title: '退出登录', icon: 'mdi-logout', change: handleLogout }
 ])
+const baseInfo = JSON.parse(localStorage.getItem('baseInfo')) // 人才信息
 
 const handleLogin = () => {
   router.push({ path: '/login' })

+ 2 - 1
src/locales/en.js

@@ -70,6 +70,7 @@ export default {
     personalAdvantages: 'Personal Advantages',
     jobIntention: 'Job Intention',
     trainingExperience: 'Training Experience',
-    educationExp: 'Educational Experience'
+    educationExp: 'Educational Experience',
+    projectExperience: 'Project Experience'
   }
 }

+ 2 - 1
src/locales/zh-CN.js

@@ -70,6 +70,7 @@ export default {
     personalAdvantages: '个人优势',
     jobIntention: '求职意向',
     trainingExperience: '培训经历',
-    educationExp: '教育经历'
+    educationExp: '教育经历',
+    projectExperience: '项目经历'
   }
 }

+ 1 - 1
src/views/Home/personal/components/hotPromotedPositions.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#fff" @click="getPositionList">
+    <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#fff" @update:model-value="getPositionList">
       <v-tab :value="1">{{ $t('position.recommend') }}</v-tab>
       <v-tab :value="2">{{ $t('position.latest') }}</v-tab>
       <v-tab :value="3">{{ $t('position.urgent') }}</v-tab>

+ 157 - 0
src/views/resume/components/projectExperience.vue

@@ -0,0 +1,157 @@
+<template>
+  <div class="resume-box">
+    <div class="resume-header">
+      <div class="resume-title">{{ $t('resume.projectExperience') }}</div>
+      <v-btn variant="text" color="primary" prepend-icon="mdi-plus-box" @click="isEdit = true; type = 'add'">{{ $t('common.add') }}</v-btn>
+    </div>
+    <div v-if="isEdit" class="edit">
+      <h4 class="label-title my-3 mx-2"> {{ type === 'add' ? $t('common.add') : $t('common.edit') }}{{ $t('resume.projectExperience') }}</h4>
+      <CtForm ref="formPageRef" :items="items" style="width: 100%;"></CtForm>
+      <div class="text-end">
+        <v-btn class="half-button mr-3" variant="tonal" @click="isEdit = false; type = ''">{{ $t('common.cancel') }}</v-btn>
+        <v-btn color="primary" class="half-button" @click="handleSave">{{ $t('common.save') }}</v-btn>
+      </div>
+    </div>
+    <div v-else v-for="(k, i) in projectExp" :key="i" class="exp mx-n2" @mouseenter="k.active = true" @mouseleave="k.active = false">
+      <span class="float-right" v-if="k.active">
+        <v-btn variant="text" color="primary" prepend-icon="mdi-square-edit-outline" @click="handleEdit(k)">{{ $t('common.edit') }}</v-btn>
+        <v-btn variant="text" color="primary" prepend-icon="mdi-trash-can-outline" @click="handleDelete(k)">{{ $t('common.delete') }}</v-btn>
+      </span>
+      <div v-for="(val, index) in desc" :key="index" class="my-1">
+        <span class="label-title">{{ val.label }}</span>
+        <span v-if="Array.isArray(val.value)">{{ timesTampChange(k[val.value[0]]).slice(0, 10) }} 至 {{ timesTampChange(k[val.value[1]]).slice(0, 10) }}</span>
+        <span>{{ k[val.value] }}</span>
+      </div>
+      <div v-if="i !== projectExp.length -1" class="border-bottom-dashed mt-3"></div>
+    </div>
+  </div>
+</template>
+
+<script setup name="projectExperience">
+import { ref, shallowRef  } from 'vue'
+import { getTimeStamp, timesTampChange } from '@/utils/date'
+import { saveResumeProjectExp, getResumeProjectExp, deleteResumeProjectExp } from '@/api/resume'
+import CtForm from '@/components/CtForm'
+import Snackbar from '@/plugins/snackbar'
+import Confirm from '@/plugins/confirm'
+
+const isEdit = ref(false)
+const formPageRef = ref()
+const type = ref('')
+const editId = ref(null)
+const items = ref({
+  options: [
+    {
+      type: 'text',
+      key: 'name',
+      value: '',
+      label: '项目名称 *',
+      rules: [v => !!v || '请输入项目名称']
+    },
+    {
+      type: 'datepicker',
+      key: 'startTime',
+      value: shallowRef(null),
+      col: 6,
+      label: '项目开始时间 *',
+      flexStyle: 'mr-3',
+      rules: [v => !!v || '请选择项目开始时间']
+    },
+    {
+      type: 'datepicker',
+      key: 'endTime',
+      value: shallowRef(null),
+      col: 6,
+      label: '项目结束时间 *',
+      rules: [v => !!v || '请选择项目结束时间']
+    },
+    {
+      type: 'textarea',
+      key: 'content',
+      value: '',
+      label: '项目描述 *',
+      counter: '500',
+      rules: [
+        value => {
+          if (value) return true
+          return '请输入项目描述'
+        },
+        value => {
+          if (value?.length <= 500) return true
+          return '请输入2-200个字符'
+        }
+      ]
+    }
+  ]
+})
+
+// 获取项目经历
+const projectExp = ref([])
+const getResumeTrainExpData = async () => {
+  const data = await getResumeProjectExp()
+  projectExp.value = data
+}
+getResumeTrainExpData()
+
+// 保存项目经历
+const handleSave = async () => {
+  const { valid } = await formPageRef.value.formRef.validate()
+  if (!valid) return
+  const obj = {}
+  items.value.options.forEach(e => {
+    if (e.key === 'startTime' || e.key == 'endTime') obj[e.key] = shallowRef(getTimeStamp(e.value))
+    else obj[e.key] = e.value
+  })
+  if (editId.value) obj.id = editId.value
+  await saveResumeProjectExp(obj)
+  Snackbar.success('保存成功!')
+  isEdit.value = false
+  type.value = ''
+  editId.value = null
+  getResumeTrainExpData()
+}
+
+const handleEdit = (item) => {
+  editId.value = item.id
+  items.value.options.forEach(e => {
+    if (e.key === 'startTime' || e.key == 'endTime') e.value = timesTampChange(item[e.key]).slice(0, 10)
+    else e.value = item[e.key]
+  })
+  isEdit.value = true
+}
+
+// 删除项目经历
+const handleDelete = ({ id }) => {
+  Confirm('系统提示', '是否确认删除此项目经历?').then(async () => {
+    await deleteResumeProjectExp(id)
+    Snackbar.success('删除成功!')
+    getResumeTrainExpData()
+  })
+}
+
+const desc = [
+  { label: '项目名称:', value: 'name' },
+  { label: '项目时间:', value: ['startTime', 'endTime'] },
+  { label: '项目描述:', value: 'content' }
+]
+</script>
+
+<style scoped lang="scss">
+.exp {
+  font-size: 15px;
+  cursor: pointer;
+  border-radius: 6px;
+  padding: 2px 10px 8px;
+  &:hover {
+    background-color: #f8f8f8;
+  }
+}
+.label-title {
+  color: #666;
+}
+.edit {
+  background-color: #f8f8f8;
+  padding: 2px 10px 8px;
+  border-radius: 6px;
+}
+</style>

+ 5 - 5
src/views/resume/components/trainingExperience.vue

@@ -44,7 +44,7 @@ const items = ref({
     {
       type: 'text',
       key: 'orgName',
-      value: '海外产品培训中心',
+      value: '',
       col: 6,
       label: '培训中心 *',
       flexStyle: 'mr-3',
@@ -53,7 +53,7 @@ const items = ref({
     {
       type: 'text',
       key: 'course',
-      value: '产品设计与分析',
+      value: '',
       col: 6,
       label: '培训课程 *',
       rules: [v => !!v || '请输入培训课程']
@@ -61,7 +61,7 @@ const items = ref({
     {
       type: 'datepicker',
       key: 'startTime',
-      value: shallowRef('2024-04-01'),
+      value: shallowRef(null),
       col: 6,
       label: '培训开始时间 *',
       flexStyle: 'mr-3',
@@ -70,7 +70,7 @@ const items = ref({
     {
       type: 'datepicker',
       key: 'endTime',
-      value: shallowRef('2024-04-30'),
+      value: shallowRef(null),
       col: 6,
       label: '培训结束时间 *',
       rules: [v => !!v || '请选择培训结束时间']
@@ -78,7 +78,7 @@ const items = ref({
     {
       type: 'textarea',
       key: 'content',
-      value: '同时具备常用大数据解决方案实践经验,包括实时计算(Storm/Flink )及离线计算(Hive/ETL/Spark)。 对搜索推荐/订单交易/经营分析等业务方向具备多年实践经验。 ',
+      value: '',
       label: '培训描述 *',
       rules: [
         value => {

+ 0 - 41
src/views/resume/dynamic/left.vue

@@ -1,41 +0,0 @@
-<template>
-  <div>
-    <v-card class="mx-auto" max-width="300">
-    <v-list>
-      <v-list-subheader class="title">简历目录</v-list-subheader>
-      <v-list-item v-for="(item, i) in items" :key="i" :value="item" color="primary">
-        <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>
-    </v-list>
-  </v-card>
-  </div>
-</template>
-
-<script setup>
-defineOptions({ name: 'resume-left'})
-
-const items = [
-    { text: '基本信息', icon: 'mdi-account-outline' },
-    { text: '个人优势', icon: 'mdi-account-star-outline' },
-    { text: '求职意向', icon: 'mdi-briefcase-variant-outline' },
-    { text: '教育经历', icon: 'mdi-school-outline' },
-    { text: '工作经历', icon: 'mdi-ballot-outline' },
-    { text: '项目经历', icon: 'mdi-card-text-outline' },
-    { text: '培训经历', icon: 'mdi-flag-outline' },
-    { text: '职业技能', icon: 'mdi-star-check-outline' }
-  ]
-</script>
-
-<style scoped lang="scss">
-.title {
-  color: #333;
-  font-weight: 600;
-  font-size: 20px;
-}
-</style>

+ 0 - 24
src/views/resume/dynamic/right.vue

@@ -1,24 +0,0 @@
-<template>
-  <div>
-    <basicInfo></basicInfo>
-    <selfEvaluation class="my"></selfEvaluation>
-    <jobIntention></jobIntention>
-    <educationExp class="my"></educationExp>
-    <trainingExperience></trainingExperience>
-  </div>
-</template>
-
-<script setup>
-defineOptions({ name: 'resume-right'})
-import basicInfo from '../components/basicInfo.vue'
-import selfEvaluation from '../components/selfEvaluation.vue'
-import jobIntention from '../components/jobIntention.vue'
-import trainingExperience from '../components/trainingExperience.vue'
-import educationExp from '../components/educationExp.vue'
-</script>
-
-<style scoped lang="scss">
-.my {
-  margin: 12px 0;
-}
-</style>

+ 63 - 9
src/views/resume/index.vue

@@ -1,21 +1,75 @@
 <template>
   <div class="default-width d-flex pt-3">
-    <LeftPage class="left mr-3"></LeftPage>
-    <RightPage class="right"></RightPage>
+    <div class="mr-3 left">
+      <v-card width="240" max-width="240" height="440">
+        <v-list>
+          <v-list-subheader class="title">简历目录</v-list-subheader>
+          <v-list-item v-for="(item, i) in items" :key="i" :value="item" 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>
+        </v-list>
+      </v-card>
+    </div>
+    <!-- <div class="right">
+      <basicInfo id="basicInfo"></basicInfo>
+      <selfEvaluation class="my" id="selfEvaluation"></selfEvaluation>
+      <jobIntention id="jobIntention"></jobIntention>
+      <educationExp class="my" id="educationExp"></educationExp>
+      <projectExperience id="projectExperience"></projectExperience>
+      <trainingExperience class="my" id="trainingExperience"></trainingExperience>
+    </div> -->
+    <div>
+      <component
+        v-for="item in comList"
+        :key="item.id"
+        ref="component"
+        class="mb-3"
+        :is="item.path"
+        :id="item.id"
+      />
+    </div>
   </div>
 </template>
 
 <script setup>
 defineOptions({ name: 'resume-index' })
-import LeftPage from './dynamic/left.vue'
-import RightPage from './dynamic/right.vue'
+import basicInfo from './components/basicInfo.vue'
+import selfEvaluation from './components/selfEvaluation.vue'
+import jobIntention from './components/jobIntention.vue'
+import trainingExperience from './components/trainingExperience.vue'
+import educationExp from './components/educationExp.vue'
+import projectExperience from './components/projectExperience.vue'
+
+const comList = [
+  { path: basicInfo, id: 'resumeBasicInfo' },
+  { path: selfEvaluation, id: 'resumeSelfEvaluation' },
+  { path: jobIntention, id: 'resumeJobIntention' },
+  { path: trainingExperience, id: 'resumeTrainingExperience' },
+  { path: educationExp, id: 'resumeEducationExp' },
+  { path: projectExperience, id: 'resumeProjectExperience' }
+]
+const items = [
+  { text: '基本信息', icon: 'mdi-account-outline', id: 'basicInfo' },
+  { text: '个人优势', icon: 'mdi-account-star-outline', id: 'selfEvaluation' },
+  { text: '求职意向', icon: 'mdi-briefcase-variant-outline', id: 'jobIntention' },
+  { text: '教育经历', icon: 'mdi-school-outline', id: 'educationExp' },
+  { text: '工作经历', icon: 'mdi-ballot-outline', id: '' },
+  { text: '项目经历', icon: 'mdi-card-text-outline', id: 'projectExperience' },
+  { text: '培训经历', icon: 'mdi-flag-outline', id: 'trainingExperience' },
+  { text: '职业技能', icon: 'mdi-star-check-outline', id: '' }
+]
 </script>
 
 <style lang="scss" scoped>
-.left {
-  width: 272px;
-}
-.right {
-  flex: 1;
+.title {
+  color: #333;
+  font-weight: 600;
+  font-size: 20px;
 }
 </style>