= 职业规划推荐
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user