Files
ks-app-employment-service/pages/service/career-planning.vue
2025-12-05 17:59:37 +08:00

561 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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;
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() !== '';
});
// 判断信息是否完整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() !== '';
});
// 判断信息是否完整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();
// 跳转到完善信息页面
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>