Explorar o código

使用简历附件生成在线简历

lifanagju_citu hai 5 meses
pai
achega
b521f94ace

+ 27 - 0
api/user.js

@@ -40,6 +40,33 @@ export const getPersonResumeCv = () => {
   })
 }
 
+// 附件简历解析2
+export const resumeParser2 = (params) => {
+  return request({
+    url: '/admin-api/menduner/system/online/resume/parser2',
+    method: 'GET',
+    params,
+    custom: {
+      showLoading: false,
+      auth: true
+    }
+  })
+}
+
+// 简历解析-保存简历信息
+export const saveResumeInfo = (data) => {
+  return request({
+    url: '/app-api/menduner/system/person/resume/save',
+    method: 'POST',
+    data,
+    custom: {
+      openEncryption: true,
+      auth: true,
+      showLoading: false
+    }
+  })
+}
+
 // 获取收藏的招聘职位列表
 export const getJobFavoriteList = (params) => {
   return request({

+ 3 - 1
pagesA/resume/index.vue

@@ -21,7 +21,7 @@
 			<image src="https://minio.citupro.com/dev/static/nodata.png" mode="widthFix" style="width: 100vw;height: 100vh;"></image>
 		</view>
     <view class="bottom-sticky flex-column">
-      <button class="recomm-button" style="margin-bottom: 0;" @click="handleResumeAnalysis">开始解析</button>
+      <button class="recomm-button" style="margin-bottom: 0;" :loading="analysisLoading" @click="handleResumeAnalysis">开始解析</button>
       <button class="recomm-button" style="margin-bottom: 0;" @click="handleUpload">微信聊天文件上传</button>
       <view class="color-primary font-size-14 ss-m-b-25 ss-m-t-10" style="text-align: center;">上传文件大小不能超过20MB</view>
     </view>
@@ -133,8 +133,10 @@ const radioChange = (index) => {
   if (bioList.value[index]?.url) fileUrl.value = encodeURIComponent(bioList.value[index].url)
 }
 
+const analysisLoading = ref(false)
 const handleResumeAnalysis = () => {
   if (!fileUrl.value) return uni.showToast({ icon: 'none', title: '请选择要解析的简历' })
+  analysisLoading.value = true
   emit('submit', fileUrl.value)
 	// uni.navigateTo({
 	// 	url: `/pagesA/resumeAnalysis/index?fileUrl=${fileUrl.value}`

+ 85 - 0
pagesA/resumeAnalysis/components/advantage.vue

@@ -0,0 +1,85 @@
+<template>
+	<view class="f-straight wrapper">
+		<uni-forms ref="form" :modelValue="formData" :rules="rules" validateTrigger="bind" label-width="105px" label-align="right">
+      <uni-forms-item label="个人优势" name="advantage" required>
+        <textarea
+          placeholder-style="color:#F76260"
+          placeholder="请填写您的个人优势..."
+          auto-focus
+          maxlength="300"
+          v-model="formData.advantage"
+          style="border: 1rpx solid gray; width: 100%; min-height: 300px;"
+        ></textarea>
+			</uni-forms-item>
+		</uni-forms>
+	</view>
+</template>
+
+<script setup>
+import { ref, watch, unref } from 'vue'
+const props = defineProps({
+  id: {
+    type: String,
+    default: ''
+  },
+  data: {
+    type: String,
+    default: ''
+  }
+})
+
+const form = ref()
+const formData = ref({ advantage:'' })
+
+watch(
+  () => props.data,
+  (newVal) => {
+    formData.value.advantage = newVal
+  },
+  { immediate: true },
+)
+
+
+const rules = {
+	advantage:{
+		rules: [{required: true, errorMessage: '请上传头像' }]
+	}
+}
+
+const submit = async () => {
+  const valid = await unref(form).validate()
+  if (!valid) return { id: props.id, data: null}
+  return { id: props.id, data: formData.value.advantage}
+}
+
+defineExpose({
+  id: props.id,
+  submit
+})
+
+</script>
+
+<style lang="less" scoped>
+
+.wrapper{
+	padding: 15px;
+  padding-top: 30px;
+}
+.upload-img{
+  position: relative;
+  width: 200rpx;
+  height: 200rpx;
+  border: 1px solid #f1f1f1;
+  margin: 10rpx;
+}
+.upload-file{
+  width: 200rpx;
+  height: 200rpx;
+  border: 1px solid #f1f1f1;
+  margin: 10rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 10rpx;
+}
+</style>

+ 12 - 5
pagesA/resumeAnalysis/components/avatarEdit.vue

@@ -17,10 +17,13 @@
 </template>
 
 <script setup>
-defineOptions({name: 'resumeAnalysis-avatarEdit'})
 import { pathToBase64 } from '@/utils/image-tools.js'
 import { ref, watch } from 'vue'
 const props = defineProps({
+  id: {
+    type: String,
+    default: ''
+  },
   data: {
     type: String,
     default: ''
@@ -75,10 +78,14 @@ const rules = {
 	}
 }
 
-// const submit = async () => {
-//   const valid = await unref(form).validate()
-//   if (!valid) return
-// }
+const submit = async () => {
+  return { id: props.id, data: formData.value.avatar}
+}
+
+defineExpose({
+  id: props.id,
+  submit
+})
 
 </script>
 

+ 17 - 6
pagesA/resumeAnalysis/components/baseInfoEdit.vue

@@ -45,15 +45,18 @@
 </template>
 
 <script setup>
-defineOptions({name: 'resumeAnalysis-baseInfoEdit'})
 import { cloneDeep } from 'lodash-es'
 import { emailRequired } from '@/utils/validate'
 import { dictObj } from '@/utils/position.js'
 import { userStore } from '@/store/user'
 // import { saveBaseInfo, updatePersonAvatar } from '@/api/user'
 // import { uploadFile } from '@/api/file'
-import { ref, watch } from 'vue'
+import { ref, watch, unref } from 'vue'
 const props = defineProps({
+  id: {
+    type: String,
+    default: ''
+  },
   data: {
     type: Object,
     default: () => ({})
@@ -126,10 +129,18 @@ const rules = {
 	}
 }
 
-// const submit = async () => {
-//   const valid = await unref(form).validate()
-//   if (!valid) return
-// }
+
+const submit = async () => {
+  const valid = await unref(form).validate()
+  if (!valid) return { id: props.id, data: null}
+  return { id: props.id, data: formData.value}
+}
+
+defineExpose({
+  id: props.id,
+  submit
+})
+
 
 </script>
 

+ 164 - 0
pagesA/resumeAnalysis/components/educationExp.vue

@@ -0,0 +1,164 @@
+<template>
+	<view class="f-straight wrapper">
+    <uni-forms ref="form" :modelValue="formData" :rules="rules" validateTrigger="bind" label-width="90px">
+      <uni-forms-item label="学校名称" name="schoolName" required>
+				<uni-combox :candidates="schoolData" placeholder="学校名称" v-model="formData.schoolName" @input="handleSearchSchool"></uni-combox>
+			</uni-forms-item>
+      <uni-forms-item label="所学专业" name="major" required>
+				<uni-combox :candidates="majorData" placeholder="所学专业" v-model="formData.major" @input="handleSearchMajor"></uni-combox>
+			</uni-forms-item>
+      <uni-forms-item label="学历" name="educationType" required>
+        <uni-data-select v-model="formData.educationType" :localdata="searchData.eduType"></uni-data-select>
+			</uni-forms-item>
+      <uni-forms-item label="学制类型" name="educationSystemType" required>
+        <uni-data-select v-model="formData.educationSystemType" :localdata="searchData.eduSystemType"></uni-data-select>
+			</uni-forms-item>
+      <uni-forms-item label="开始时间" name="startTime" required>
+				<picker mode="date" :value="formData.startTime" fields="month" :end="endDate" @change="e => formData.startTime = e.detail.value">
+					<view class="uni-input ss-m-t-20">{{ formData.startTime }}</view>
+				</picker>
+			</uni-forms-item>
+      <uni-forms-item label="结束时间" name="endTime" required>
+				<picker mode="date" :value="formData.endTime" fields="month" @change="e => formData.endTime = e.detail.value">
+					<view class="uni-input ss-m-t-20">{{ formData.endTime }}</view>
+				</picker>
+			</uni-forms-item>
+      <uni-forms-item label="在校经历" name="content">
+				<uni-easyinput type="textarea" v-model="formData.content" autoHeight  placeholder="请输入内容"></uni-easyinput>
+			</uni-forms-item>
+    </uni-forms>
+	</view>
+</template>
+
+<script setup>
+import { cloneDeep } from 'lodash-es'
+import { ref, watch, unref } from 'vue'
+import { dictObj } from '@/utils/position.js'
+import { convertYearMonthToTimestamp, timesTampChange } from '@/utils/date.js'
+const props = defineProps({
+  id: {
+    type: String,
+    default: ''
+  },
+  data: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+let formData = ref({
+  startTime: '2014-01',
+  endTime: '2018-01'
+})
+const majorData = ref([])
+const schoolData = ref([])
+const form = ref()
+const date = new Date()
+const endDate = date.getFullYear() + '-' + (date.getMonth() + 1) // 不可选时间
+const searchData = ref({
+  school: [],
+  major: [],
+  eduType: dictObj.edu.map(e => ({ text: e.label, value: e.value})),
+  eduSystemType: dictObj.eduSystemType.map(e => ({ text: e.label, value: e.value}))
+})
+
+const getInfo = (data) => {
+  data.startTime = data.startTime ? timesTampChange(data.startTime, 'Y-M') : '2014-01'
+  data.endTime = data.endTime ? timesTampChange(data.endTime, 'Y-M') : '2018-01'
+  formData.value = cloneDeep(data) || {
+    startTime: '2014-01',
+    endTime: '2018-01'
+  }
+}
+
+watch(
+  () => props.data,
+  (newVal) => {
+    if (newVal && Object.keys(newVal)) {
+      getInfo(newVal)
+    }
+  },
+  { immediate: true },
+)
+
+const rules = {
+	schoolName:{
+		rules: [{required: true, errorMessage: '请输入学校名称' }]
+	},
+	major:{
+		rules: [{required: true, errorMessage: '请输入所学专业' }]
+	},
+	educationType:{
+		rules: [{required: true, errorMessage: '请选择学历' }]
+	},
+	educationSystemType:{
+		rules: [{required: true, errorMessage: '请选择学制类型' }]
+	},
+	startTime:{
+		rules: [{required: true, errorMessage: '请选择开始时间' }]
+	},
+	endTime:{
+		rules: [{required: true, errorMessage: '请选择结束时间' }]
+	}
+}
+
+
+// 学校搜索
+const handleSearchSchool = (e) => {
+  if (!e) return schoolData.value = []
+  schoolSearchByName({ name: e }).then(res => {
+    searchData.value.school = res.data
+    schoolData.value = res.data && res.data?.length ? res.data.map(e => e.value) : []
+  })
+}
+
+// 专业搜索
+const handleSearchMajor = (e) => {
+  if (!e) return majorData.value = []
+  schoolMajorByName({ name: e }).then(res => {
+    searchData.value.major = res.data
+    majorData.value = res.data && res.data?.length ? res.data.map(e => e.nameCn) : []
+  })
+}
+
+const submit = async () => {
+  const valid = await unref(form).validate()
+  if (!valid) return { id: props.id, data: null}
+  //
+  const startTime = convertYearMonthToTimestamp(formData.value.startTime)
+  const endTime = convertYearMonthToTimestamp(formData.value.endTime)
+  return { id: props.id, data: { ...formData.value, startTime, endTime }}
+}
+
+defineExpose({
+  id: props.id,
+  submit
+})
+
+
+</script>
+
+<style lang="less" scoped>
+
+.wrapper{
+	padding: 15px;
+  // padding-top: 30px;
+}
+.upload-img{
+  position: relative;
+  width: 200rpx;
+  height: 200rpx;
+  border: 1px solid #f1f1f1;
+  margin: 10rpx;
+}
+.upload-file{
+  width: 200rpx;
+  height: 200rpx;
+  border: 1px solid #f1f1f1;
+  margin: 10rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 10rpx;
+}
+</style>

+ 124 - 0
pagesA/resumeAnalysis/components/trainingExperience.vue

@@ -0,0 +1,124 @@
+<template>
+	<view class="f-straight wrapper">
+    <uni-forms ref="form" :modelValue="formData" :rules="rules" validateTrigger="bind" label-width="90px">
+      <uni-forms-item label="培训中心" name="orgName" required>
+				<uni-easyinput type="text" v-model="formData.orgName" placeholder="请输入内容"></uni-easyinput>
+			</uni-forms-item>
+      <uni-forms-item label="培训课程" name="course" required>
+				<uni-easyinput type="text" v-model="formData.course" placeholder="请输入内容"></uni-easyinput>
+			</uni-forms-item>
+      <uni-forms-item label="开始时间" name="startTime" required>
+				<picker mode="date" :value="formData.startTime" fields="month" :end="endDate" @change="e => formData.startTime = e.detail.value">
+					<view class="uni-input ss-m-t-20">{{ formData.startTime }}</view>
+				</picker>
+			</uni-forms-item>
+      <uni-forms-item label="结束时间" name="endTime" required>
+				<picker mode="date" :value="formData.endTime" fields="month" :end="endDate" @change="e => formData.endTime = e.detail.value">
+          <view class="uni-input ss-m-t-20">{{ formData.endTime }}</view>
+        </picker>
+			</uni-forms-item>
+      <uni-forms-item label="培训描述" name="content">
+				<uni-easyinput type="textarea" v-model="formData.content" autoHeight  placeholder="请输入内容"></uni-easyinput>
+			</uni-forms-item>
+    </uni-forms>
+	</view>
+</template>
+
+<script setup>
+import { cloneDeep } from 'lodash-es'
+import { ref, watch, unref } from 'vue'
+import { convertYearMonthToTimestamp, timesTampChange } from '@/utils/date.js'
+const props = defineProps({
+  id: {
+    type: String,
+    default: ''
+  },
+  data: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+let formData = ref({
+  startTime: '2014-01',
+  endTime: '2018-01'
+})
+const form = ref()
+const date = new Date()
+const endDate = date.getFullYear() + '-' + (date.getMonth() + 1) // 不可选时间
+
+const getInfo = (data) => {
+  data.startTime = data.startTime ? timesTampChange(data.startTime, 'Y-M') : '2014-01'
+  data.endTime = data.endTime ? timesTampChange(data.endTime, 'Y-M') : '2018-01'
+  formData.value = cloneDeep(data) || {
+    startTime: '2014-01',
+    endTime: '2018-01'
+  }
+}
+
+watch(
+  () => props.data,
+  (newVal) => {
+    if (newVal && Object.keys(newVal)) {
+      getInfo(newVal)
+    }
+  },
+  { immediate: true },
+)
+
+const rules = {
+	orgName:{
+		rules: [{required: true, errorMessage: '请输入培训中心' }]
+	},
+	course:{
+		rules: [{required: true, errorMessage: '请输入培训课程' }]
+	},
+	startTime:{
+		rules: [{required: true, errorMessage: '请选择培训开始时间' }]
+	},
+  endTime:{
+		rules: [{required: true, errorMessage: '请选择培训结束时间' }]
+	}
+}
+
+const submit = async () => {
+  const valid = await unref(form).validate()
+  if (!valid) return { id: props.id, data: null}
+  //
+  const startTime = convertYearMonthToTimestamp(formData.value.startTime)
+  const endTime = convertYearMonthToTimestamp(formData.value.endTime)
+  return { id: props.id, data: { ...formData.value, startTime, endTime }}
+}
+
+defineExpose({
+  id: props.id,
+  submit
+})
+
+
+</script>
+
+<style lang="less" scoped>
+
+.wrapper{
+	padding: 15px;
+  // padding-top: 30px;
+}
+.upload-img{
+  position: relative;
+  width: 200rpx;
+  height: 200rpx;
+  border: 1px solid #f1f1f1;
+  margin: 10rpx;
+}
+.upload-file{
+  width: 200rpx;
+  height: 200rpx;
+  border: 1px solid #f1f1f1;
+  margin: 10rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 10rpx;
+}
+</style>

+ 168 - 0
pagesA/resumeAnalysis/components/workExperience.vue

@@ -0,0 +1,168 @@
+<template>
+	<view class="f-straight wrapper">
+    <uni-forms ref="form" :modelValue="formData" :rules="rules" validateTrigger="bind" label-width="90px">
+      <uni-forms-item label="企业名称" name="enterpriseName" required>
+				<uni-combox :candidates="enterpriseData" placeholder="企业名称" v-model="formData.enterpriseName" @input="handleSearchEnterprise"></uni-combox>
+			</uni-forms-item>
+      <uni-forms-item label="职位名称" name="positionName" required>
+				<uni-combox :candidates="positionData" placeholder="职位名称" v-model="formData.positionName"></uni-combox>
+			</uni-forms-item>
+      <uni-forms-item label="开始时间" name="startTime" required>
+				<picker mode="date" :value="formData.startTime" fields="month" :end="endDate" @change="e => formData.startTime = e.detail.value">
+					<view class="uni-input ss-m-t-20">{{ formData.startTime }}</view>
+				</picker>
+			</uni-forms-item>
+      <uni-forms-item label="结束时间" name="endTime" required>
+				<view class="d-flex">
+          <picker mode="date" :value="formData.endTime" :disabled="endDisabled" fields="month" :end="endDate" @change="e => formData.endTime = e.detail.value">
+            <view class="uni-input ss-m-t-20" :style="{'opacity': endDisabled ? '0.5' : '1'}">{{ formData.endTime }}</view>
+          </picker>
+          <uni-data-checkbox selectedColor="#00897B" class="ss-m-l-50 ss-m-t-14" multiple v-model="sofar" :localdata="[{ text: '至今', value: 1 }]" @change="handleChangeSofar"></uni-data-checkbox>
+        </view>
+			</uni-forms-item>
+      <uni-forms-item label="工作内容" name="content" required>
+				<uni-easyinput type="textarea" v-model="formData.content" autoHeight  placeholder="请输入内容"></uni-easyinput>
+			</uni-forms-item>
+    </uni-forms>
+	</view>
+</template>
+
+<script setup>
+import { cloneDeep } from 'lodash-es'
+import { ref, watch, unref } from 'vue'
+import { getDict } from '@/hooks/useDictionaries.js'
+import { convertYearMonthToTimestamp, timesTampChange } from '@/utils/date.js'
+const props = defineProps({
+  id: {
+    type: String,
+    default: ''
+  },
+  data: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+let formData = ref({
+  startTime: '2014-01',
+  endTime: '2018-01'
+})
+const sofar = ref([])
+const endDisabled = ref(false)
+const enterpriseData = ref([])
+const positionData = ref([])
+const form = ref()
+const date = new Date()
+const endDate = date.getFullYear() + '-' + (date.getMonth() + 1) // 不可选时间
+const searchData = ref({
+  enterprise: [],
+  position: []
+})
+
+const getInfo = (data) => {
+  data.startTime = data.startTime ? timesTampChange(data.startTime, 'Y-M') : '2014-01'
+  data.endTime = data.endTime ? timesTampChange(data.endTime, 'Y-M') : null
+  if (!data.endTime) {
+    endDisabled.value = true
+    data.endTime = '2018-01'
+    sofar.value = [1]
+  }
+  formData.value = cloneDeep(data) || {
+    startTime: '2014-01',
+    endTime: '2018-01'
+  }
+}
+
+watch(
+  () => props.data,
+  (newVal) => {
+    if (newVal && Object.keys(newVal)) {
+      getInfo(newVal)
+    }
+  },
+  { immediate: true },
+)
+
+const rules = {
+	enterpriseName:{
+		rules: [{required: true, errorMessage: '请输入企业名称' }]
+	},
+	positionName:{
+		rules: [{required: true, errorMessage: '请输入职位名称' }]
+	},
+	startTime:{
+		rules: [{required: true, errorMessage: '请选择开始时间' }]
+	},
+  content:{
+		rules: [{required: true, errorMessage: '请输入工作内容' }]
+	}
+}
+
+
+// 企业搜索
+const handleSearchEnterprise = (e) => {
+  if (!e) return enterpriseData.value = []
+  enterpriseSearchByName({ name: e }).then(res => {
+    searchData.value.enterprise = res.data
+    enterpriseData.value = res.data && res.data?.length ? res.data.map(e => e.value) : []
+  })
+}
+
+let positionTreeChildrenData = []
+getDict('positionTreeData', null, 'positionTreeData').then(({ data }) => {
+  data = data.data?.length && data.data || []
+  data.forEach(e => {
+    if (e?.children?.length) positionTreeChildrenData = positionTreeChildrenData.concat(e.children)
+  })
+  searchData.value.position = positionTreeChildrenData
+  positionData.value = positionTreeChildrenData.map(e => e.nameCn)
+})
+
+// 至今
+const handleChangeSofar = (e) => {
+  const value = e.detail.value.length ? e.detail.value[0] : ''
+  endDisabled.value = value ? true : false
+}
+
+const submit = async () => {
+  const valid = await unref(form).validate()
+  if (!valid) return { id: props.id, data: null}
+  if (!formData.value.endTime && !sofar.value.length) return uni.showToast({ icon: 'none', title: '请选择工作经历的结束时间' })
+  //
+  const startTime = convertYearMonthToTimestamp(formData.value.startTime)
+  const endTime = sofar.value.length ? null : convertYearMonthToTimestamp(formData.value.endTime)
+  return { id: props.id, data: { ...formData.value, startTime, endTime }}
+}
+
+defineExpose({
+  id: props.id,
+  submit
+})
+
+
+</script>
+
+<style lang="less" scoped>
+
+.wrapper{
+	padding: 15px;
+  // padding-top: 30px;
+}
+.upload-img{
+  position: relative;
+  width: 200rpx;
+  height: 200rpx;
+  border: 1px solid #f1f1f1;
+  margin: 10rpx;
+}
+.upload-file{
+  width: 200rpx;
+  height: 200rpx;
+  border: 1px solid #f1f1f1;
+  margin: 10rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 10rpx;
+}
+</style>

+ 134 - 44
pagesA/resumeAnalysis/index.vue

@@ -1,8 +1,10 @@
 <!--  -->
 <template>
   <view>
-    <resume v-if="showResumeList" resumeAnalysis @submit="handleResumeAnalysis"></resume>
-    <view v-if="formLIst?.length">
+    <!-- 选择简历 -->
+    <resume v-if="step === 1" resumeAnalysis @submit="handleResumeAnalysis"></resume>
+    <!-- 解析内容-表单 -->
+    <view v-if="step === 2 && formLIst?.length" style="padding-bottom: 150rpx;">
       <uni-card v-for="item of formLIst" :key="item.id" :id="item.id">
 		    <uni-section :title="item.text" type="line">
           <template v-slot:right>
@@ -10,12 +12,22 @@
           </template>
           <avatarEdit v-if="item.path === 'avatarEdit'" ref="componentRef" :id="item.id" :data="item.data" />
           <baseInfoEdit v-if="item.path === 'baseInfoEdit'" ref="componentRef" :id="item.id" :data="item.data" />
+          <advantageEdit v-if="item.path === 'advantageEdit'" ref="componentRef" :id="item.id" :data="item.data" />
+          <educationEdit v-if="item.path === 'educationEdit'" ref="componentRef" :id="item.id" :data="item.data" />
+          <workExperienceEdit v-if="item.path === 'workExperienceEdit'" ref="componentRef" :id="item.id" :data="item.data" />
+          <trainingExperienceEdit v-if="item.path === 'trainingExperienceEdit'" ref="componentRef" :id="item.id" :data="item.data" />
         </uni-section>
       </uni-card>
+      <!-- 保存 -->
+      <view class="bottom-sticky flex-column ss-p-b-25" style="background-color: #fff; z-index: 2000; border-top: 1px solid #eee;">
+        <button class="recomm-button" :loading="submitLoading" @click="submit">提交(保存至在线简历)</button>
+      </view>
     </view>
-    <view v-else>
-      <view v-if="loading">加载中...</view>
-      <view v-else>加载失败</view>
+    <view v-if="step === 3">
+      <view class="tips">加载中...</view>
+    </view>
+    <view v-if="step === 4">
+      <view class="tips">加载失败</view>
     </view>
     <!-- 确认框 -->
     <uni-popup ref="confirmRef" type="dialog">
@@ -24,6 +36,7 @@
         cancelText="取消"
         confirmText="确认" 
         title="系统提示"
+        :showClose="showClose"
         :content="dialogContent"
         @confirm="handleConfirm"
         @close="null"
@@ -33,33 +46,27 @@
 </template>
 
 <script setup>
-defineOptions({name: 'resumeAnalysis-index'})
-// import { saveResumeInfo, resumeParser2 } from '@/api/recruit/personal/resume'
-// import { resumeParser2 } from '@/api/recruit/personal/resume'
+import { saveResumeInfo, resumeParser2 } from '@/api/user'
 // import { envObj, baseUrl } from '@/utils/config'
 import resume from '../resume/index.vue'
 import { ref, shallowRef } from 'vue'
-import baseInfoEdit from './components/baseInfoEdit.vue'
 import avatarEdit from './components/avatarEdit.vue'
-import { data } from './testData.js'
-
-const showResumeList = ref(true)
+import baseInfoEdit from './components/baseInfoEdit.vue'
+import advantageEdit from './components/advantage.vue'
+import educationEdit from './components/educationExp.vue'
+import workExperienceEdit from './components/workExperience.vue'
+import trainingExperienceEdit from './components/trainingExperience.vue'
+// import { data } from './testData.js'
 
-const handleResumeAnalysis = (url) => {
-  if (!url) {
-    return uni.showToast({ icon: 'none', title: '请选择要解析的简历' })
-  }
-  showResumeList.value = false
-  // handleAnalysis(url)
-}
+const step = ref(1)
 
 const exampleList = {
   avatar: { text: '头像', id: 'avatar', path: 'avatarEdit' },
   person: { text: '基础信息', id: 'person', path: 'baseInfoEdit' },
-  advantage: { text: '个人优势', id: 'advantage', path: 'advantage' },
-  eduList: { text: '教育经历', id: 'eduList', path: '' },
-  workList: { text: '工作经历', id: 'workList', path: '' },
-  trainList: { text: '培训经历', id: 'trainList', path: '' },
+  advantage: { text: '个人优势', id: 'advantage', path: 'advantageEdit' },
+  eduList: { text: '教育经历', id: 'eduList', path: 'educationEdit' },
+  workList: { text: '工作经历', id: 'workList', path: 'workExperienceEdit' },
+  trainList: { text: '培训经历', id: 'trainList', path: 'trainingExperienceEdit' },
 }
 
 const resumeTxt = ref([]) // 查看文本信息
@@ -98,36 +105,119 @@ const transformToLIst = async (result) => {
 
 const confirmRef = ref()
 const dialogContent = ref('')
+const showClose = ref(true)
 let delId = null
+let dialogType = 'del'
+
 const del = (item) => {
-  dialogContent.value =  `是否确认删除${item.text}?`
+  dialogContent.value = `是否确认删除${item.text}?`
   delId = item.id
+  dialogType = 'del'
+  showClose.value = true
   confirmRef.value.open()
 }
+
 const handleConfirm = () => {
-  formLIst.value = formLIst.value.filter(e => e.id !== delId)
+  if (dialogType === 'del') {
+    formLIst.value = formLIst.value.filter(e => e.id !== delId)
+  }
+  if (dialogType === 'submitSuccess') {
+    uni.navigateTo({ url: '/pagesA/resumeOnline/index' })
+  }
 }
 
-const result = ref(JSON.parse(JSON.stringify(data)))
+// const result = ref(JSON.parse(JSON.stringify(data))) // 测试
+// transformToLIst(result.value) // 测试
+
+const result = ref({})
 const loading = ref(false)
-transformToLIst(result.value)
-// const handleAnalysis = async (url) => {
-//   url = decodeURIComponent(url)
-//   if (!url) return
-//   showSelect.value = false
-//   loading.value = true
-//   // const baseUrl = envObj.previewUrl
-//   // fileUrl.value = !url.includes('.pdf') ?  `${baseUrl}/onlinePreview?url=${encodeURIComponent(Base64.encode(url))}` : url
-//   try {
-//     const data = await resumeParser2({ fileUrl: url })
-//     result.value = data || {}
-//     await transformToLIst(result.value)
-//   } catch (error) {
-//     console.log(error)
-//   } finally {
-//     loading.value = false
-//   }
-// }
+const handleAnalysis = async (url) => {
+  url = decodeURIComponent(url)
+  if (!url) return
+  loading.value = true
+  step.value = 3
+  // const baseUrl = envObj.previewUrl
+  // fileUrl.value = !url.includes('.pdf') ?  `${baseUrl}/onlinePreview?url=${encodeURIComponent(Base64.encode(url))}` : url
+  try {
+    const res = await resumeParser2({ fileUrl: url })
+    result.value = res?.data || {}
+    await transformToLIst(result.value)
+    step.value = 2
+  } catch (error) {
+    step.value = 4
+    console.log(error)
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleResumeAnalysis = (url) => {
+  if (!url) {
+    return uni.showToast({ icon: 'none', title: '请选择要解析的简历' })
+  }
+  handleAnalysis(url)
+}
+
+const componentRef = ref()
+const getValue = async () => {
+  let id = ''
+  let data = {}
+  for (let index = 0; index < componentRef.value.length; index++) {
+    const e = componentRef.value[index]
+    const query = await e.submit()
+    if (query && query.data) {
+      data[query.id] = query.data
+    } else {
+      id = id ? id : query.id
+    }
+  }
+  console.log('id:', id)
+  if (id) {
+    uni.showToast({ icon: 'none', title: '请填写完整后提交!' })
+    return
+  }
+  // 处理data
+  let obj = Object.keys(data).length ? {} : null
+  const keyTransform = { // 转换给后端的key
+    eduList: 'eduExp',
+    workList: 'workExp',
+    trainList: 'trainExp',
+  }
+  if (obj) {
+    Object.keys(data).forEach(key => {
+      if (key.includes('_')) { // 数组
+        const oldKey = key.split('_')[0]
+        const newKey = keyTransform[oldKey] ? keyTransform[oldKey] : oldKey
+        if (!obj[newKey]) obj[newKey] = [data[key]]
+        else obj[newKey].push(data[key])
+      } else {
+        const newKey = keyTransform[key] ? keyTransform[key] : key
+        obj[newKey] = data[key]
+      }
+    })
+  }
+  console.log('123456:', obj)
+  return obj && Object.keys(obj).length ? JSON.stringify(obj) : null
+}
+
+const submitLoading = ref(false)
+const submit = async () => {
+  const obj = await getValue()
+  if (!obj) return
+  submitLoading.value = true
+  await saveResumeInfo(obj)
+  dialogType = 'submitSuccess'
+  dialogContent.value = '提交成功,立即前往在线简历查看'
+  showClose.value = false
+  confirmRef.value.open()
+  // await useUserStore().getUserBaseInfos() // 更新用户信息
+}
+
 </script>
 <style lang="scss" scoped>
+.tips {
+  text-align: center;
+  margin-top: 50px;
+  color: #777;
+}
 </style>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 49
pagesA/resumeAnalysis/testData.js


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio