= 职业规划推荐
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 getAddedJobs(params) {
|
||||
// return request({
|
||||
// url: '/personnel/personBaseInfo/postRecommend',
|
||||
// method: 'get',
|
||||
// params,
|
||||
// })
|
||||
// }
|
||||
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'
|
||||
})
|
||||
}
|
||||
|
||||
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>
|
||||
<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>
|
||||
|
||||
<!--suppress HtmlUnknownTag, NpmUsedModulesInstalled, JSFileReferences -->
|
||||
<script setup>
|
||||
import { ref, inject, nextTick, onMounted } from 'vue';
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
||||
@@ -153,13 +90,13 @@ 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') || {};
|
||||
@@ -167,22 +104,22 @@ async function getRemindInfo() {
|
||||
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 ??
|
||||
'';
|
||||
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 不为空)
|
||||
@@ -190,14 +127,14 @@ async function getRemindInfo() {
|
||||
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 = [];
|
||||
@@ -217,55 +154,55 @@ async function getRemindInfo() {
|
||||
currentJobName.value = jobTitles[0];
|
||||
} else {
|
||||
currentJobName.value = jobName;
|
||||
currentJobId.value = userInfo?.jobId ??
|
||||
userInfo?.currentJobId ??
|
||||
userInfo?.resume?.jobId ??
|
||||
userInfo?.resume?.currentJobId ??
|
||||
null;
|
||||
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 ??
|
||||
'';
|
||||
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 = [];
|
||||
@@ -285,17 +222,17 @@ async function getRemindInfo() {
|
||||
currentJobName.value = cachedJobTitles[0];
|
||||
} else {
|
||||
currentJobName.value = jobName;
|
||||
currentJobId.value = cachedUserInfo?.jobId ??
|
||||
cachedUserInfo?.currentJobId ??
|
||||
cachedUserInfo?.resume?.jobId ??
|
||||
cachedUserInfo?.resume?.currentJobId ??
|
||||
null;
|
||||
currentJobId.value = cachedUserInfo?.jobId ??
|
||||
cachedUserInfo?.currentJobId ??
|
||||
cachedUserInfo?.resume?.jobId ??
|
||||
cachedUserInfo?.resume?.currentJobId ??
|
||||
null;
|
||||
}
|
||||
// 信息完整,直接显示页面内容
|
||||
showContent.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
openRemindPopup();
|
||||
}, 500);
|
||||
@@ -311,18 +248,18 @@ function handleCancel() {
|
||||
// 确认按钮
|
||||
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');
|
||||
@@ -336,32 +273,32 @@ async function handleConfirm() {
|
||||
// 切换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 ??
|
||||
'市场专员');
|
||||
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;
|
||||
|
||||
|
||||
const newJobId = cachedUserInfo?.jobId ??
|
||||
cachedUserInfo?.currentJobId ??
|
||||
cachedUserInfo?.resume?.jobId ??
|
||||
cachedUserInfo?.resume?.currentJobId ??
|
||||
null;
|
||||
|
||||
currentJobId.value = newJobId;
|
||||
currentJobName.value = newJobName;
|
||||
}
|
||||
@@ -437,13 +374,13 @@ async function handleJobCardClick(job) {
|
||||
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) {
|
||||
@@ -532,6 +469,70 @@ onMounted(() => {
|
||||
});
|
||||
</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>
|
||||
.career-planning-page {
|
||||
width: 100vw;
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
|
||||
<script setup>
|
||||
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({
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<!--suppress JSFileReferences, NpmUsedModulesInstalled, VueMissingComponentImportInspection -->
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { recommendJob } from '@/apiRc/jobRecommend.js';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useCareerRecommendationStore } from './store';
|
||||
import { recommendJob } from '@/apiRc/service/jobRecommend.js';
|
||||
import { appUserInfo } from '@/apiRc/user/user.js';
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
currentJobId: {
|
||||
type: [Number, String],
|
||||
type: [ Number, String ],
|
||||
default: null
|
||||
},
|
||||
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([]);
|
||||
@@ -110,7 +117,7 @@ async function fetchRecommendedJobs() {
|
||||
return {
|
||||
id: item?.jobId ?? index,
|
||||
jobId: item?.jobId ?? null,
|
||||
title: item?.jobName || `推荐职位${index + 1}`,
|
||||
title: item?.jobName || `推荐职位${ index + 1 }`,
|
||||
jobName: item?.jobName || '',
|
||||
skills: skillNames,
|
||||
rawSkills: skillList
|
||||
@@ -145,7 +152,7 @@ onMounted(() => {
|
||||
|
||||
// 监听 props 变化,自动获取推荐职位和技能标签
|
||||
watch(
|
||||
() => [props.currentJobId, props.currentJobName],
|
||||
() => [ props.currentJobId, props.currentJobName ],
|
||||
() => {
|
||||
if (props.currentJobName) {
|
||||
fetchCurrentJobSkills();
|
||||
@@ -159,60 +166,60 @@ watch(
|
||||
function handleJobCardClick(job) {
|
||||
emit('job-card-click', job);
|
||||
}
|
||||
|
||||
const eventSelectCurrentJob = () => {
|
||||
popupRef.value?.open('bottom');
|
||||
};
|
||||
|
||||
const eventCloseCurrentJob = () => {
|
||||
popupRef.value?.close('bottom');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="career-recommend">
|
||||
<!-- 当前职位信息卡片 -->
|
||||
<div class="info-card">
|
||||
<div class="card-title">当前职位信息</div>
|
||||
<div class="card-content">
|
||||
<text class="label">当前职位</text>
|
||||
<text class="value">{{ currentJobDisplay }}</text>
|
||||
</div>
|
||||
<div class="career-recommend">
|
||||
<!-- 当前职位信息卡片 -->
|
||||
<div class="info-card">
|
||||
<div class="card-title">当前职位信息</div>
|
||||
<div class="card-content">
|
||||
<span class="label">当前职位</span>
|
||||
<span class="value" @click="eventSelectCurrentJob">{{ currentJobDisplay }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 我的技能标签卡片 -->
|
||||
<div class="info-card">
|
||||
<div class="card-title">我的技能标签</div>
|
||||
<div class="skill-tags">
|
||||
<div
|
||||
class="skill-tag"
|
||||
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 class="info-card">
|
||||
<div class="card-title">我的技能标签</div>
|
||||
<div class="skill-tags">
|
||||
<div v-for="(skill, index) in skillTags" :key="index" class="skill-tag">
|
||||
{{ skill }}
|
||||
</div>
|
||||
<span v-if="!skillTags.length && !isLoadingSkillTags" class="empty-text">暂无技能数据</span>
|
||||
<span v-if="isLoadingSkillTags" class="empty-text">加载中...</span>
|
||||
</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 v-if="!isLoadingRecommend && recommendedJobs.length === 0" class="empty-text">暂无推荐职位</div>
|
||||
<div
|
||||
class="job-item-card"
|
||||
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 class="job-skills">
|
||||
<div v-for="(skill, skillIndex) in job.skills" :key="skillIndex" class="job-skill-tag">
|
||||
{{ skill }}
|
||||
</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>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -6,22 +6,22 @@
|
||||
<uni-icons type="search" size="18" color="#286BFA"></uni-icons>
|
||||
<text class="title-text">职业技能查询</text>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="input-group">
|
||||
<view class="input-item">
|
||||
<text class="input-label">当前职位</text>
|
||||
<input
|
||||
class="input-field"
|
||||
<input
|
||||
class="input-field"
|
||||
:value="currentPosition"
|
||||
placeholder="市场专员"
|
||||
placeholder-style="color: #999999"
|
||||
disabled
|
||||
/>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="input-item">
|
||||
<text class="input-label">目标职业</text>
|
||||
<picker
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="targetCareerOptions"
|
||||
range-key="label"
|
||||
@@ -37,7 +37,7 @@
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<button class="query-btn" @click="handleQuery">
|
||||
<text>查询技能发展路径</text>
|
||||
<uni-icons type="search" size="18" color="#FFFFFF"></uni-icons>
|
||||
@@ -52,19 +52,19 @@
|
||||
<uni-icons type="person-filled" size="18" color="#000000"></uni-icons>
|
||||
<text class="title-text">技能发展路径</text>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="intro-text">
|
||||
基于您的当前职业和目标职业,以下是您需要重点发展的技能:
|
||||
</view>
|
||||
|
||||
|
||||
<view class="skill-list">
|
||||
<view v-if="isLoadingSkills" class="empty-text">加载中...</view>
|
||||
<view v-else-if="!hasQueried" class="empty-text">请先查询职业路径以获取技能发展数据</view>
|
||||
<view v-else-if="skillList.length === 0" class="empty-text">暂无数据</view>
|
||||
<view
|
||||
<view
|
||||
v-else
|
||||
class="skill-item"
|
||||
v-for="(skill, index) in skillList"
|
||||
class="skill-item"
|
||||
v-for="(skill, index) in skillList"
|
||||
:key="index"
|
||||
>
|
||||
<view class="skill-header">
|
||||
@@ -75,9 +75,9 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="skill-tags" v-if="skill.tags && skill.tags.length > 0">
|
||||
<view
|
||||
class="skill-tag"
|
||||
v-for="(tag, tagIndex) in skill.tags"
|
||||
<view
|
||||
class="skill-tag"
|
||||
v-for="(tag, tagIndex) in skill.tags"
|
||||
:key="tagIndex"
|
||||
>
|
||||
{{ tag }}
|
||||
@@ -90,9 +90,9 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { getJobPathPage, getJobPathDetail, getJobPathNum } from '@/apiRc/jobPath.js';
|
||||
import { getJobSkillWeight } from '@/apiRc/jobSkill.js';
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { getJobPathPage, getJobPathDetail, getJobPathNum } from '@/apiRc/service/jobPath.js';
|
||||
import { getJobSkillWeight } from '@/apiRc/service/jobSkill.js';
|
||||
|
||||
const props = defineProps({
|
||||
// 当前职位名称
|
||||
@@ -138,9 +138,9 @@ async function fetchTargetCareerOptions(keyword = '') {
|
||||
pageNo: 1,
|
||||
pageSize: 100
|
||||
});
|
||||
|
||||
|
||||
const list = response?.data?.list || response?.list || [];
|
||||
|
||||
|
||||
targetCareerOptions.value = list.map(item => ({
|
||||
label: item.endJob || item.startJob || '未知职位',
|
||||
value: item.id,
|
||||
@@ -186,16 +186,16 @@ async function loadPathDetail(jobPathId) {
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const requestParams = {
|
||||
jobPathId: jobPathId
|
||||
};
|
||||
|
||||
|
||||
const response = await getJobPathDetail(requestParams);
|
||||
|
||||
|
||||
const details = Array.isArray(response?.data) ? response.data : [];
|
||||
|
||||
|
||||
if (details.length === 0) {
|
||||
localPathData.value = {
|
||||
start: { title: '暂无数据', skills: [] },
|
||||
@@ -223,7 +223,7 @@ async function loadPathDetail(jobPathId) {
|
||||
steps,
|
||||
end
|
||||
};
|
||||
|
||||
|
||||
// 通知父组件路径数据已更新
|
||||
emit('path-data-updated', {
|
||||
pathData: localPathData.value,
|
||||
@@ -282,7 +282,7 @@ async function handleQuery() {
|
||||
async function fetchSkillWeight() {
|
||||
// 获取当前职位(使用界面上显示的值)
|
||||
const currentJob = currentPosition.value || props.currentJobName || '';
|
||||
|
||||
|
||||
if (!currentJob) {
|
||||
skillList.value = [];
|
||||
uni.showToast({
|
||||
@@ -291,12 +291,12 @@ async function fetchSkillWeight() {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 获取目标职业(使用界面上选择的值)
|
||||
const targetCareer = selectedTargetIndex.value >= 0
|
||||
? targetCareerOptions.value[selectedTargetIndex.value]?.label
|
||||
const targetCareer = selectedTargetIndex.value >= 0
|
||||
? targetCareerOptions.value[selectedTargetIndex.value]?.label
|
||||
: '';
|
||||
|
||||
|
||||
if (!targetCareer) {
|
||||
skillList.value = [];
|
||||
uni.showToast({
|
||||
@@ -305,31 +305,31 @@ async function fetchSkillWeight() {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
isLoadingSkills.value = true;
|
||||
uni.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
const response = await getJobSkillWeight({
|
||||
currentJobName: currentJob,
|
||||
targetJobName: targetCareer
|
||||
});
|
||||
|
||||
|
||||
// 标记已经查询过
|
||||
hasQueried.value = true;
|
||||
|
||||
|
||||
// 处理接口返回的数据
|
||||
const responseData = response?.data || response || [];
|
||||
const dataItem = Array.isArray(responseData) ? responseData[0] : responseData;
|
||||
|
||||
|
||||
// 合并当前职位和目标职位的技能列表
|
||||
const currentSkills = Array.isArray(dataItem?.currentSkillDetList) ? dataItem.currentSkillDetList : [];
|
||||
const targetSkills = Array.isArray(dataItem?.targetSkillDetList) ? dataItem.targetSkillDetList : [];
|
||||
const allSkills = [...currentSkills, ...targetSkills];
|
||||
|
||||
|
||||
// 转换为组件需要的格式
|
||||
skillList.value = allSkills.map(item => ({
|
||||
name: item?.skillName || item?.name || '',
|
||||
@@ -343,7 +343,7 @@ async function fetchSkillWeight() {
|
||||
const scoreB = parseFloat(b.score) || 0;
|
||||
return scoreB - scoreA;
|
||||
});
|
||||
|
||||
|
||||
if (skillList.value.length === 0) {
|
||||
uni.showToast({
|
||||
title: '暂无技能数据',
|
||||
@@ -385,7 +385,7 @@ onMounted(async () => {
|
||||
padding: 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
|
||||
|
||||
.section-title {
|
||||
margin-top: 0;
|
||||
}
|
||||
@@ -482,7 +482,7 @@ button::after {
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
|
||||
.content-section & {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
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