flat:职业图谱小程序端页面完成
This commit is contained in:
14
apiRc/user/user.js
Normal file
14
apiRc/user/user.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* @Date: 2025-01-XX
|
||||||
|
* @LastEditors:
|
||||||
|
* @LastEditTime:
|
||||||
|
*/
|
||||||
|
import request from '@/utilsRc/request'
|
||||||
|
|
||||||
|
// 获取用户信息(职业规划推荐用)
|
||||||
|
export function appUserInfo() {
|
||||||
|
return request({
|
||||||
|
url: '/app/user/appUserInfo',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
4528
package-lock.json
generated
4528
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -150,6 +150,14 @@
|
|||||||
// "navigationStyle": "custom"
|
// "navigationStyle": "custom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/service/career-planning",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "职业规划推荐",
|
||||||
|
"navigationBarTitleTextSize": "30rpx",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/mine/company-mine",
|
"path": "pages/mine/company-mine",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@@ -124,6 +124,12 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="service-title" style="overflow:unset">高校毕业生<br/>智慧就业服务</view>
|
<view class="service-title" style="overflow:unset">高校毕业生<br/>智慧就业服务</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="service-item press-button" @click="handleServiceClick('career-planning')">
|
||||||
|
<view class="service-icon service-icon-11">
|
||||||
|
<image class="service-icon-img" src="/static/icon/antOutline.png" mode="aspectFit"></image>
|
||||||
|
</view>
|
||||||
|
<view class="service-title">职业规划推荐</view>
|
||||||
|
</view>
|
||||||
<view class="service-item press-button" @click="navToTestPage">
|
<view class="service-item press-button" @click="navToTestPage">
|
||||||
<view class="service-icon service-icon-10">
|
<view class="service-icon service-icon-10">
|
||||||
<uni-icons type="gear-filled" size="32" color="#FFFFFF"></uni-icons>
|
<uni-icons type="gear-filled" size="32" color="#FFFFFF"></uni-icons>
|
||||||
@@ -1319,15 +1325,17 @@ defineExpose({ loadData });
|
|||||||
align-items: center
|
align-items: center
|
||||||
justify-content: center
|
justify-content: center
|
||||||
.service-icon-11
|
.service-icon-11
|
||||||
background: linear-gradient(135deg, #FF9800 0%, #FFB74D 100%)
|
background-color: #72A4FC
|
||||||
position: relative
|
position: relative
|
||||||
&::before
|
display: flex
|
||||||
content: '📈'
|
align-items: center
|
||||||
position: absolute
|
justify-content: center
|
||||||
top: 50%
|
.service-icon-img
|
||||||
left: 50%
|
width: 42rpx
|
||||||
transform: translate(-50%, -50%)
|
height: 42rpx
|
||||||
font-size: 32rpx
|
position: relative
|
||||||
|
z-index: 1
|
||||||
|
filter: brightness(1.8) contrast(1.4)
|
||||||
.service-icon-12
|
.service-icon-12
|
||||||
background: linear-gradient(135deg, #4CAF50 0%, #81C784 100%)
|
background: linear-gradient(135deg, #4CAF50 0%, #81C784 100%)
|
||||||
position: relative
|
position: relative
|
||||||
|
|||||||
247
pages/service/career-planning.vue
Normal file
247
pages/service/career-planning.vue
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
<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">
|
||||||
|
<component
|
||||||
|
:is="currentComponent"
|
||||||
|
@job-card-click="handleJobCardClick"
|
||||||
|
></component>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部导航栏 -->
|
||||||
|
<view class="tabbar-wrapper" v-if="showContent">
|
||||||
|
<CustomTabBar :currentPage="0" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, inject, nextTick, onMounted, computed } from 'vue';
|
||||||
|
import { onLoad } 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 } = 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 currentComponent = computed(() => {
|
||||||
|
const components = [CareerRecommend, CareerPath, SkillDevelopment];
|
||||||
|
return components[activeTab.value];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
function openRemindPopup() {
|
||||||
|
nextTick(() => {
|
||||||
|
if (remindPopup.value) {
|
||||||
|
remindPopup.value.open();
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (remindPopup.value) {
|
||||||
|
remindPopup.value.open();
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
remindPopup.value?.open();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取提醒信息的接口
|
||||||
|
async function getRemindInfo() {
|
||||||
|
try {
|
||||||
|
// 接口已准备好,但暂时不使用(按用户要求)
|
||||||
|
// const response = await appUserInfo();
|
||||||
|
// if (response && response.code === 200) {
|
||||||
|
// const data = response.data || {};
|
||||||
|
// const jobTitles = data.jobTitles || [];
|
||||||
|
// const appSkillsList = data.appSkillsList || [];
|
||||||
|
//
|
||||||
|
// remindList.value = [];
|
||||||
|
// if (!jobTitles || jobTitles.length === 0) {
|
||||||
|
// remindList.value.push('求职期望;');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (remindList.value.length > 0) {
|
||||||
|
// openRemindPopup();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 暂时使用测试数据显示弹窗
|
||||||
|
remindList.value = ['求职期望;'];
|
||||||
|
openRemindPopup();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取提醒信息失败:', error);
|
||||||
|
remindList.value = ['求职期望;'];
|
||||||
|
openRemindPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消按钮
|
||||||
|
function handleCancel() {
|
||||||
|
remindPopup.value?.close();
|
||||||
|
navBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认按钮
|
||||||
|
function handleConfirm() {
|
||||||
|
remindPopup.value?.close();
|
||||||
|
showContent.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换tab
|
||||||
|
function switchTab(index) {
|
||||||
|
activeTab.value = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索点击
|
||||||
|
function handleSearchClick() {
|
||||||
|
// TODO: 跳转到搜索页面
|
||||||
|
console.log('搜索点击');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 菜单点击
|
||||||
|
function handleMenuClick() {
|
||||||
|
console.log('菜单点击');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更多点击
|
||||||
|
function handleMoreClick() {
|
||||||
|
console.log('更多点击');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理职位卡片点击
|
||||||
|
function handleJobCardClick(job) {
|
||||||
|
selectedJobTitle.value = job.title || '';
|
||||||
|
selectedJobPossessedSkills.value = job.possessedSkills || [];
|
||||||
|
selectedJobImprovementSkills.value = job.improvementSkills || [];
|
||||||
|
skillDetailPopup.value?.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理技能弹出层关闭
|
||||||
|
function handleSkillPopupClose() {
|
||||||
|
// 可以在这里处理关闭后的逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
getRemindInfo();
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (remindList.value.length > 0 && !showContent.value) {
|
||||||
|
openRemindPopup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</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;
|
||||||
|
padding-bottom: 88rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.content-scroll {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabbar-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
394
pages/service/components/CareerPath.vue
Normal file
394
pages/service/components/CareerPath.vue
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
<template>
|
||||||
|
<view class="career-path">
|
||||||
|
<!-- 职业路径查询区域 -->
|
||||||
|
<view class="query-section">
|
||||||
|
<view class="section-title">
|
||||||
|
<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"
|
||||||
|
:value="currentPosition"
|
||||||
|
placeholder="前端开发工程师"
|
||||||
|
placeholder-style="color: #999999"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="input-item">
|
||||||
|
<text class="input-label">目标职业</text>
|
||||||
|
<picker
|
||||||
|
mode="selector"
|
||||||
|
:range="targetCareerOptions"
|
||||||
|
range-key="label"
|
||||||
|
:value="selectedTargetIndex"
|
||||||
|
@change="handleTargetChange"
|
||||||
|
>
|
||||||
|
<view class="picker-field">
|
||||||
|
<text :class="selectedTargetIndex >= 0 ? 'picker-text' : 'picker-placeholder'">
|
||||||
|
{{ selectedTargetIndex >= 0 ? targetCareerOptions[selectedTargetIndex].label : '请选择目标职业' }}
|
||||||
|
</text>
|
||||||
|
<uni-icons type="arrowdown" size="16" color="#999999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<button class="query-btn" @click="handleQuery">
|
||||||
|
<text>查询职业发展路径</text>
|
||||||
|
<uni-icons type="search" size="18" color="#FFFFFF"></uni-icons>
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 职业发展路径区域 -->
|
||||||
|
<view class="path-section">
|
||||||
|
<view class="section-title">
|
||||||
|
<uni-icons type="person-filled" size="24" color="#000000"></uni-icons>
|
||||||
|
<text class="title-text">职业发展路径</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="timeline">
|
||||||
|
<!-- 起点 -->
|
||||||
|
<view class="timeline-item start">
|
||||||
|
<view class="timeline-marker start-marker"></view>
|
||||||
|
<view class="timeline-content">
|
||||||
|
<view class="step-title">起点: {{ pathData.start.title }}</view>
|
||||||
|
<view class="skill-tags">
|
||||||
|
<view
|
||||||
|
class="skill-tag"
|
||||||
|
v-for="(skill, index) in pathData.start.skills"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ skill }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 步骤 -->
|
||||||
|
<view
|
||||||
|
class="timeline-item step"
|
||||||
|
v-for="(step, index) in pathData.steps"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<view class="timeline-marker step-marker"></view>
|
||||||
|
<view class="timeline-content">
|
||||||
|
<view class="step-title">第{{ index + 1 }}步: {{ step.title }}</view>
|
||||||
|
<view class="skill-tags">
|
||||||
|
<view
|
||||||
|
class="skill-tag"
|
||||||
|
v-for="(skill, sIndex) in step.skills"
|
||||||
|
:key="sIndex"
|
||||||
|
>
|
||||||
|
{{ skill }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 终点 -->
|
||||||
|
<view class="timeline-item end">
|
||||||
|
<view class="timeline-marker end-marker"></view>
|
||||||
|
<view class="timeline-content">
|
||||||
|
<view class="step-title">终点: {{ pathData.end.title }}</view>
|
||||||
|
<view class="skill-tags">
|
||||||
|
<view
|
||||||
|
class="skill-tag"
|
||||||
|
v-for="(skill, index) in pathData.end.skills"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ skill }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
|
||||||
|
// 当前职位(从接口获取,只读)
|
||||||
|
const currentPosition = ref('前端开发工程师');
|
||||||
|
|
||||||
|
// 目标职业选项(根据当前职位动态获取)
|
||||||
|
const targetCareerOptions = ref([
|
||||||
|
{ label: '互联网/IT', value: 'internet_it' },
|
||||||
|
{ label: '高级前端开发工程师', value: 'senior_frontend' },
|
||||||
|
{ label: '前端架构师', value: 'frontend_architect' },
|
||||||
|
{ label: '技术总监', value: 'tech_director' }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const selectedTargetIndex = ref(-1);
|
||||||
|
|
||||||
|
// 职业路径数据(后续从接口获取)
|
||||||
|
const pathData = ref({
|
||||||
|
start: {
|
||||||
|
title: '初级网络安全管理员',
|
||||||
|
skills: ['React', 'Node.js', 'Typescript', '数据库']
|
||||||
|
},
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
title: '中级网络安全工程师',
|
||||||
|
skills: ['Vue3', '微前端', '性能优化', '团队管理']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '资深安全顾问(横向发展)',
|
||||||
|
skills: ['架构设计', '技术选型', '工程化', '团队领导']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '高级网络安全架构师',
|
||||||
|
skills: ['架构设计', '技术选型', '工程化', '团队领导']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '技术部门经理',
|
||||||
|
skills: ['架构设计', '技术选型', '工程化', '团队领导']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
end: {
|
||||||
|
title: 'IT 项目总监',
|
||||||
|
skills: ['架构设计', '技术选型', '工程化', '团队领导']
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取当前职位信息(从接口获取)
|
||||||
|
async function getCurrentPosition() {
|
||||||
|
// TODO: 调用接口获取当前职位
|
||||||
|
// const response = await getCareerCurrentPosition();
|
||||||
|
// if (response && response.code === 200) {
|
||||||
|
// currentPosition.value = response.data?.position || '';
|
||||||
|
// // 根据当前职位获取目标职业选项
|
||||||
|
// await getTargetCareerOptions(currentPosition.value);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据当前职位获取目标职业选项
|
||||||
|
async function getTargetCareerOptions(position) {
|
||||||
|
// TODO: 调用接口获取目标职业选项
|
||||||
|
// const response = await getTargetCareers(position);
|
||||||
|
// if (response && response.code === 200) {
|
||||||
|
// targetCareerOptions.value = response.data || [];
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTargetChange(e) {
|
||||||
|
selectedTargetIndex.value = e.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleQuery() {
|
||||||
|
if (selectedTargetIndex.value < 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请选择目标职业',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedTarget = targetCareerOptions.value[selectedTargetIndex.value];
|
||||||
|
console.log('查询职业发展路径', currentPosition.value, selectedTarget);
|
||||||
|
// TODO: 调用接口查询职业路径
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getCurrentPosition();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.career-path {
|
||||||
|
padding: 10rpx 28rpx 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-section {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 28rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #157DF0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #000000;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
border: 1rpx solid #E0E0E0;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-field {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
border: 1rpx solid #E0E0E0;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-placeholder {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
background: linear-gradient(180deg, rgba(18, 125, 240, 1) 0%, rgba(59, 14, 123, 0.71) 100%);
|
||||||
|
color: rgba(255, 255, 255, 1);
|
||||||
|
font-size: 28rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-family: '阿里巴巴普惠体3.0-regular', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||||
|
border: 2rpx solid rgba(187, 187, 187, 1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.path-section {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 28rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child)::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: -32rpx;
|
||||||
|
top: 0;
|
||||||
|
width: 2rpx;
|
||||||
|
height: 100%;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
#E0E0E0 0,
|
||||||
|
#E0E0E0 6rpx,
|
||||||
|
transparent 6rpx,
|
||||||
|
transparent 12rpx
|
||||||
|
);
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-marker {
|
||||||
|
position: absolute;
|
||||||
|
left: -32rpx;
|
||||||
|
top: 0;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-marker {
|
||||||
|
background-color: #FF4444;
|
||||||
|
border: 4rpx solid #FFFFFF;
|
||||||
|
box-shadow: 0 0 0 2rpx #FF4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-marker {
|
||||||
|
background-color: #286BFA;
|
||||||
|
border: 4rpx solid #FFFFFF;
|
||||||
|
box-shadow: 0 0 0 2rpx #286BFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.end-marker {
|
||||||
|
background-color: #52C41A;
|
||||||
|
border: 4rpx solid #FFFFFF;
|
||||||
|
box-shadow: 0 0 0 2rpx #52C41A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-content {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
margin-left: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #000000;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-tag {
|
||||||
|
background-color: #F0F0F0;
|
||||||
|
color: #286BFA;
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
291
pages/service/components/CareerRecommend.vue
Normal file
291
pages/service/components/CareerRecommend.vue
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
<template>
|
||||||
|
<view class="career-recommend">
|
||||||
|
<!-- 当前职位信息卡片 -->
|
||||||
|
<view class="info-card">
|
||||||
|
<view class="card-title">当前职位信息</view>
|
||||||
|
<view class="card-content">
|
||||||
|
<text class="label">当前职位</text>
|
||||||
|
<text class="value">{{ currentJob || '前端开发工程师' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 我的技能标签卡片 -->
|
||||||
|
<view class="info-card">
|
||||||
|
<view class="card-title">我的技能标签</view>
|
||||||
|
<view class="skill-tags">
|
||||||
|
<view
|
||||||
|
class="skill-tag"
|
||||||
|
v-for="(skill, index) in skillTags"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ skill }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 相似推荐职位 -->
|
||||||
|
<view class="section-title">相似推荐职位</view>
|
||||||
|
<view
|
||||||
|
class="job-item-card"
|
||||||
|
v-for="(job, index) in recommendedJobs"
|
||||||
|
:key="index"
|
||||||
|
@click="handleJobCardClick(job)"
|
||||||
|
>
|
||||||
|
<button class="search-btn" @click.stop="handleJobSearch(job)">职位搜索</button>
|
||||||
|
<view class="job-header">
|
||||||
|
<text class="job-title">{{ job.title }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="job-skills">
|
||||||
|
<view
|
||||||
|
class="job-skill-tag"
|
||||||
|
v-for="(skill, skillIndex) in job.skills"
|
||||||
|
:key="skillIndex"
|
||||||
|
>
|
||||||
|
{{ skill }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const emit = defineEmits(['job-card-click']);
|
||||||
|
|
||||||
|
// 当前职位信息(后续从接口获取)
|
||||||
|
// TODO: 从接口获取当前职位信息
|
||||||
|
// async function getCurrentJob() {
|
||||||
|
// try {
|
||||||
|
// const response = await getCareerCurrentJob();
|
||||||
|
// if (response && response.code === 200) {
|
||||||
|
// currentJob.value = response.data?.jobTitle || '前端开发工程师';
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('获取当前职位失败:', error);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
const currentJob = ref('前端开发工程师'); // mock数据
|
||||||
|
|
||||||
|
// 技能标签列表(后续从接口获取)
|
||||||
|
const skillTags = ref(['JavaScript', 'React', 'Vue', 'CSS3', 'HTML5', 'Git', 'Webpack', 'TypeScript']);
|
||||||
|
|
||||||
|
// 相似推荐职位列表(后续从接口获取)
|
||||||
|
const recommendedJobs = ref([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: '全栈开发工程师',
|
||||||
|
skills: ['React', 'Node.js', 'TypeScript', '数据库'],
|
||||||
|
possessedSkills: [
|
||||||
|
{ name: 'React', level: 5 },
|
||||||
|
{ name: 'JavaScript', level: 5 },
|
||||||
|
{ name: 'HTML/CSS', level: 6 },
|
||||||
|
{ name: 'Git', level: 5 }
|
||||||
|
],
|
||||||
|
improvementSkills: [
|
||||||
|
{ name: 'Node.js', level: 3 },
|
||||||
|
{ name: '数据库设计', level: 3 },
|
||||||
|
{ name: 'RESTful API', level: 3 },
|
||||||
|
{ name: 'Docker', level: 3 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: '高级前端开发工程师',
|
||||||
|
skills: ['React', 'Vue', 'TypeScript', 'Webpack'],
|
||||||
|
possessedSkills: [
|
||||||
|
{ name: 'React', level: 5 },
|
||||||
|
{ name: 'Vue', level: 4 },
|
||||||
|
{ name: 'JavaScript', level: 5 }
|
||||||
|
],
|
||||||
|
improvementSkills: [
|
||||||
|
{ name: 'TypeScript', level: 3 },
|
||||||
|
{ name: 'Webpack', level: 3 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: '前端架构师',
|
||||||
|
skills: ['React', 'Vue', 'TypeScript', '架构设计'],
|
||||||
|
possessedSkills: [
|
||||||
|
{ name: 'React', level: 6 },
|
||||||
|
{ name: 'Vue', level: 5 }
|
||||||
|
],
|
||||||
|
improvementSkills: [
|
||||||
|
{ name: '架构设计', level: 3 },
|
||||||
|
{ name: '技术选型', level: 3 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: '前端架构师',
|
||||||
|
skills: ['架构设计', '技术选型', '工程化', '团队领导'],
|
||||||
|
possessedSkills: [
|
||||||
|
{ name: '工程化', level: 4 }
|
||||||
|
],
|
||||||
|
improvementSkills: [
|
||||||
|
{ name: '架构设计', level: 3 },
|
||||||
|
{ name: '技术选型', level: 3 },
|
||||||
|
{ name: '团队领导', level: 2 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// TODO: 从接口获取推荐职位数据
|
||||||
|
// async function getRecommendedJobs() {
|
||||||
|
// try {
|
||||||
|
// const response = await getCareerRecommendJobs();
|
||||||
|
// if (response && response.code === 200) {
|
||||||
|
// recommendedJobs.value = response.data || [];
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('获取推荐职位失败:', error);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
function handleJobSearch(job) {
|
||||||
|
console.log('搜索职位:', job.title);
|
||||||
|
// TODO: 实现职位搜索跳转
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleJobCardClick(job) {
|
||||||
|
emit('job-card-click', job);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.career-recommend {
|
||||||
|
padding: 10rpx 28rpx 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 28rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #000000;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 34rpx;
|
||||||
|
color: rgba(154, 154, 154, 1);
|
||||||
|
text-align: left;
|
||||||
|
font-family: 'PingFangSC-Bold', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 32rpx;
|
||||||
|
line-height: 46rpx;
|
||||||
|
color: rgb(16, 16, 16);
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
font-family: 'PingFangSC-Bold', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-tag {
|
||||||
|
background-color: #EAEFFE;
|
||||||
|
color: rgba(44, 101, 247, 1);
|
||||||
|
padding: 8rpx 20rpx;
|
||||||
|
border-radius: 0;
|
||||||
|
font-size: 24rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #000000;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-item-card {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 28rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
border-left: 6rpx solid #409EFF;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 16rpx;
|
||||||
|
right: 16rpx;
|
||||||
|
background-color: #FFDAB9;
|
||||||
|
color: #FF6347;
|
||||||
|
font-size: 20rpx;
|
||||||
|
padding: 6rpx 14rpx;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
border: 1rpx solid #FFA07A;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 10;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-header {
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
padding-right: 100rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgb(16, 16, 16);
|
||||||
|
text-align: left;
|
||||||
|
font-family: 'PingFangSC-Bold', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||||
|
line-height: 46rpx;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-skills {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-skill-tag {
|
||||||
|
width: 130rpx;
|
||||||
|
height: 36rpx;
|
||||||
|
line-height: 34rpx;
|
||||||
|
background-color: rgba(49, 100, 239, 0.1);
|
||||||
|
color: rgba(44, 101, 247, 1);
|
||||||
|
font-size: 24rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
203
pages/service/components/PageHeader.vue
Normal file
203
pages/service/components/PageHeader.vue
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page-header">
|
||||||
|
<view class="header-top">
|
||||||
|
<view class="title-section">
|
||||||
|
<text class="main-title">职业规划推荐指导平台</text>
|
||||||
|
<text class="sub-title">智能匹配·精准推荐·职业发展</text>
|
||||||
|
</view>
|
||||||
|
<view class="header-icons">
|
||||||
|
<view class="icon-dot" @click="handleMenuClick">
|
||||||
|
<view class="dot"></view>
|
||||||
|
<view class="dot"></view>
|
||||||
|
<view class="dot"></view>
|
||||||
|
</view>
|
||||||
|
<view class="icon-single-dot" @click="handleMoreClick"></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 搜索栏 -->
|
||||||
|
<view class="search-bar" @click="handleSearchClick">
|
||||||
|
<uni-icons class="search-icon" type="search" size="18" color="#999999"></uni-icons>
|
||||||
|
<text class="search-placeholder">职位名称、薪资要求等</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Tab导航 -->
|
||||||
|
<view class="tab-nav-wrapper">
|
||||||
|
<view class="tab-nav">
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'active': activeTab === 0 }"
|
||||||
|
@click="handleTabClick(0)"
|
||||||
|
>
|
||||||
|
职业推荐
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'active': activeTab === 1 }"
|
||||||
|
@click="handleTabClick(1)"
|
||||||
|
>
|
||||||
|
职业路径
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'active': activeTab === 2 }"
|
||||||
|
@click="handleTabClick(2)"
|
||||||
|
>
|
||||||
|
技能发展
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineProps, defineEmits } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
activeTab: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['tab-change', 'search-click', 'menu-click', 'more-click']);
|
||||||
|
|
||||||
|
function handleTabClick(index) {
|
||||||
|
emit('tab-change', index);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSearchClick() {
|
||||||
|
emit('search-click');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMenuClick() {
|
||||||
|
emit('menu-click');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMoreClick() {
|
||||||
|
emit('more-click');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page-header {
|
||||||
|
padding: 40rpx 28rpx 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-top {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-section {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #000000;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-title {
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 28rpx;
|
||||||
|
color: rgba(51, 51, 51, 1);
|
||||||
|
text-align: left;
|
||||||
|
font-family: 'PingFangSC-Bold', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-icons {
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-dot {
|
||||||
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4rpx;
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
width: 6rpx;
|
||||||
|
height: 6rpx;
|
||||||
|
background-color: #666666;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-single-dot {
|
||||||
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
background-color: #EBF1FF;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
padding: 0 28rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
height: 88rpx;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-placeholder {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999999;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-nav-wrapper {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 6rpx 20rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12rpx 24rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #000000;
|
||||||
|
background-color: transparent;
|
||||||
|
text-align: center;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active {
|
||||||
|
background: linear-gradient(135deg, #9974FD 0%, #286BFA 100%);
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
140
pages/service/components/RemindPopup.vue
Normal file
140
pages/service/components/RemindPopup.vue
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<template>
|
||||||
|
<uni-popup
|
||||||
|
ref="popupRef"
|
||||||
|
type="center"
|
||||||
|
borderRadius="10px 10px 10px 10px"
|
||||||
|
background-color="#FFFFFF"
|
||||||
|
:mask-click="false"
|
||||||
|
>
|
||||||
|
<view class="remind-popup-content">
|
||||||
|
<view class="remind-title">提醒</view>
|
||||||
|
<view class="remind-message">请先完善您的个人信息:</view>
|
||||||
|
<view class="remind-list">
|
||||||
|
<view
|
||||||
|
class="remind-item"
|
||||||
|
v-for="(item, index) in remindList"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ index + 1 }}、{{ item }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="remind-btns">
|
||||||
|
<button class="remind-btn cancel-btn" @click="handleCancel">取消</button>
|
||||||
|
<button class="remind-btn confirm-btn" @click="handleConfirm">确认</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, defineExpose } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
remindList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['cancel', 'confirm']);
|
||||||
|
|
||||||
|
const popupRef = ref(null);
|
||||||
|
|
||||||
|
// 打开弹窗
|
||||||
|
function open() {
|
||||||
|
if (popupRef.value) {
|
||||||
|
popupRef.value.open('center');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
function close() {
|
||||||
|
popupRef.value?.close('center');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消按钮
|
||||||
|
function handleCancel() {
|
||||||
|
emit('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认按钮
|
||||||
|
function handleConfirm() {
|
||||||
|
emit('confirm');
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
open,
|
||||||
|
close
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.remind-popup-content {
|
||||||
|
padding: 40rpx;
|
||||||
|
width: 630rpx;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remind-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #000000;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remind-message {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #000000;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remind-list {
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
min-height: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remind-item {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #000000;
|
||||||
|
line-height: 40rpx;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remind-btns {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remind-btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 88rpx;
|
||||||
|
line-height: 88rpx;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
color: #666666;
|
||||||
|
border: 1rpx solid #E5E7EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn {
|
||||||
|
background-color: #256BFA;
|
||||||
|
color: #FFFFFF;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
223
pages/service/components/SkillDetailPopup.vue
Normal file
223
pages/service/components/SkillDetailPopup.vue
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
<template>
|
||||||
|
<uni-popup
|
||||||
|
ref="popupRef"
|
||||||
|
type="bottom"
|
||||||
|
:borderRadius="'20rpx 20rpx 0 0'"
|
||||||
|
background-color="#FFFFFF"
|
||||||
|
maskBackgroundColor="rgba(255, 255, 255, 0.6)"
|
||||||
|
:isMaskClick="true"
|
||||||
|
@maskClick="handleClose"
|
||||||
|
>
|
||||||
|
<view class="skill-popup-content">
|
||||||
|
<!-- 头部:标题和关闭按钮 -->
|
||||||
|
<view class="popup-header">
|
||||||
|
<text class="popup-title">{{ jobTitle }}</text>
|
||||||
|
<view class="close-btn" @click="handleClose">
|
||||||
|
<uni-icons type="closeempty" size="20" color="#000000"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<scroll-view scroll-y class="popup-body" :show-scrollbar="false">
|
||||||
|
<view
|
||||||
|
class="skill-section"
|
||||||
|
v-for="(section, sIndex) in skillSections"
|
||||||
|
:key="sIndex"
|
||||||
|
>
|
||||||
|
<view class="section-header">
|
||||||
|
<view class="section-icon"></view>
|
||||||
|
<text class="section-title">{{ section.title }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="skill-list">
|
||||||
|
<view
|
||||||
|
class="skill-item"
|
||||||
|
v-for="(skill, index) in section.skills"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<text class="skill-name">{{ skill.name }}</text>
|
||||||
|
<view class="skill-dots">
|
||||||
|
<view
|
||||||
|
class="dot"
|
||||||
|
:class="{ 'active': i - 1 < skill.level }"
|
||||||
|
v-for="i in 6"
|
||||||
|
:key="i"
|
||||||
|
></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, defineExpose } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
jobTitle: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
possessedSkills: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
improvementSkills: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
|
const popupRef = ref(null);
|
||||||
|
|
||||||
|
// 技能区块数据
|
||||||
|
const skillSections = computed(() => [
|
||||||
|
{ title: '已具备技能', skills: props.possessedSkills },
|
||||||
|
{ title: '需要提升的技能', skills: props.improvementSkills }
|
||||||
|
]);
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
popupRef.value?.open('bottom');
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
popupRef.value?.close('bottom');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
close();
|
||||||
|
emit('close');
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open, close });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.skill-popup-content {
|
||||||
|
height: calc(100vh - 330rpx);
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 20rpx 20rpx 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 32rpx 28rpx;
|
||||||
|
border-bottom: 1rpx solid #F0F0F0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #000000;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
position: absolute;
|
||||||
|
right: 28rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-body {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 28rpx 28rpx;
|
||||||
|
overflow-y: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-section {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #9974FD 0%, #286BFA 100%);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #286BFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-item {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 64rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #000000;
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-dots {
|
||||||
|
display: flex;
|
||||||
|
gap: 8rpx;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 112rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
width: 12rpx;
|
||||||
|
height: 12rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #E0E0E0;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #286BFA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
231
pages/service/components/SkillDevelopment.vue
Normal file
231
pages/service/components/SkillDevelopment.vue
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
<template>
|
||||||
|
<view class="skill-development">
|
||||||
|
<view class="content-section">
|
||||||
|
<view class="section-title">
|
||||||
|
<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
|
||||||
|
class="skill-item"
|
||||||
|
v-for="(skill, index) in skillList"
|
||||||
|
:key="index"
|
||||||
|
@click="handleSkillItemClick(skill)"
|
||||||
|
>
|
||||||
|
<view class="skill-header">
|
||||||
|
<text class="skill-name">{{ skill.name }}</text>
|
||||||
|
<text class="skill-weight">权重: {{ skill.weight }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="skill-tags">
|
||||||
|
<view
|
||||||
|
class="skill-tag"
|
||||||
|
v-for="(tag, tagIndex) in skill.tags"
|
||||||
|
:key="tagIndex"
|
||||||
|
>
|
||||||
|
{{ tag }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 技能权重弹出层 -->
|
||||||
|
<SkillWeightPopup
|
||||||
|
ref="skillWeightPopup"
|
||||||
|
:skill-name="selectedSkillName"
|
||||||
|
:weight="selectedSkillWeight"
|
||||||
|
:current-level="selectedSkillLevel"
|
||||||
|
@close="handlePopupClose"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import SkillWeightPopup from './SkillWeightPopup.vue';
|
||||||
|
|
||||||
|
// 技能列表(后续从接口获取)
|
||||||
|
const skillList = ref([
|
||||||
|
{
|
||||||
|
name: '项目管理(PMP)',
|
||||||
|
weight: '0.98',
|
||||||
|
tags: ['核心技能', '管理能力'],
|
||||||
|
currentLevel: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '团队领导力',
|
||||||
|
weight: '0.90',
|
||||||
|
tags: ['核心技能', '软技能'],
|
||||||
|
currentLevel: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '云安全架构',
|
||||||
|
weight: '0.85',
|
||||||
|
tags: ['技术技能', '网络安全'],
|
||||||
|
currentLevel: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '财务预算规划',
|
||||||
|
weight: '0.60',
|
||||||
|
tags: ['软技能', '管理能力'],
|
||||||
|
currentLevel: 1
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 弹出层引用
|
||||||
|
const skillWeightPopup = ref(null);
|
||||||
|
|
||||||
|
// 选中的技能信息
|
||||||
|
const selectedSkillName = ref('');
|
||||||
|
const selectedSkillWeight = ref('');
|
||||||
|
const selectedSkillLevel = ref(0);
|
||||||
|
|
||||||
|
// 获取技能发展数据(从接口获取)
|
||||||
|
async function getSkillDevelopmentData() {
|
||||||
|
// TODO: 调用接口获取技能发展数据
|
||||||
|
// const response = await getSkillDevelopment();
|
||||||
|
// if (response && response.code === 200) {
|
||||||
|
// skillList.value = response.data || [];
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理技能项点击
|
||||||
|
function handleSkillItemClick(skill) {
|
||||||
|
selectedSkillName.value = skill.name || '';
|
||||||
|
selectedSkillWeight.value = skill.weight || '0';
|
||||||
|
selectedSkillLevel.value = skill.currentLevel || 0;
|
||||||
|
skillWeightPopup.value?.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理弹出层关闭
|
||||||
|
function handlePopupClose() {
|
||||||
|
// 可以在这里处理关闭后的逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getSkillDevelopmentData();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.skill-development {
|
||||||
|
padding: 10rpx 0 20rpx;
|
||||||
|
background-color: #EBF4FF;
|
||||||
|
min-height:95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-section {
|
||||||
|
background-color: #EBF4FF;
|
||||||
|
padding: 28rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
margin-top: -20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-text {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #167CF1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 34rpx;
|
||||||
|
color: rgba(154, 154, 154, 1);
|
||||||
|
text-align: left;
|
||||||
|
font-family: 'PingFangSC-Bold', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 90rpx;
|
||||||
|
width: 672rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-item {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 796rpx;
|
||||||
|
min-height: 162rpx;
|
||||||
|
line-height: 40rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
background-color: rgba(239, 239, 239, 1);
|
||||||
|
color: rgba(16, 16, 16, 1);
|
||||||
|
font-size: 28rpx;
|
||||||
|
text-align: center;
|
||||||
|
padding: 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
line-height: 46rpx;
|
||||||
|
color: rgb(16, 16, 16);
|
||||||
|
text-align: left;
|
||||||
|
font-family: 'PingFangSC-Bold', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-weight {
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 34rpx;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
background-color: rgba(49, 100, 239, 0.1);
|
||||||
|
color: rgba(44, 101, 247, 1);
|
||||||
|
text-align: center;
|
||||||
|
padding: 6rpx 12rpx;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skill-tag {
|
||||||
|
background-color: rgba(49, 100, 239, 0.1);
|
||||||
|
color: rgba(44, 101, 247, 1);
|
||||||
|
padding: 6rpx 12rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 34rpx;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
212
pages/service/components/SkillWeightPopup.vue
Normal file
212
pages/service/components/SkillWeightPopup.vue
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
<template>
|
||||||
|
<uni-popup
|
||||||
|
ref="popupRef"
|
||||||
|
type="bottom"
|
||||||
|
:borderRadius="'20rpx 20rpx 0 0'"
|
||||||
|
background-color="#FFFFFF"
|
||||||
|
maskBackgroundColor="rgba(255, 255, 255, 0.6)"
|
||||||
|
:isMaskClick="true"
|
||||||
|
@maskClick="handleClose"
|
||||||
|
>
|
||||||
|
<view class="skill-weight-popup-content">
|
||||||
|
<!-- 头部:标题和关闭按钮 -->
|
||||||
|
<view class="popup-header">
|
||||||
|
<text class="popup-title">{{ skillName }}</text>
|
||||||
|
<view class="close-btn" @click="handleClose">
|
||||||
|
<uni-icons type="closeempty" size="20" color="#000000"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<scroll-view scroll-y class="popup-body" :show-scrollbar="false">
|
||||||
|
<!-- 技能权重 -->
|
||||||
|
<view class="info-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<view class="section-icon"></view>
|
||||||
|
<text class="section-title">技能权重</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="info-label">{{ skillName }}</text>
|
||||||
|
<text class="info-value">{{ weight }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 当前水平 -->
|
||||||
|
<view class="info-section">
|
||||||
|
<view class="section-header">
|
||||||
|
<view class="section-icon"></view>
|
||||||
|
<text class="section-title">当前水平</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-item">
|
||||||
|
<text class="info-label">能力等级</text>
|
||||||
|
<view class="level-dots">
|
||||||
|
<view
|
||||||
|
class="dot"
|
||||||
|
:class="{ 'active': i - 1 < currentLevel }"
|
||||||
|
v-for="i in 5"
|
||||||
|
:key="i"
|
||||||
|
></view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, defineExpose } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
skillName: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
weight: {
|
||||||
|
type: String,
|
||||||
|
default: '0'
|
||||||
|
},
|
||||||
|
currentLevel: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['close']);
|
||||||
|
|
||||||
|
const popupRef = ref(null);
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
popupRef.value?.open('bottom');
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
popupRef.value?.close('bottom');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
close();
|
||||||
|
emit('close');
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open, close });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.skill-weight-popup-content {
|
||||||
|
height: calc(100vh - 330rpx);
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border-radius: 20rpx 20rpx 0 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 32rpx 28rpx;
|
||||||
|
border-bottom: 1rpx solid #F0F0F0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #000000;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
position: absolute;
|
||||||
|
right: 28rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-body {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 28rpx 28rpx;
|
||||||
|
overflow-y: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-section {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #9974FD 0%, #286BFA 100%);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #286BFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #000000;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #286BFA;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-dots {
|
||||||
|
display: flex;
|
||||||
|
gap: 8rpx;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
width: 12rpx;
|
||||||
|
height: 12rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #E0E0E0;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #286BFA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
BIN
static/icon/antOutline.png
Normal file
BIN
static/icon/antOutline.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 308 B |
Reference in New Issue
Block a user