532 lines
17 KiB
Vue
532 lines
17 KiB
Vue
<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-skills="recommendSkillsData.currentJobSkills"
|
||
:recommended-jobs="recommendSkillsData.recommendedJobs"
|
||
:path-data="pathSkillsData.pathData"
|
||
:target-career="pathSkillsData.targetCareer"
|
||
/>
|
||
</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 } from '@dcloudio/uni-app';
|
||
import { getJobSkillDetail } from '@/apiRc/jobSkill.js';
|
||
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;
|
||
|
||
// 如果接口返回的数据中没有 idCard,则从缓存中读取
|
||
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||
if (!idCard || idCard === null || idCard === '') {
|
||
idCard = cachedUserInfo?.resume?.idCard ?? cachedUserInfo?.idCard ?? null;
|
||
}
|
||
|
||
// 优先从接口数据中获取职位信息
|
||
currentJobId.value = userInfo?.jobId ??
|
||
userInfo?.currentJobId ??
|
||
userInfo?.resume?.jobId ??
|
||
userInfo?.resume?.currentJobId ??
|
||
null;
|
||
currentJobName.value = userInfo?.jobName ??
|
||
userInfo?.currentJobName ??
|
||
userInfo?.resume?.jobName ??
|
||
userInfo?.resume?.currentJobName ??
|
||
'';
|
||
|
||
// 如果接口数据中没有职位信息,从缓存中读取
|
||
if (!currentJobId.value && !currentJobName.value) {
|
||
currentJobId.value = cachedUserInfo?.jobId ??
|
||
cachedUserInfo?.currentJobId ??
|
||
cachedUserInfo?.resume?.jobId ??
|
||
cachedUserInfo?.resume?.currentJobId ??
|
||
null;
|
||
currentJobName.value = cachedUserInfo?.jobName ??
|
||
cachedUserInfo?.currentJobName ??
|
||
cachedUserInfo?.resume?.jobName ??
|
||
cachedUserInfo?.resume?.currentJobName ??
|
||
'';
|
||
}
|
||
|
||
// 如果还是没有职位信息,使用默认值
|
||
if (!currentJobName.value) {
|
||
currentJobName.value = '市场专员';
|
||
}
|
||
|
||
// 判断 idCard 是否存在(包括空字符串、undefined、null 的情况)
|
||
const hasIdCard = idCard !== null && idCard !== undefined && idCard !== '';
|
||
|
||
if (!hasIdCard) {
|
||
remindList.value = ['请完善个人信息'];
|
||
} else {
|
||
remindList.value = ['暂无待完善信息'];
|
||
}
|
||
|
||
setTimeout(() => {
|
||
openRemindPopup();
|
||
}, 500);
|
||
} catch (error) {
|
||
// 接口调用失败时,使用缓存作为降级方案
|
||
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||
const idCard = cachedUserInfo?.resume?.idCard ?? cachedUserInfo?.idCard ?? null;
|
||
|
||
// 从缓存中获取职位信息
|
||
currentJobId.value = cachedUserInfo?.jobId ??
|
||
cachedUserInfo?.currentJobId ??
|
||
cachedUserInfo?.resume?.jobId ??
|
||
cachedUserInfo?.resume?.currentJobId ??
|
||
null;
|
||
currentJobName.value = cachedUserInfo?.jobName ??
|
||
cachedUserInfo?.currentJobName ??
|
||
cachedUserInfo?.resume?.jobName ??
|
||
cachedUserInfo?.resume?.currentJobName ??
|
||
'';
|
||
// 如果缓存中没有职位信息,使用默认值
|
||
if (!currentJobName.value) {
|
||
currentJobName.value = '市场专员';
|
||
}
|
||
|
||
if (!idCard || idCard === null || idCard === '') {
|
||
remindList.value = ['请完善个人信息'];
|
||
} else {
|
||
remindList.value = ['暂无待完善信息'];
|
||
}
|
||
|
||
setTimeout(() => {
|
||
openRemindPopup();
|
||
}, 500);
|
||
}
|
||
}
|
||
|
||
// 取消按钮
|
||
function handleCancel() {
|
||
remindPopup.value?.close();
|
||
navBack();
|
||
}
|
||
|
||
// 确认按钮
|
||
async function handleConfirm() {
|
||
remindPopup.value?.close();
|
||
|
||
// 直接从缓存中读取 idCard(因为接口返回的数据中没有 idCard)
|
||
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||
let idCard = cachedUserInfo?.resume?.idCard ?? cachedUserInfo?.idCard ?? null;
|
||
|
||
// 如果缓存中也没有,尝试从接口获取(虽然接口通常也没有)
|
||
if (!idCard || idCard === null || idCard === '') {
|
||
try {
|
||
const response = await appUserInfo();
|
||
const userInfo = response?.data || {};
|
||
idCard = userInfo?.resume?.idCard ?? userInfo?.idCard ?? null;
|
||
} catch (error) {
|
||
// 接口调用失败,继续使用缓存数据
|
||
}
|
||
}
|
||
|
||
// 如果 idCard 为空,才跳转到完善信息页面
|
||
if (!idCard || idCard === null || idCard === '') {
|
||
navTo('/pages/complete-info/complete-info');
|
||
return;
|
||
}
|
||
|
||
// 如果 idCard 存在,说明已经完善了信息,直接显示页面内容
|
||
// 从缓存中更新职位信息
|
||
currentJobId.value = cachedUserInfo?.jobId ??
|
||
cachedUserInfo?.currentJobId ??
|
||
cachedUserInfo?.resume?.jobId ??
|
||
cachedUserInfo?.resume?.currentJobId ??
|
||
null;
|
||
currentJobName.value = cachedUserInfo?.jobName ??
|
||
cachedUserInfo?.currentJobName ??
|
||
cachedUserInfo?.resume?.jobName ??
|
||
cachedUserInfo?.resume?.currentJobName ??
|
||
'';
|
||
if (!currentJobName.value) {
|
||
currentJobName.value = '市场专员';
|
||
}
|
||
|
||
showContent.value = true;
|
||
}
|
||
|
||
// 切换tab
|
||
function switchTab(index) {
|
||
activeTab.value = index;
|
||
|
||
if (index === 0 && !currentJobId.value) {
|
||
const cachedUserInfo = uni.getStorageSync('userInfo') || {};
|
||
|
||
const newJobId = cachedUserInfo?.jobId ??
|
||
cachedUserInfo?.currentJobId ??
|
||
cachedUserInfo?.resume?.jobId ??
|
||
cachedUserInfo?.resume?.currentJobId ??
|
||
null;
|
||
|
||
const newJobName = currentJobName.value ||
|
||
(cachedUserInfo?.jobName ??
|
||
cachedUserInfo?.currentJobName ??
|
||
cachedUserInfo?.resume?.jobName ??
|
||
cachedUserInfo?.resume?.currentJobName ??
|
||
'市场专员');
|
||
|
||
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
|
||
});
|
||
|
||
const fallbackSkills = Array.isArray(job?.rawSkills) ? job.rawSkills : [];
|
||
const params = {};
|
||
if (job?.jobId) {
|
||
params.jobId = job.jobId;
|
||
}
|
||
if (job?.title) {
|
||
params.jobName = job.title;
|
||
} else if (job?.jobName) {
|
||
params.jobName = job.jobName;
|
||
}
|
||
|
||
try {
|
||
const response = await getJobSkillDetail(params);
|
||
const skillList = Array.isArray(response?.data) ? response.data : [];
|
||
const { possessed, improvement } = splitSkillListByScore(skillList);
|
||
|
||
if (possessed.length === 0 && improvement.length === 0) {
|
||
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) {
|
||
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();
|
||
});
|
||
|
||
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>
|