= 职业规划推荐
This commit is contained in:
@@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Date: 2025-11-12
|
|
||||||
* @Description: 职业推荐相关接口
|
|
||||||
*/
|
|
||||||
import request from '@/utilsRc/request'
|
|
||||||
|
|
||||||
function createFormData(payload = {}) {
|
|
||||||
if (typeof FormData !== 'undefined') {
|
|
||||||
const formData = new FormData()
|
|
||||||
Object.keys(payload).forEach(key => {
|
|
||||||
const value = payload[key]
|
|
||||||
if (value !== undefined && value !== null && value !== '') {
|
|
||||||
formData.append(key, value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return formData
|
|
||||||
}
|
|
||||||
return payload
|
|
||||||
}
|
|
||||||
|
|
||||||
export function recommendJob(data) {
|
|
||||||
const params = {};
|
|
||||||
if (data?.jobName !== undefined && data?.jobName !== null && data?.jobName !== '') {
|
|
||||||
params.jobName = String(data.jobName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return request({
|
|
||||||
url: '/job/recommendJobByJobName',
|
|
||||||
method: 'get',
|
|
||||||
params: params,
|
|
||||||
baseUrlType: 'zytp'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Date: 2025-11-12
|
|
||||||
* @Description: 职业技能相关接口
|
|
||||||
*/
|
|
||||||
import request from '@/utilsRc/request'
|
|
||||||
|
|
||||||
export function getJobSkillDetail(params) {
|
|
||||||
return request({
|
|
||||||
url: '/jobSkillDet/getJobSkillDet',
|
|
||||||
method: 'get',
|
|
||||||
params,
|
|
||||||
baseUrlType: 'zytp'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取技能权重
|
|
||||||
export function getJobSkillWeight(params) {
|
|
||||||
return request({
|
|
||||||
url: '/jobSkillDet/getJobSkillWeight',
|
|
||||||
method: 'get',
|
|
||||||
params,
|
|
||||||
baseUrlType: 'zytp'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 暂未使用 - 如果需要在 CareerPath.vue 中点击路径职位查看详细技能信息时使用
|
|
||||||
// 使用场景:获取职业路径中某个职位的详细技能信息(包含技能分数、类型等)
|
|
||||||
// export function getJobPathSkill(data) {
|
|
||||||
// let formData
|
|
||||||
// if (typeof FormData !== 'undefined') {
|
|
||||||
// formData = new FormData()
|
|
||||||
// if (data?.pathId !== undefined && data?.pathId !== null) {
|
|
||||||
// formData.append('pathId', data.pathId)
|
|
||||||
// }
|
|
||||||
// if (data?.currentJobName !== undefined && data?.currentJobName !== null) {
|
|
||||||
// formData.append('currentJobName', data.currentJobName)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// formData = {
|
|
||||||
// pathId: data?.pathId ?? '',
|
|
||||||
// currentJobName: data?.currentJobName ?? ''
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return request({
|
|
||||||
// url: '/jobSkillDet/getJobPathSkill',
|
|
||||||
// method: 'post',
|
|
||||||
// data: formData,
|
|
||||||
// baseUrlType: 'zytp',
|
|
||||||
// header: {
|
|
||||||
// 'content-type': 'multipart/form-data'
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
37
apiRc/service/career-path.js
Normal file
37
apiRc/service/career-path.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* @Date: 2024-09-25 11:14:29
|
||||||
|
* @LastEditors: shirlwang
|
||||||
|
* @LastEditTime: 2025-12-23 17:40:11
|
||||||
|
* @Description: 职业路径相关接口
|
||||||
|
*/
|
||||||
|
import request from '@/utilsRc/request'
|
||||||
|
|
||||||
|
// 获取当前职位
|
||||||
|
export function getCurrentPosition(query) {
|
||||||
|
return request({
|
||||||
|
url: '/jobPath/getJob',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取路径列表
|
||||||
|
export function getPath(query) {
|
||||||
|
return request({
|
||||||
|
url: '/jobPath/getJobPathList',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取路径详情
|
||||||
|
export function getPathDetail(query) {
|
||||||
|
return request({
|
||||||
|
url: '/jobPath/getJobPathById',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
37
apiRc/service/career-recommendation.js
Normal file
37
apiRc/service/career-recommendation.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* @Date: 2024-09-25 11:14:29
|
||||||
|
* @LastEditors: shirlwang
|
||||||
|
* @LastEditTime: 2025-12-23 17:40:11
|
||||||
|
* @Description: 职业推荐相关接口
|
||||||
|
*/
|
||||||
|
import request from '@/utilsRc/request'
|
||||||
|
|
||||||
|
// 获取职业列表
|
||||||
|
export function getProfessions(query) {
|
||||||
|
return request({
|
||||||
|
url: '/jobSimilarity/getJob',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取技能标签
|
||||||
|
export function getSkillTags(query) {
|
||||||
|
return request({
|
||||||
|
url: '/jobSkillDet/getJobSkill',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取推荐职业
|
||||||
|
export function getRecommend(query) {
|
||||||
|
return request({
|
||||||
|
url: '/jobSimilarity/recommendJobByJobName',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
3
apiRc/service/index.js
Normal file
3
apiRc/service/index.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './career-recommendation.js';
|
||||||
|
export * from './career-path.js';
|
||||||
|
export * from './skill-development.js';
|
||||||
@@ -76,11 +76,16 @@ export function getAddedJobs(params) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 获取推荐岗位
|
export function recommendJob(data) {
|
||||||
// export function getAddedJobs(params) {
|
const params = {};
|
||||||
// return request({
|
if (data?.jobName !== undefined && data?.jobName !== null && data?.jobName !== '') {
|
||||||
// url: '/personnel/personBaseInfo/postRecommend',
|
params.jobName = String(data.jobName);
|
||||||
// method: 'get',
|
}
|
||||||
// params,
|
|
||||||
// })
|
return request({
|
||||||
// }
|
url: '/job/recommendJobByJobName',
|
||||||
|
method: 'get',
|
||||||
|
params: params,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
24
apiRc/service/jobSkill.js
Normal file
24
apiRc/service/jobSkill.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* @Date: 2025-11-12
|
||||||
|
* @Description: 职业技能相关接口
|
||||||
|
*/
|
||||||
|
import request from '@/utilsRc/request'
|
||||||
|
|
||||||
|
export function getJobSkillDetail(params) {
|
||||||
|
return request({
|
||||||
|
url: '/jobSkillDet/getJobSkillDet',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取技能权重
|
||||||
|
export function getJobSkillWeight(params) {
|
||||||
|
return request({
|
||||||
|
url: '/jobSkillDet/getJobSkillWeight',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
17
apiRc/service/skill-development.js
Normal file
17
apiRc/service/skill-development.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* @Date: 2024-09-25 11:14:29
|
||||||
|
* @LastEditors: shirlwang
|
||||||
|
* @LastEditTime: 2025-12-23 17:40:11
|
||||||
|
* @Description: 技能发展相关接口
|
||||||
|
*/
|
||||||
|
import request from '@/utilsRc/request'
|
||||||
|
|
||||||
|
// 获取技能信息
|
||||||
|
export function getSkill(query) {
|
||||||
|
return request({
|
||||||
|
url: '/jobSkillDet/getJobSkillWeight',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
baseUrlType: 'zytp'
|
||||||
|
})
|
||||||
|
}
|
||||||
590
pages/service/career-planning.bak.vue
Normal file
590
pages/service/career-planning.bak.vue
Normal file
@@ -0,0 +1,590 @@
|
|||||||
|
<template>
|
||||||
|
<view class="career-planning-page">
|
||||||
|
<!-- 提醒弹窗 -->
|
||||||
|
<RemindPopup
|
||||||
|
ref="remindPopup"
|
||||||
|
:remind-list="remindList"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@confirm="handleConfirm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 技能详情弹出层 -->
|
||||||
|
<SkillDetailPopup
|
||||||
|
ref="skillDetailPopup"
|
||||||
|
:job-title="selectedJobTitle"
|
||||||
|
:possessed-skills="selectedJobPossessedSkills"
|
||||||
|
:improvement-skills="selectedJobImprovementSkills"
|
||||||
|
@close="handleSkillPopupClose"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 页面内容 -->
|
||||||
|
<view class="page-content" v-if="showContent">
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<!-- 小程序背景图片 -->
|
||||||
|
<image class="mp-background" src="/static/icon/background2.png" mode="aspectFill"></image>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<!-- 头部区域 -->
|
||||||
|
<PageHeader
|
||||||
|
:active-tab="activeTab"
|
||||||
|
@tab-change="switchTab"
|
||||||
|
@search-click="handleSearchClick"
|
||||||
|
@menu-click="handleMenuClick"
|
||||||
|
@more-click="handleMoreClick"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<scroll-view scroll-y class="content-scroll">
|
||||||
|
<CareerRecommend
|
||||||
|
v-if="activeTab === 0"
|
||||||
|
:current-job-id="currentJobId"
|
||||||
|
:current-job-name="currentJobName"
|
||||||
|
@job-card-click="handleJobCardClick"
|
||||||
|
@skills-updated="handleRecommendSkillsUpdated"
|
||||||
|
/>
|
||||||
|
<CareerPath
|
||||||
|
v-else-if="activeTab === 1"
|
||||||
|
:current-job-name="currentJobName"
|
||||||
|
@path-data-updated="handlePathDataUpdated"
|
||||||
|
/>
|
||||||
|
<SkillDevelopment
|
||||||
|
v-else
|
||||||
|
:current-job-name="currentJobName"
|
||||||
|
@path-data-updated="handlePathDataUpdated"
|
||||||
|
/>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部导航栏 -->
|
||||||
|
<view class="tabbar-wrapper" v-if="showContent">
|
||||||
|
<CustomTabBar :currentPage="0" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, inject, nextTick, onMounted } from 'vue';
|
||||||
|
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||||
|
import { appUserInfo } from '@/apiRc/user/user.js';
|
||||||
|
import RemindPopup from './components/RemindPopup.vue';
|
||||||
|
import PageHeader from './components/PageHeader.vue';
|
||||||
|
import SkillDetailPopup from './components/SkillDetailPopup.vue';
|
||||||
|
import CareerRecommend from './components/CareerRecommend.vue';
|
||||||
|
import CareerPath from './components/CareerPath.vue';
|
||||||
|
import SkillDevelopment from './components/SkillDevelopment.vue';
|
||||||
|
import CustomTabBar from '@/components/CustomTabBar/CustomTabBar.vue';
|
||||||
|
|
||||||
|
const { navBack, navTo } = inject('globalFunction');
|
||||||
|
|
||||||
|
// 弹窗引用
|
||||||
|
const remindPopup = ref(null);
|
||||||
|
const skillDetailPopup = ref(null);
|
||||||
|
// 提醒列表(由接口返回)
|
||||||
|
const remindList = ref([]);
|
||||||
|
// 是否显示页面内容
|
||||||
|
const showContent = ref(false);
|
||||||
|
// 当前激活的tab
|
||||||
|
const activeTab = ref(0);
|
||||||
|
// 选中的职位信息
|
||||||
|
const selectedJobTitle = ref('');
|
||||||
|
const selectedJobPossessedSkills = ref([]);
|
||||||
|
const selectedJobImprovementSkills = ref([]);
|
||||||
|
const isLoadingJobSkill = ref(false);
|
||||||
|
const currentJobId = ref(null);
|
||||||
|
const currentJobName = ref('');
|
||||||
|
|
||||||
|
// 技能发展所需的数据
|
||||||
|
const recommendSkillsData = ref({
|
||||||
|
currentJobSkills: [],
|
||||||
|
recommendedJobs: []
|
||||||
|
});
|
||||||
|
|
||||||
|
const pathSkillsData = ref({
|
||||||
|
pathData: {
|
||||||
|
start: { title: '', skills: [] },
|
||||||
|
steps: [],
|
||||||
|
end: { title: '', skills: [] }
|
||||||
|
},
|
||||||
|
targetCareer: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
function openRemindPopup() {
|
||||||
|
nextTick(() => {
|
||||||
|
if (remindPopup.value) {
|
||||||
|
try {
|
||||||
|
remindPopup.value.open();
|
||||||
|
} catch (error) {
|
||||||
|
// 静默处理错误
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (remindPopup.value) {
|
||||||
|
try {
|
||||||
|
remindPopup.value.open();
|
||||||
|
} catch (error) {
|
||||||
|
// 静默处理错误
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (remindPopup.value) {
|
||||||
|
try {
|
||||||
|
remindPopup.value.open();
|
||||||
|
} catch (error) {
|
||||||
|
// 静默处理错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户是否完善了个人信息(调用接口获取)
|
||||||
|
let hasCheckedRemindInfo = false;
|
||||||
|
// 保存缺失信息的标识
|
||||||
|
const missingInfo = ref({
|
||||||
|
hasJobInfo: false,
|
||||||
|
hasSkills: false
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getRemindInfo() {
|
||||||
|
if (hasCheckedRemindInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasCheckedRemindInfo = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await appUserInfo();
|
||||||
|
const userInfo = response?.data || {};
|
||||||
|
|
||||||
|
// 检查 idCard(身份证)- 必须项
|
||||||
|
let idCard = userInfo?.resume?.idCard ?? userInfo?.idCard ?? null;
|
||||||
|
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||||||
|
if (!idCard || idCard === null || idCard === '') {
|
||||||
|
idCard = cachedUserInfo?.resume?.idCard ?? cachedUserInfo?.idCard ?? null;
|
||||||
|
}
|
||||||
|
const hasIdCard = idCard !== null && idCard !== undefined && idCard !== '';
|
||||||
|
|
||||||
|
// 检查职位信息:优先从 jobTitles 数组获取
|
||||||
|
const jobTitles = Array.isArray(userInfo?.jobTitles) ? userInfo.jobTitles : [];
|
||||||
|
const hasJobTitle = jobTitles.length > 0;
|
||||||
|
|
||||||
|
// 如果 jobTitles 为空,尝试从其他字段获取
|
||||||
|
let jobName = '';
|
||||||
|
if (!hasJobTitle) {
|
||||||
|
jobName = userInfo?.jobName ??
|
||||||
|
userInfo?.currentJobName ??
|
||||||
|
userInfo?.resume?.jobName ??
|
||||||
|
userInfo?.resume?.currentJobName ??
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
const hasJobInfo = hasJobTitle || (jobName && jobName.trim() !== '');
|
||||||
|
|
||||||
|
// 检查技能标签:从 appSkillsList 获取
|
||||||
|
const appSkillsList = Array.isArray(userInfo?.appSkillsList) ? userInfo.appSkillsList : [];
|
||||||
|
// 检查是否有有效的技能(name 或 nameStr 不为空)
|
||||||
|
const hasSkills = appSkillsList.some(skill => {
|
||||||
|
const skillName = skill?.name || skill?.nameStr;
|
||||||
|
return skillName && skillName.trim() !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 保存缺失信息标识(只保存职位信息和技能标签,身份证信息跳转到个人信息页面)
|
||||||
|
missingInfo.value.hasJobInfo = hasJobInfo;
|
||||||
|
missingInfo.value.hasSkills = hasSkills;
|
||||||
|
|
||||||
|
// 判断信息是否完整(idCard、职位信息、技能标签都必须有)
|
||||||
|
const isComplete = hasIdCard && hasJobInfo && hasSkills;
|
||||||
|
|
||||||
|
if (!isComplete) {
|
||||||
|
// 收集缺失的信息提示
|
||||||
|
const missingItems = [];
|
||||||
|
if (!hasIdCard) {
|
||||||
|
missingItems.push('身份证信息');
|
||||||
|
}
|
||||||
|
if (!hasJobInfo) {
|
||||||
|
missingItems.push('职位信息');
|
||||||
|
}
|
||||||
|
if (!hasSkills) {
|
||||||
|
missingItems.push('技能标签');
|
||||||
|
}
|
||||||
|
remindList.value = [`请完善${missingItems.join('、')}`];
|
||||||
|
} else {
|
||||||
|
// 信息完整,设置职位信息
|
||||||
|
if (hasJobTitle) {
|
||||||
|
currentJobName.value = jobTitles[0];
|
||||||
|
} else {
|
||||||
|
currentJobName.value = jobName;
|
||||||
|
currentJobId.value = userInfo?.jobId ??
|
||||||
|
userInfo?.currentJobId ??
|
||||||
|
userInfo?.resume?.jobId ??
|
||||||
|
userInfo?.resume?.currentJobId ??
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
// 信息完整,直接显示页面内容
|
||||||
|
showContent.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
openRemindPopup();
|
||||||
|
}, 500);
|
||||||
|
} catch (error) {
|
||||||
|
// 接口调用失败时,使用缓存作为降级方案
|
||||||
|
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||||||
|
|
||||||
|
// 检查 idCard
|
||||||
|
const idCard = cachedUserInfo?.resume?.idCard ?? cachedUserInfo?.idCard ?? null;
|
||||||
|
const hasIdCard = idCard !== null && idCard !== undefined && idCard !== '';
|
||||||
|
|
||||||
|
// 检查职位信息
|
||||||
|
const cachedJobTitles = Array.isArray(cachedUserInfo?.jobTitles) ? cachedUserInfo.jobTitles : [];
|
||||||
|
const hasJobTitle = cachedJobTitles.length > 0;
|
||||||
|
let jobName = '';
|
||||||
|
if (!hasJobTitle) {
|
||||||
|
jobName = cachedUserInfo?.jobName ??
|
||||||
|
cachedUserInfo?.currentJobName ??
|
||||||
|
cachedUserInfo?.resume?.jobName ??
|
||||||
|
cachedUserInfo?.resume?.currentJobName ??
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
const hasJobInfo = hasJobTitle || (jobName && jobName.trim() !== '');
|
||||||
|
|
||||||
|
// 检查技能标签
|
||||||
|
const cachedAppSkillsList = Array.isArray(cachedUserInfo?.appSkillsList) ? cachedUserInfo.appSkillsList : [];
|
||||||
|
const hasSkills = cachedAppSkillsList.some(skill => {
|
||||||
|
const skillName = skill?.name || skill?.nameStr;
|
||||||
|
return skillName && skillName.trim() !== '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 保存缺失信息标识
|
||||||
|
missingInfo.value.hasJobInfo = hasJobInfo;
|
||||||
|
missingInfo.value.hasSkills = hasSkills;
|
||||||
|
|
||||||
|
// 判断信息是否完整(idCard、职位信息、技能标签都必须有)
|
||||||
|
const isComplete = hasIdCard && hasJobInfo && hasSkills;
|
||||||
|
|
||||||
|
if (!isComplete) {
|
||||||
|
// 收集缺失的信息提示
|
||||||
|
const missingItems = [];
|
||||||
|
if (!hasIdCard) {
|
||||||
|
missingItems.push('身份证信息');
|
||||||
|
}
|
||||||
|
if (!hasJobInfo) {
|
||||||
|
missingItems.push('职位信息');
|
||||||
|
}
|
||||||
|
if (!hasSkills) {
|
||||||
|
missingItems.push('技能标签');
|
||||||
|
}
|
||||||
|
remindList.value = [`请完善${missingItems.join('、')}`];
|
||||||
|
} else {
|
||||||
|
// 信息完整,设置职位信息
|
||||||
|
if (hasJobTitle) {
|
||||||
|
currentJobName.value = cachedJobTitles[0];
|
||||||
|
} else {
|
||||||
|
currentJobName.value = jobName;
|
||||||
|
currentJobId.value = cachedUserInfo?.jobId ??
|
||||||
|
cachedUserInfo?.currentJobId ??
|
||||||
|
cachedUserInfo?.resume?.jobId ??
|
||||||
|
cachedUserInfo?.resume?.currentJobId ??
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
// 信息完整,直接显示页面内容
|
||||||
|
showContent.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
openRemindPopup();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消按钮
|
||||||
|
function handleCancel() {
|
||||||
|
remindPopup.value?.close();
|
||||||
|
navBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认按钮
|
||||||
|
async function handleConfirm() {
|
||||||
|
remindPopup.value?.close();
|
||||||
|
|
||||||
|
const { hasJobInfo, hasSkills } = missingInfo.value;
|
||||||
|
|
||||||
|
// 如果同时缺少职位信息和技能标签:先跳转到职位信息页面,并传递参数表示完成后需要继续跳转到技能页面
|
||||||
|
if (!hasJobInfo && !hasSkills) {
|
||||||
|
// 跳转到职位信息页面,传递参数表示完成后需要继续跳转到技能页面
|
||||||
|
navTo('/packageA/pages/jobExpect/jobExpect?needSkill=true');
|
||||||
|
}
|
||||||
|
// 如果只缺少技能标签:直接跳转到技能页面(个人信息页面的技能部分)
|
||||||
|
else if (!hasSkills) {
|
||||||
|
navTo('/packageA/pages/personalInfo/personalInfo');
|
||||||
|
}
|
||||||
|
// 如果只缺少职位信息:直接跳转到职位信息页面
|
||||||
|
else if (!hasJobInfo) {
|
||||||
|
navTo('/packageA/pages/jobExpect/jobExpect');
|
||||||
|
}
|
||||||
|
// 如果只缺少身份证信息:跳转到个人信息页面
|
||||||
|
else {
|
||||||
|
navTo('/packageA/pages/personalInfo/personalInfo');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换tab
|
||||||
|
function switchTab(index) {
|
||||||
|
activeTab.value = index;
|
||||||
|
|
||||||
|
if (index === 0 && !currentJobId.value) {
|
||||||
|
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||||||
|
|
||||||
|
// 优先从缓存中的 jobTitles 数组获取职位信息(取第一个)
|
||||||
|
const cachedJobTitles = Array.isArray(cachedUserInfo?.jobTitles) ? cachedUserInfo.jobTitles : [];
|
||||||
|
let newJobName = '';
|
||||||
|
|
||||||
|
if (cachedJobTitles.length > 0) {
|
||||||
|
newJobName = cachedJobTitles[0];
|
||||||
|
} else {
|
||||||
|
// 如果缓存中没有 jobTitles,从其他字段获取
|
||||||
|
newJobName = currentJobName.value ||
|
||||||
|
(cachedUserInfo?.jobName ??
|
||||||
|
cachedUserInfo?.currentJobName ??
|
||||||
|
cachedUserInfo?.resume?.jobName ??
|
||||||
|
cachedUserInfo?.resume?.currentJobName ??
|
||||||
|
'市场专员');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newJobId = cachedUserInfo?.jobId ??
|
||||||
|
cachedUserInfo?.currentJobId ??
|
||||||
|
cachedUserInfo?.resume?.jobId ??
|
||||||
|
cachedUserInfo?.resume?.currentJobId ??
|
||||||
|
null;
|
||||||
|
|
||||||
|
currentJobId.value = newJobId;
|
||||||
|
currentJobName.value = newJobName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索点击
|
||||||
|
function handleSearchClick() {
|
||||||
|
navTo('/pages/search/search');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 菜单点击
|
||||||
|
function handleMenuClick() {
|
||||||
|
// TODO: 实现菜单功能
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更多点击
|
||||||
|
function handleMoreClick() {
|
||||||
|
// TODO: 实现更多功能
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSkillLevel(score) {
|
||||||
|
const numericScore = Number(score);
|
||||||
|
if (Number.isNaN(numericScore)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const rounded = Math.round(numericScore);
|
||||||
|
return Math.max(1, Math.min(6, rounded));
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitSkillListByScore(skills = []) {
|
||||||
|
if (!Array.isArray(skills) || skills.length === 0) {
|
||||||
|
return {
|
||||||
|
possessed: [],
|
||||||
|
improvement: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted = [...skills].sort((a, b) => (Number(b.skillScore) || 0) - (Number(a.skillScore) || 0));
|
||||||
|
const midpoint = Math.ceil(sorted.length / 2);
|
||||||
|
const mapSkill = (item) => ({
|
||||||
|
name: item?.skillName || '',
|
||||||
|
level: normalizeSkillLevel(item?.skillScore)
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
possessed: sorted.slice(0, midpoint).map(mapSkill),
|
||||||
|
improvement: sorted.slice(midpoint).map(mapSkill)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理职位卡片点击
|
||||||
|
async function handleJobCardClick(job) {
|
||||||
|
if (!job) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedJobTitle.value = job.title || job.jobName || '';
|
||||||
|
selectedJobPossessedSkills.value = [];
|
||||||
|
selectedJobImprovementSkills.value = [];
|
||||||
|
|
||||||
|
if (isLoadingJobSkill.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoadingJobSkill.value = true;
|
||||||
|
uni.showLoading({
|
||||||
|
title: '加载中...',
|
||||||
|
mask: true
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 从 appUserInfo 接口获取技能数据
|
||||||
|
const response = await appUserInfo();
|
||||||
|
const userInfo = response?.data || {};
|
||||||
|
const appSkillsList = Array.isArray(userInfo?.appSkillsList) ? userInfo.appSkillsList : [];
|
||||||
|
|
||||||
|
// 将 appSkillsList 转换为 splitSkillListByScore 需要的格式
|
||||||
|
const skillList = appSkillsList.map(item => ({
|
||||||
|
skillName: item?.name || item?.nameStr || '',
|
||||||
|
skillScore: item?.levels || item?.levelStr || 0
|
||||||
|
})).filter(item => item.skillName);
|
||||||
|
|
||||||
|
const { possessed, improvement } = splitSkillListByScore(skillList);
|
||||||
|
|
||||||
|
if (possessed.length === 0 && improvement.length === 0) {
|
||||||
|
// 如果 appUserInfo 中没有技能数据,尝试使用推荐职位数据中的技能信息
|
||||||
|
const fallbackSkills = Array.isArray(job?.rawSkills) ? job.rawSkills : [];
|
||||||
|
if (fallbackSkills.length === 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '暂无技能数据',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fallbackSplit = splitSkillListByScore(fallbackSkills);
|
||||||
|
selectedJobPossessedSkills.value = fallbackSplit.possessed;
|
||||||
|
selectedJobImprovementSkills.value = fallbackSplit.improvement;
|
||||||
|
} else {
|
||||||
|
selectedJobPossessedSkills.value = possessed;
|
||||||
|
selectedJobImprovementSkills.value = improvement;
|
||||||
|
}
|
||||||
|
skillDetailPopup.value?.open();
|
||||||
|
} catch (error) {
|
||||||
|
// 接口调用失败,尝试使用推荐职位数据中的技能信息
|
||||||
|
const fallbackSkills = Array.isArray(job?.rawSkills) ? job.rawSkills : [];
|
||||||
|
if (fallbackSkills.length > 0) {
|
||||||
|
const fallbackSplit = splitSkillListByScore(fallbackSkills);
|
||||||
|
selectedJobPossessedSkills.value = fallbackSplit.possessed;
|
||||||
|
selectedJobImprovementSkills.value = fallbackSplit.improvement;
|
||||||
|
skillDetailPopup.value?.open();
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '获取技能信息失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isLoadingJobSkill.value = false;
|
||||||
|
uni.hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理技能弹出层关闭
|
||||||
|
function handleSkillPopupClose() {
|
||||||
|
// 可以在这里处理关闭后的逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理职业推荐技能数据更新
|
||||||
|
function handleRecommendSkillsUpdated(data) {
|
||||||
|
recommendSkillsData.value = {
|
||||||
|
currentJobSkills: data.currentJobSkills || [],
|
||||||
|
recommendedJobs: data.recommendedJobs || []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理职业路径数据更新
|
||||||
|
function handlePathDataUpdated(data) {
|
||||||
|
pathSkillsData.value = {
|
||||||
|
pathData: data.pathData || {
|
||||||
|
start: { title: '', skills: [] },
|
||||||
|
steps: [],
|
||||||
|
end: { title: '', skills: [] }
|
||||||
|
},
|
||||||
|
targetCareer: data.targetCareer || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
getRemindInfo();
|
||||||
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
// 返回本页后,如果之前因为信息缺失未展示内容,则重新检查
|
||||||
|
if (!showContent.value) {
|
||||||
|
hasCheckedRemindInfo = false;
|
||||||
|
getRemindInfo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (remindList.value.length > 0 && !showContent.value) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (remindPopup.value) {
|
||||||
|
openRemindPopup();
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.career-planning-page {
|
||||||
|
width: 100vw;
|
||||||
|
/* #ifdef H5 */
|
||||||
|
height: calc(100vh - var(--window-top) - var(--status-bar-height) - var(--window-bottom));
|
||||||
|
background: url('@/static/icon/background2.png') 0 0 no-repeat;
|
||||||
|
background-size: 100% 728rpx;
|
||||||
|
/* #endif */
|
||||||
|
/* #ifdef MP-WEIXIN */
|
||||||
|
height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
/* #endif */
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* #ifdef MP-WEIXIN */
|
||||||
|
.mp-background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 728rpx;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.content-scroll {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: calc(88rpx + env(safe-area-inset-bottom));
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabbar-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,67 +1,4 @@
|
|||||||
<template>
|
<!--suppress HtmlUnknownTag, NpmUsedModulesInstalled, JSFileReferences -->
|
||||||
<view class="career-planning-page">
|
|
||||||
<!-- 提醒弹窗 -->
|
|
||||||
<RemindPopup
|
|
||||||
ref="remindPopup"
|
|
||||||
:remind-list="remindList"
|
|
||||||
@cancel="handleCancel"
|
|
||||||
@confirm="handleConfirm"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 技能详情弹出层 -->
|
|
||||||
<SkillDetailPopup
|
|
||||||
ref="skillDetailPopup"
|
|
||||||
:job-title="selectedJobTitle"
|
|
||||||
:possessed-skills="selectedJobPossessedSkills"
|
|
||||||
:improvement-skills="selectedJobImprovementSkills"
|
|
||||||
@close="handleSkillPopupClose"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 页面内容 -->
|
|
||||||
<view class="page-content" v-if="showContent">
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<!-- 小程序背景图片 -->
|
|
||||||
<image class="mp-background" src="/static/icon/background2.png" mode="aspectFill"></image>
|
|
||||||
<!-- #endif -->
|
|
||||||
|
|
||||||
<!-- 头部区域 -->
|
|
||||||
<PageHeader
|
|
||||||
:active-tab="activeTab"
|
|
||||||
@tab-change="switchTab"
|
|
||||||
@search-click="handleSearchClick"
|
|
||||||
@menu-click="handleMenuClick"
|
|
||||||
@more-click="handleMoreClick"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 内容区域 -->
|
|
||||||
<scroll-view scroll-y class="content-scroll">
|
|
||||||
<CareerRecommend
|
|
||||||
v-if="activeTab === 0"
|
|
||||||
:current-job-id="currentJobId"
|
|
||||||
:current-job-name="currentJobName"
|
|
||||||
@job-card-click="handleJobCardClick"
|
|
||||||
@skills-updated="handleRecommendSkillsUpdated"
|
|
||||||
/>
|
|
||||||
<CareerPath
|
|
||||||
v-else-if="activeTab === 1"
|
|
||||||
:current-job-name="currentJobName"
|
|
||||||
@path-data-updated="handlePathDataUpdated"
|
|
||||||
/>
|
|
||||||
<SkillDevelopment
|
|
||||||
v-else
|
|
||||||
:current-job-name="currentJobName"
|
|
||||||
@path-data-updated="handlePathDataUpdated"
|
|
||||||
/>
|
|
||||||
</scroll-view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 底部导航栏 -->
|
|
||||||
<view class="tabbar-wrapper" v-if="showContent">
|
|
||||||
<CustomTabBar :currentPage="0" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, inject, nextTick, onMounted } from 'vue';
|
import { ref, inject, nextTick, onMounted } from 'vue';
|
||||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||||
@@ -176,10 +113,10 @@ async function getRemindInfo() {
|
|||||||
let jobName = '';
|
let jobName = '';
|
||||||
if (!hasJobTitle) {
|
if (!hasJobTitle) {
|
||||||
jobName = userInfo?.jobName ??
|
jobName = userInfo?.jobName ??
|
||||||
userInfo?.currentJobName ??
|
userInfo?.currentJobName ??
|
||||||
userInfo?.resume?.jobName ??
|
userInfo?.resume?.jobName ??
|
||||||
userInfo?.resume?.currentJobName ??
|
userInfo?.resume?.currentJobName ??
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
const hasJobInfo = hasJobTitle || (jobName && jobName.trim() !== '');
|
const hasJobInfo = hasJobTitle || (jobName && jobName.trim() !== '');
|
||||||
|
|
||||||
@@ -218,10 +155,10 @@ async function getRemindInfo() {
|
|||||||
} else {
|
} else {
|
||||||
currentJobName.value = jobName;
|
currentJobName.value = jobName;
|
||||||
currentJobId.value = userInfo?.jobId ??
|
currentJobId.value = userInfo?.jobId ??
|
||||||
userInfo?.currentJobId ??
|
userInfo?.currentJobId ??
|
||||||
userInfo?.resume?.jobId ??
|
userInfo?.resume?.jobId ??
|
||||||
userInfo?.resume?.currentJobId ??
|
userInfo?.resume?.currentJobId ??
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
// 信息完整,直接显示页面内容
|
// 信息完整,直接显示页面内容
|
||||||
showContent.value = true;
|
showContent.value = true;
|
||||||
@@ -245,10 +182,10 @@ async function getRemindInfo() {
|
|||||||
let jobName = '';
|
let jobName = '';
|
||||||
if (!hasJobTitle) {
|
if (!hasJobTitle) {
|
||||||
jobName = cachedUserInfo?.jobName ??
|
jobName = cachedUserInfo?.jobName ??
|
||||||
cachedUserInfo?.currentJobName ??
|
cachedUserInfo?.currentJobName ??
|
||||||
cachedUserInfo?.resume?.jobName ??
|
cachedUserInfo?.resume?.jobName ??
|
||||||
cachedUserInfo?.resume?.currentJobName ??
|
cachedUserInfo?.resume?.currentJobName ??
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
const hasJobInfo = hasJobTitle || (jobName && jobName.trim() !== '');
|
const hasJobInfo = hasJobTitle || (jobName && jobName.trim() !== '');
|
||||||
|
|
||||||
@@ -286,10 +223,10 @@ async function getRemindInfo() {
|
|||||||
} else {
|
} else {
|
||||||
currentJobName.value = jobName;
|
currentJobName.value = jobName;
|
||||||
currentJobId.value = cachedUserInfo?.jobId ??
|
currentJobId.value = cachedUserInfo?.jobId ??
|
||||||
cachedUserInfo?.currentJobId ??
|
cachedUserInfo?.currentJobId ??
|
||||||
cachedUserInfo?.resume?.jobId ??
|
cachedUserInfo?.resume?.jobId ??
|
||||||
cachedUserInfo?.resume?.currentJobId ??
|
cachedUserInfo?.resume?.currentJobId ??
|
||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
// 信息完整,直接显示页面内容
|
// 信息完整,直接显示页面内容
|
||||||
showContent.value = true;
|
showContent.value = true;
|
||||||
@@ -349,18 +286,18 @@ function switchTab(index) {
|
|||||||
} else {
|
} else {
|
||||||
// 如果缓存中没有 jobTitles,从其他字段获取
|
// 如果缓存中没有 jobTitles,从其他字段获取
|
||||||
newJobName = currentJobName.value ||
|
newJobName = currentJobName.value ||
|
||||||
(cachedUserInfo?.jobName ??
|
(cachedUserInfo?.jobName ??
|
||||||
cachedUserInfo?.currentJobName ??
|
cachedUserInfo?.currentJobName ??
|
||||||
cachedUserInfo?.resume?.jobName ??
|
cachedUserInfo?.resume?.jobName ??
|
||||||
cachedUserInfo?.resume?.currentJobName ??
|
cachedUserInfo?.resume?.currentJobName ??
|
||||||
'市场专员');
|
'市场专员');
|
||||||
}
|
}
|
||||||
|
|
||||||
const newJobId = cachedUserInfo?.jobId ??
|
const newJobId = cachedUserInfo?.jobId ??
|
||||||
cachedUserInfo?.currentJobId ??
|
cachedUserInfo?.currentJobId ??
|
||||||
cachedUserInfo?.resume?.jobId ??
|
cachedUserInfo?.resume?.jobId ??
|
||||||
cachedUserInfo?.resume?.currentJobId ??
|
cachedUserInfo?.resume?.currentJobId ??
|
||||||
null;
|
null;
|
||||||
|
|
||||||
currentJobId.value = newJobId;
|
currentJobId.value = newJobId;
|
||||||
currentJobName.value = newJobName;
|
currentJobName.value = newJobName;
|
||||||
@@ -532,6 +469,70 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="career-planning-page">
|
||||||
|
<!-- 提醒弹窗 -->
|
||||||
|
<RemindPopup
|
||||||
|
ref="remindPopup"
|
||||||
|
:remind-list="remindList"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@confirm="handleConfirm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 技能详情弹出层 -->
|
||||||
|
<SkillDetailPopup
|
||||||
|
ref="skillDetailPopup"
|
||||||
|
:job-title="selectedJobTitle"
|
||||||
|
:possessed-skills="selectedJobPossessedSkills"
|
||||||
|
:improvement-skills="selectedJobImprovementSkills"
|
||||||
|
@close="handleSkillPopupClose"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 页面内容 -->
|
||||||
|
<div class="page-content" v-if="showContent">
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<!-- 小程序背景图片 -->
|
||||||
|
<image class="mp-background" src="/static/icon/background2.png" mode="aspectFill"></image>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<!-- 头部区域 -->
|
||||||
|
<PageHeader
|
||||||
|
:active-tab="activeTab"
|
||||||
|
@tab-change="switchTab"
|
||||||
|
@search-click="handleSearchClick"
|
||||||
|
@menu-click="handleMenuClick"
|
||||||
|
@more-click="handleMoreClick"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<scroll-view scroll-y class="content-scroll">
|
||||||
|
<CareerRecommend
|
||||||
|
v-if="activeTab === 0"
|
||||||
|
:current-job-id="currentJobId"
|
||||||
|
:current-job-name="currentJobName"
|
||||||
|
@job-card-click="handleJobCardClick"
|
||||||
|
@skills-updated="handleRecommendSkillsUpdated"
|
||||||
|
/>
|
||||||
|
<CareerPath
|
||||||
|
v-else-if="activeTab === 1"
|
||||||
|
:current-job-name="currentJobName"
|
||||||
|
@path-data-updated="handlePathDataUpdated"
|
||||||
|
/>
|
||||||
|
<SkillDevelopment
|
||||||
|
v-else
|
||||||
|
:current-job-name="currentJobName"
|
||||||
|
@path-data-updated="handlePathDataUpdated"
|
||||||
|
/>
|
||||||
|
</scroll-view>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部导航栏 -->
|
||||||
|
<div class="tabbar-wrapper" v-if="showContent">
|
||||||
|
<CustomTabBar :currentPage="0" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.career-planning-page {
|
.career-planning-page {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import { getJobPathPage, getJobPathDetail, getJobPathNum } from '@/apiRc/jobPath.js';
|
import { getJobPathPage, getJobPathDetail, getJobPathNum } from '@/apiRc/service/jobPath.js';
|
||||||
|
|
||||||
// 接收父组件传递的当前职位名称
|
// 接收父组件传递的当前职位名称
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
|
<!--suppress JSFileReferences, NpmUsedModulesInstalled, VueMissingComponentImportInspection -->
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, onMounted } from 'vue';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
import { recommendJob } from '@/apiRc/jobRecommend.js';
|
import { useCareerRecommendationStore } from './store';
|
||||||
|
import { recommendJob } from '@/apiRc/service/jobRecommend.js';
|
||||||
import { appUserInfo } from '@/apiRc/user/user.js';
|
import { appUserInfo } from '@/apiRc/user/user.js';
|
||||||
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
currentJobId: {
|
currentJobId: {
|
||||||
type: [Number, String],
|
type: [ Number, String ],
|
||||||
default: null
|
default: null
|
||||||
},
|
},
|
||||||
currentJobName: {
|
currentJobName: {
|
||||||
@@ -14,7 +17,11 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['job-card-click', 'skills-updated']);
|
const store = useCareerRecommendationStore();
|
||||||
|
|
||||||
|
const emit = defineEmits([ 'job-card-click', 'skills-updated' ]);
|
||||||
|
|
||||||
|
const popupRef = ref();
|
||||||
|
|
||||||
// 数据状态
|
// 数据状态
|
||||||
const skillTags = ref([]);
|
const skillTags = ref([]);
|
||||||
@@ -110,7 +117,7 @@ async function fetchRecommendedJobs() {
|
|||||||
return {
|
return {
|
||||||
id: item?.jobId ?? index,
|
id: item?.jobId ?? index,
|
||||||
jobId: item?.jobId ?? null,
|
jobId: item?.jobId ?? null,
|
||||||
title: item?.jobName || `推荐职位${index + 1}`,
|
title: item?.jobName || `推荐职位${ index + 1 }`,
|
||||||
jobName: item?.jobName || '',
|
jobName: item?.jobName || '',
|
||||||
skills: skillNames,
|
skills: skillNames,
|
||||||
rawSkills: skillList
|
rawSkills: skillList
|
||||||
@@ -145,7 +152,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
// 监听 props 变化,自动获取推荐职位和技能标签
|
// 监听 props 变化,自动获取推荐职位和技能标签
|
||||||
watch(
|
watch(
|
||||||
() => [props.currentJobId, props.currentJobName],
|
() => [ props.currentJobId, props.currentJobName ],
|
||||||
() => {
|
() => {
|
||||||
if (props.currentJobName) {
|
if (props.currentJobName) {
|
||||||
fetchCurrentJobSkills();
|
fetchCurrentJobSkills();
|
||||||
@@ -159,60 +166,60 @@ watch(
|
|||||||
function handleJobCardClick(job) {
|
function handleJobCardClick(job) {
|
||||||
emit('job-card-click', job);
|
emit('job-card-click', job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const eventSelectCurrentJob = () => {
|
||||||
|
popupRef.value?.open('bottom');
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventCloseCurrentJob = () => {
|
||||||
|
popupRef.value?.close('bottom');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="career-recommend">
|
<div class="career-recommend">
|
||||||
<!-- 当前职位信息卡片 -->
|
<!-- 当前职位信息卡片 -->
|
||||||
<div class="info-card">
|
<div class="info-card">
|
||||||
<div class="card-title">当前职位信息</div>
|
<div class="card-title">当前职位信息</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<text class="label">当前职位</text>
|
<span class="label">当前职位</span>
|
||||||
<text class="value">{{ currentJobDisplay }}</text>
|
<span class="value" @click="eventSelectCurrentJob">{{ currentJobDisplay }}</span>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 我的技能标签卡片 -->
|
<!-- 我的技能标签卡片 -->
|
||||||
<div class="info-card">
|
<div class="info-card">
|
||||||
<div class="card-title">我的技能标签</div>
|
<div class="card-title">我的技能标签</div>
|
||||||
<div class="skill-tags">
|
<div class="skill-tags">
|
||||||
<div
|
<div v-for="(skill, index) in skillTags" :key="index" class="skill-tag">
|
||||||
class="skill-tag"
|
{{ skill }}
|
||||||
v-for="(skill, index) in skillTags"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
{{ skill }}
|
|
||||||
</div>
|
|
||||||
<text v-if="!skillTags.length && !isLoadingSkillTags" class="empty-text">暂无技能数据</text>
|
|
||||||
<text v-if="isLoadingSkillTags" class="empty-text">加载中...</text>
|
|
||||||
</div>
|
</div>
|
||||||
|
<span v-if="!skillTags.length && !isLoadingSkillTags" class="empty-text">暂无技能数据</span>
|
||||||
|
<span v-if="isLoadingSkillTags" class="empty-text">加载中...</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 相似推荐职位 -->
|
<!-- 相似推荐职位 -->
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
相似推荐职位
|
相似推荐职位
|
||||||
|
</div>
|
||||||
|
<div v-if="!isLoadingRecommend && recommendedJobs.length === 0" class="empty-text">暂无推荐职位</div>
|
||||||
|
<div v-for="(job, index) in recommendedJobs" :key="index" class="job-item-card" @click="handleJobCardClick(job)">
|
||||||
|
<div class="job-header">
|
||||||
|
<span class="job-title">{{ job.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isLoadingRecommend && recommendedJobs.length === 0" class="empty-text">暂无推荐职位</div>
|
<div class="job-skills">
|
||||||
<div
|
<div v-for="(skill, skillIndex) in job.skills" :key="skillIndex" class="job-skill-tag">
|
||||||
class="job-item-card"
|
{{ skill }}
|
||||||
v-for="(job, index) in recommendedJobs"
|
|
||||||
:key="index"
|
|
||||||
@click="handleJobCardClick(job)"
|
|
||||||
>
|
|
||||||
<div class="job-header">
|
|
||||||
<text class="job-title">{{ job.title }}</text>
|
|
||||||
</div>
|
|
||||||
<div class="job-skills">
|
|
||||||
<div
|
|
||||||
class="job-skill-tag"
|
|
||||||
v-for="(skill, skillIndex) in job.skills"
|
|
||||||
:key="skillIndex"
|
|
||||||
>
|
|
||||||
{{ skill }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<uni-popup ref="popupRef" :border-radius="'20rpx 20rpx 0 0'" :is-mask-click="true" background-color="#FFFFFF" mask-background-color="rgba(255, 255, 255, 0.6)" type="bottom" @mask-click="eventCloseCurrentJob">
|
||||||
|
<div class="">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</uni-popup>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -90,9 +90,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, onMounted } from 'vue';
|
import { ref, computed, onMounted } from 'vue';
|
||||||
import { getJobPathPage, getJobPathDetail, getJobPathNum } from '@/apiRc/jobPath.js';
|
import { getJobPathPage, getJobPathDetail, getJobPathNum } from '@/apiRc/service/jobPath.js';
|
||||||
import { getJobSkillWeight } from '@/apiRc/jobSkill.js';
|
import { getJobSkillWeight } from '@/apiRc/service/jobSkill.js';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// 当前职位名称
|
// 当前职位名称
|
||||||
|
|||||||
188
pages/service/store/career-path.js
Normal file
188
pages/service/store/career-path.js
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { useAuthUserStore, useBasicStore } from './index';
|
||||||
|
import { getCurrentPosition, getPath, getPathDetail } from '@/apiRc/service';
|
||||||
|
|
||||||
|
export const useCareerPathStore = defineStore('career-path', () => {
|
||||||
|
const storeBasic = useBasicStore();
|
||||||
|
const storeUser = useAuthUserStore();
|
||||||
|
|
||||||
|
const profession = ref('');
|
||||||
|
const professions = ref([]);
|
||||||
|
const professionsRef = computed(() => {
|
||||||
|
const userInfo = storeUser.userInfo;
|
||||||
|
if (!userInfo || !userInfo.professions || userInfo.professions.length === 0) {
|
||||||
|
return professions.value;
|
||||||
|
}
|
||||||
|
const userProfessionsLabels = userInfo.professions.map((d) => d.label);
|
||||||
|
let professionsA = [];
|
||||||
|
let professionsB = [];
|
||||||
|
professions.value.filter((d) => userProfessionsLabels.includes(d.label));
|
||||||
|
for (const d of professions.value) {
|
||||||
|
if (userProfessionsLabels.includes(d.label)) {
|
||||||
|
professionsA.push(d);
|
||||||
|
} else {
|
||||||
|
professionsB.push(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (professionsA.length === 0) {
|
||||||
|
professionsA = userInfo.professions;
|
||||||
|
professionsB = professions.value;
|
||||||
|
}
|
||||||
|
return [...professionsA, ...professionsB];
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetCareer = ref('');
|
||||||
|
const paths = ref([]);
|
||||||
|
const pathsRef = computed(() => {
|
||||||
|
return paths.value.filter((d) => {
|
||||||
|
return `${d.startJobId}` === profession.value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = ref([]);
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getCurrentPosition();
|
||||||
|
if (code !== 0) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
professions.value = data.map((d) => {
|
||||||
|
return {
|
||||||
|
label: d.name,
|
||||||
|
value: `${d.jobId}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchDataPath = async () => {
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getPath();
|
||||||
|
if (code !== 0) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
paths.value = data.map((d) => {
|
||||||
|
return {
|
||||||
|
label: d.endJob,
|
||||||
|
value: `${d.startJobId}-${d.endJobId}`,
|
||||||
|
startJobId: d.startJobId
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchResult = async () => {
|
||||||
|
if (!targetCareer.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [startJobId, endJobId] = targetCareer.value.split('-');
|
||||||
|
const params = {
|
||||||
|
startJobId: Number(startJobId),
|
||||||
|
endJobId: Number(endJobId)
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getPathDetail(params);
|
||||||
|
if (code !== 0) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result.value = data.map((d, i) => {
|
||||||
|
return {
|
||||||
|
type: i === 0 ? 'start' : i === data.length - 1 ? 'end' : 'normal',
|
||||||
|
step: i,
|
||||||
|
title: d.name,
|
||||||
|
tags: d.skillNameList.split(',')
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventChange = () => {
|
||||||
|
targetCareer.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventSearch = () => {
|
||||||
|
if (pathsRef.value.length === 0) {
|
||||||
|
ElMessage.warning({
|
||||||
|
message: '当前职业暂无发展路径,敬请期待!',
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!profession.value) {
|
||||||
|
ElMessage.warning({
|
||||||
|
message: '请选择当前职位!',
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!targetCareer.value) {
|
||||||
|
ElMessage.warning({
|
||||||
|
message: '请选择目标职业!',
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void fetchResult();
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => storeBasic.loaded,
|
||||||
|
() => {
|
||||||
|
if (storeBasic.loaded) {
|
||||||
|
void fetchData();
|
||||||
|
void fetchDataPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => professionsRef.value,
|
||||||
|
() => {
|
||||||
|
if (typeof professionsRef.value[0] !== 'undefined') {
|
||||||
|
if (professionsRef.value[0].value) {
|
||||||
|
profession.value = professionsRef.value[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => profession.value,
|
||||||
|
() => {
|
||||||
|
const userInfo = storeUser.userInfo;
|
||||||
|
if (userInfo.professions[0] && profession.value === userInfo.professions[0].value) {
|
||||||
|
targetCareer.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
profession,
|
||||||
|
professionsRef,
|
||||||
|
targetCareer,
|
||||||
|
pathsRef,
|
||||||
|
result,
|
||||||
|
eventChange,
|
||||||
|
eventSearch
|
||||||
|
};
|
||||||
|
});
|
||||||
154
pages/service/store/career-recommendation.js
Normal file
154
pages/service/store/career-recommendation.js
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { useBasicStore, useAuthUserStore } from './index';
|
||||||
|
import { getProfessions, getSkillTags, getRecommend } from '@/apiRc/service';
|
||||||
|
|
||||||
|
export const useCareerRecommendationStore = defineStore('career-recommendation', () => {
|
||||||
|
const storeBasic = useBasicStore();
|
||||||
|
const storeUser = useAuthUserStore();
|
||||||
|
|
||||||
|
const profession = ref('');
|
||||||
|
const professions = ref([]);
|
||||||
|
|
||||||
|
const professionsRef = computed(() => {
|
||||||
|
const userInfo = storeUser.userInfo;
|
||||||
|
if (!userInfo || !userInfo.professions || userInfo.professions.length === 0) {
|
||||||
|
return professions.value;
|
||||||
|
}
|
||||||
|
const userProfessionsLabels = userInfo.professions.map((d) => d.label);
|
||||||
|
let professionsA = [];
|
||||||
|
let professionsB = [];
|
||||||
|
professions.value.filter((d) => userProfessionsLabels.includes(d.label));
|
||||||
|
for (const d of professions.value) {
|
||||||
|
if (userProfessionsLabels.includes(d.label)) {
|
||||||
|
professionsA.push(d);
|
||||||
|
} else {
|
||||||
|
professionsB.push(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (professionsA.length === 0) {
|
||||||
|
professionsA = userInfo.professions;
|
||||||
|
professionsB = professions.value;
|
||||||
|
}
|
||||||
|
return [...professionsA, ...professionsB];
|
||||||
|
});
|
||||||
|
|
||||||
|
const skills = ref([]);
|
||||||
|
|
||||||
|
const skillTags = computed(() => {
|
||||||
|
const userInfo = storeUser.userInfo;
|
||||||
|
if (userInfo.professions[0] && profession.value === userInfo.professions[0].value) {
|
||||||
|
return userInfo.skills.map((d) => d.label);
|
||||||
|
}
|
||||||
|
return skills.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = ref([]);
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getProfessions();
|
||||||
|
if (code !== 0) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
professions.value = data.map((d) => {
|
||||||
|
return {
|
||||||
|
label: d.name,
|
||||||
|
value: `${d.jobId}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchSkillTags = async () => {
|
||||||
|
const params = {
|
||||||
|
jobName: profession.value
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getSkillTags(params);
|
||||||
|
if (code !== 0) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof data !== 'undefined' && Array.isArray(data) && data.length > 0 && data[0]) {
|
||||||
|
skills.value = data[0].skillDetList.map((d) => d.skillName);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchRecommend = async () => {
|
||||||
|
const params = {
|
||||||
|
jobName: profession.value
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getRecommend(params);
|
||||||
|
if (code !== 0) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result.value = data.map((d) => {
|
||||||
|
return {
|
||||||
|
title: d.jobName,
|
||||||
|
tags: d.skillList.map((d) => d.skillName),
|
||||||
|
percentage: d.similarityScore ?? 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventSearch = () => {
|
||||||
|
void fetchRecommend();
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => storeBasic.loaded,
|
||||||
|
() => {
|
||||||
|
if (storeBasic.loaded) {
|
||||||
|
void fetchData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => profession.value,
|
||||||
|
() => {
|
||||||
|
if (profession.value) {
|
||||||
|
void fetchSkillTags();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => professionsRef.value,
|
||||||
|
() => {
|
||||||
|
if (professionsRef.value[0]) {
|
||||||
|
profession.value = professionsRef.value[0].label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
profession,
|
||||||
|
professions,
|
||||||
|
professionsRef,
|
||||||
|
skillTags,
|
||||||
|
result,
|
||||||
|
eventSearch
|
||||||
|
};
|
||||||
|
});
|
||||||
4
pages/service/store/index.js
Normal file
4
pages/service/store/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export * from './user';
|
||||||
|
export * from './career-recommendation';
|
||||||
|
export * from './career-path';
|
||||||
|
export * from './skill-development';
|
||||||
192
pages/service/store/skill-development.js
Normal file
192
pages/service/store/skill-development.js
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { useAuthUserStore, useBasicStore } from './index';
|
||||||
|
import { getCurrentPosition, getPath, getSkill } from '@/apiRc/service';
|
||||||
|
|
||||||
|
export const useSkillDevelopmentStore = defineStore('skill-development', () => {
|
||||||
|
const storeBasic = useBasicStore();
|
||||||
|
const storeUser = useAuthUserStore();
|
||||||
|
|
||||||
|
const profession = ref('');
|
||||||
|
const professions = ref([]);
|
||||||
|
const professionsRef = computed(() => {
|
||||||
|
const userInfo = storeUser.userInfo;
|
||||||
|
if (!userInfo || !userInfo.professions || userInfo.professions.length === 0) {
|
||||||
|
return professions.value;
|
||||||
|
}
|
||||||
|
const userProfessionsLabels = userInfo.professions.map((d) => d.label);
|
||||||
|
let professionsA = [];
|
||||||
|
let professionsB = [];
|
||||||
|
professions.value.filter((d) => userProfessionsLabels.includes(d.label));
|
||||||
|
for (const d of professions.value) {
|
||||||
|
if (userProfessionsLabels.includes(d.label)) {
|
||||||
|
professionsA.push(d);
|
||||||
|
} else {
|
||||||
|
professionsB.push(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (professionsA.length === 0) {
|
||||||
|
professionsA = userInfo.professions;
|
||||||
|
professionsB = professions.value;
|
||||||
|
}
|
||||||
|
return [...professionsA, ...professionsB];
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetCareer = ref('');
|
||||||
|
const paths = ref([]);
|
||||||
|
const pathsRef = computed(() => {
|
||||||
|
return paths.value.filter((d) => {
|
||||||
|
return `${d.startJobId}` === profession.value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = ref([]);
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getCurrentPosition();
|
||||||
|
if (code !== 0) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
professions.value = data.map((d) => {
|
||||||
|
return {
|
||||||
|
label: d.name,
|
||||||
|
value: `${d.jobId}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchDataPath = async () => {
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getPath();
|
||||||
|
if (code !== 0) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
paths.value = data.map((d) => {
|
||||||
|
return {
|
||||||
|
label: d.endJob,
|
||||||
|
value: d.endJob,
|
||||||
|
startJobId: d.startJobId
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchResult = async () => {
|
||||||
|
const current = professionsRef.value.find((d) => d.value === profession.value);
|
||||||
|
const target = pathsRef.value.find((d) => d.value === targetCareer.value);
|
||||||
|
if (!current || !target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
currentJobName: current.label,
|
||||||
|
targetJobName: target.label
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getSkill(params);
|
||||||
|
if (code !== 0) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof data !== 'undefined' && Array.isArray(data) && data.length > 0 && data[0]) {
|
||||||
|
const excludes = data[0].currentSkillDetList.map((d) => d.skillId);
|
||||||
|
result.value = data[0].targetSkillDetList
|
||||||
|
.filter((d) => !excludes.includes(d.skillId))
|
||||||
|
.map((d) => {
|
||||||
|
return {
|
||||||
|
type: d.skillType,
|
||||||
|
title: d.skillName,
|
||||||
|
name: d.skillName,
|
||||||
|
weight: d.skillWeight,
|
||||||
|
score: d.skillScore
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventChange = () => {
|
||||||
|
targetCareer.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const eventSearch = () => {
|
||||||
|
if (pathsRef.value.length === 0) {
|
||||||
|
ElMessage.warning({
|
||||||
|
message: '当前职业暂无发展路径,敬请期待!',
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!profession.value) {
|
||||||
|
ElMessage.warning({
|
||||||
|
message: '请选择当前职位!',
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!targetCareer.value) {
|
||||||
|
ElMessage.warning({
|
||||||
|
message: '请选择目标职业!',
|
||||||
|
duration: 5000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void fetchResult();
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => storeBasic.loaded,
|
||||||
|
() => {
|
||||||
|
if (storeBasic.loaded) {
|
||||||
|
void fetchData();
|
||||||
|
void fetchDataPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => professionsRef.value,
|
||||||
|
() => {
|
||||||
|
if (typeof professionsRef.value[0] !== 'undefined') {
|
||||||
|
if (professionsRef.value[0].value) {
|
||||||
|
profession.value = professionsRef.value[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => profession.value,
|
||||||
|
() => {
|
||||||
|
const userInfo = storeUser.userInfo;
|
||||||
|
if (userInfo.professions[0] && profession.value === userInfo.professions[0].value) {
|
||||||
|
targetCareer.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
profession,
|
||||||
|
professionsRef,
|
||||||
|
targetCareer,
|
||||||
|
pathsRef,
|
||||||
|
result,
|
||||||
|
eventChange,
|
||||||
|
eventSearch
|
||||||
|
};
|
||||||
|
});
|
||||||
66
pages/service/store/user.js
Normal file
66
pages/service/store/user.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { getUserInfo, getUserName } from '@/apiRc/service';
|
||||||
|
|
||||||
|
export const useAuthUserStore = defineStore('auth-user', () => {
|
||||||
|
const token = ref('');
|
||||||
|
|
||||||
|
const userLoaded = ref(false);
|
||||||
|
|
||||||
|
const userInfoRef = ref({
|
||||||
|
userName: '',
|
||||||
|
professions: [],
|
||||||
|
skills: []
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchUserInfo = async () => {
|
||||||
|
const tokenA = await $getItem('tokenA');
|
||||||
|
const cryptogram = await $getItem('cryptogram');
|
||||||
|
if (!cryptogram) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
token.value = cryptogram;
|
||||||
|
try {
|
||||||
|
const { code, msg, data } = await getUserInfo(cryptogram);
|
||||||
|
if (code !== 200) {
|
||||||
|
$emitter.emit('error-message', msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userInfoRef.value.professions = data.jobTitles.map((d) => {
|
||||||
|
return {
|
||||||
|
label: d,
|
||||||
|
value: d
|
||||||
|
};
|
||||||
|
});
|
||||||
|
userInfoRef.value.skills = data.appSkillsList.map((d) => {
|
||||||
|
return {
|
||||||
|
label: d.name,
|
||||||
|
value: d.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const { code: c, msg: m, data: userName } = await getUserName({ accessToken: tokenA });
|
||||||
|
if (c !== 0 || !userName) {
|
||||||
|
$emitter.emit('error-message', m);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userInfoRef.value.userName = userName.name;
|
||||||
|
userLoaded.value = true;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
token,
|
||||||
|
userInfo: userInfoRef,
|
||||||
|
userLoaded,
|
||||||
|
fetchUserInfo
|
||||||
|
};
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user