生成定制简历部分默认优化选项全选
This commit is contained in:
+14
-2
@@ -435,6 +435,16 @@ export interface SkillGapResume {
|
||||
targetPosition: string
|
||||
}
|
||||
|
||||
/** 技能差距分析 — 差距项 */
|
||||
export interface SkillGapItem {
|
||||
/** 关键词 */
|
||||
keyword: string
|
||||
/** 差距标题 */
|
||||
title: string
|
||||
/** 差距描述 */
|
||||
description: string
|
||||
}
|
||||
|
||||
/** 技能差距分析返回数据 */
|
||||
export interface SkillGapData {
|
||||
/** 匹配度分数 */
|
||||
@@ -443,8 +453,10 @@ export interface SkillGapData {
|
||||
job: SkillGapJob
|
||||
/** 简历信息 */
|
||||
resume: SkillGapResume
|
||||
/** 缺少的技能列表 */
|
||||
missingSkills: string[]
|
||||
/** 缺少的技能列表(兼容旧版) */
|
||||
missingSkills?: string[]
|
||||
/** 差距分析列表(新版) */
|
||||
gaps?: SkillGapItem[]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -232,7 +232,7 @@
|
||||
background: $btn-dark;
|
||||
color: $bg-white;
|
||||
border: none;
|
||||
border-radius: 0.24rem;
|
||||
border-radius: 0.08rem;
|
||||
font-size: 0.15rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
@@ -274,7 +274,6 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.02rem;
|
||||
}
|
||||
|
||||
@@ -282,34 +281,44 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.08rem;
|
||||
font-size: 0.13rem;
|
||||
color: $text-light;
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
color: $text-middle;
|
||||
}
|
||||
|
||||
&__step--active {
|
||||
color: $text-dark;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&__step-num {
|
||||
width: 0.22rem;
|
||||
height: 0.22rem;
|
||||
width: 0.32rem;
|
||||
height: 0.32rem;
|
||||
border-radius: 50%;
|
||||
background: $bg-main;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.11rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.14rem;
|
||||
font-weight: 450;
|
||||
color: $text-middle;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&__step--active &__step-num {
|
||||
background: $text-dark;
|
||||
background: $accent;
|
||||
color: $bg-white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
// ==================== 差距分析头部 ====================
|
||||
// 步骤之间的连接横线
|
||||
&__step-line {
|
||||
width: 0.935rem;
|
||||
height: 0.02rem;
|
||||
background: #E5E7EB;
|
||||
margin: 0 0.16rem;
|
||||
}
|
||||
|
||||
// ==================== 差距分析头部(新版) ====================
|
||||
&__gap-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@@ -323,28 +332,24 @@
|
||||
}
|
||||
|
||||
&__gap-title {
|
||||
font-size: 0.2rem;
|
||||
font-weight: 700;
|
||||
font-size: 0.24rem;
|
||||
font-weight: 630;
|
||||
color: $text-dark;
|
||||
margin: 0 0 0.1rem 0;
|
||||
line-height: 1.3;
|
||||
margin: 0 0 0.08rem 0;
|
||||
line-height: 0.32rem;
|
||||
}
|
||||
|
||||
&__gap-warn {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.06rem;
|
||||
font-size: 0.12rem;
|
||||
color: $text-middle;
|
||||
line-height: 1.5;
|
||||
// 匹配度高亮文字(品牌色)
|
||||
&__gap-title-highlight {
|
||||
color: #FE6D52;
|
||||
}
|
||||
|
||||
&__warn-icon {
|
||||
width: 0.14rem;
|
||||
height: 0.14rem;
|
||||
flex-shrink: 0;
|
||||
margin-top: 0.02rem;
|
||||
&__gap-subtitle {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 330;
|
||||
color: $text-middle;
|
||||
margin: 0;
|
||||
line-height: 0.19rem;
|
||||
}
|
||||
|
||||
&__gap-score-area {
|
||||
@@ -355,169 +360,183 @@
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
|
||||
// ==================== 对比卡片表格 ====================
|
||||
&__gap-table {
|
||||
margin-bottom: 0.36rem;
|
||||
// 匹配度分数圆环
|
||||
&__score-circle {
|
||||
position: relative;
|
||||
width: 0.72rem;
|
||||
height: 0.72rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__score-svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__score-num {
|
||||
font-size: 0.24rem;
|
||||
font-weight: 630;
|
||||
color: $accent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// 匹配度标签(底部黑底白字)
|
||||
&__score-tag {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 330;
|
||||
color: $bg-white;
|
||||
background: $text-dark;
|
||||
border-radius: 0.04rem;
|
||||
padding: 0.02rem 0.08rem;
|
||||
margin-top: -0.1rem;
|
||||
line-height: 0.19rem;
|
||||
}
|
||||
|
||||
// ==================== 通用区域标题 ====================
|
||||
&__section {
|
||||
margin-bottom: 0.28rem;
|
||||
}
|
||||
|
||||
&__section-title {
|
||||
font-size: 0.18rem;
|
||||
font-weight: 600;
|
||||
color: $text-dark;
|
||||
margin: 0 0 0.16rem 0;
|
||||
line-height: 0.24rem;
|
||||
}
|
||||
|
||||
// ==================== 简历选择下拉框 ====================
|
||||
&__resume-select-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.12rem 0.16rem;
|
||||
background: $bg-main;
|
||||
border-radius: 0.12rem;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
width: 1.6rem;
|
||||
}
|
||||
|
||||
&__resume-select-name {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
color: $text-middle;
|
||||
line-height: 0.19rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__resume-select-arrow {
|
||||
width: 0.14rem;
|
||||
height: 0.14rem;
|
||||
color: $text-middle;
|
||||
flex-shrink: 0;
|
||||
margin-left: 0.16rem;
|
||||
}
|
||||
|
||||
// ==================== 岗位概览卡片 ====================
|
||||
&__overview-card {
|
||||
border: 1px solid #F0F0F0;
|
||||
border-radius: 0.12rem;
|
||||
padding: 0.16rem 0.2rem;
|
||||
}
|
||||
|
||||
&__overview-title {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
color: $text-dark;
|
||||
line-height: 0.19rem;
|
||||
margin-bottom: 0.04rem;
|
||||
}
|
||||
|
||||
&__overview-sub {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 400;
|
||||
color: $text-middle;
|
||||
line-height: 0.19rem;
|
||||
margin-bottom: 0.08rem;
|
||||
}
|
||||
|
||||
&__overview-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.04rem;
|
||||
}
|
||||
|
||||
&__overview-tag {
|
||||
font-size: 0.12rem;
|
||||
font-weight: 400;
|
||||
color: $text-middle;
|
||||
background: $bg-main;
|
||||
border-radius: 0.04rem;
|
||||
padding: 0.04rem 0.06rem;
|
||||
line-height: 0.18rem;
|
||||
}
|
||||
|
||||
// ==================== 差距分析列表 ====================
|
||||
&__gap-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.06rem;
|
||||
gap: 0.13rem;
|
||||
}
|
||||
|
||||
&__gap-row {
|
||||
display: flex;
|
||||
gap: 0.06rem;
|
||||
}
|
||||
|
||||
&__gap-cell {
|
||||
flex: 1;
|
||||
padding: 0.18rem 0.16rem;
|
||||
font-size: 0.13rem;
|
||||
color: $text-dark;
|
||||
background: $bg-main;
|
||||
border-radius: 0.08rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 0.6rem;
|
||||
}
|
||||
|
||||
&__gap-cell--label {
|
||||
flex: 0 0 1.2rem;
|
||||
font-weight: 600;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&__gap-cell-title {
|
||||
font-size: 0.18rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
// 关键词列横跨两列
|
||||
&__gap-cell--keywords {
|
||||
flex: 2.06;
|
||||
}
|
||||
|
||||
// 概览行岗位信息
|
||||
&__gap-job-info {
|
||||
&__gap-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.16rem 0.2rem;
|
||||
border: 1px solid #F0F0F0;
|
||||
border-radius: 0.12rem;
|
||||
gap: 0.12rem;
|
||||
}
|
||||
|
||||
&__gap-company-icon {
|
||||
width: 0.36rem;
|
||||
height: 0.36rem;
|
||||
border-radius: 0.06rem;
|
||||
background: $bg-white;
|
||||
// 序号圆圈
|
||||
&__gap-item-num {
|
||||
width: 0.32rem;
|
||||
height: 0.32rem;
|
||||
border-radius: 50%;
|
||||
background: $accent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__gap-company-logo {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 0.06rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&__gap-company-svg {
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
color: $text-dark;
|
||||
}
|
||||
|
||||
&__gap-job-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.04rem;
|
||||
}
|
||||
|
||||
&__gap-job-title {
|
||||
span {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
color: $text-dark;
|
||||
line-height: 1.2;
|
||||
color: $bg-white;
|
||||
line-height: 0.19rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__gap-job-sub {
|
||||
font-size: 0.11rem;
|
||||
color: $text-light;
|
||||
line-height: 1.2;
|
||||
&__gap-item-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
// 岗位名称/简历目标值
|
||||
&__gap-value {
|
||||
&__gap-item-title {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 500;
|
||||
font-weight: 520;
|
||||
color: $text-dark;
|
||||
line-height: 0.19rem;
|
||||
margin-bottom: 0.04rem;
|
||||
}
|
||||
|
||||
// ==================== 简历选择器 ====================
|
||||
&__resume-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
&__gap-item-desc {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 330;
|
||||
color: $text-middle;
|
||||
line-height: 0.19rem;
|
||||
}
|
||||
|
||||
&__resume-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.1rem;
|
||||
}
|
||||
|
||||
&__resume-file-icon {
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
color: $text-dark;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__resume-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.02rem;
|
||||
}
|
||||
|
||||
&__resume-label {
|
||||
font-size: 0.1rem;
|
||||
color: $text-light;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
&__resume-name {
|
||||
font-size: 0.12rem;
|
||||
color: $text-dark;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
&__resume-select-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.04rem;
|
||||
background: $bg-white;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 0.14rem;
|
||||
padding: 0.05rem 0.12rem;
|
||||
font-size: 0.11rem;
|
||||
color: $text-dark;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
border-color: $accent;
|
||||
color: $accent;
|
||||
}
|
||||
}
|
||||
|
||||
&__dropdown-arrow {
|
||||
width: 0.1rem;
|
||||
height: 0.1rem;
|
||||
}
|
||||
// ==================== 简历下拉选项(通用) ====================
|
||||
|
||||
&__resume-dropdown {
|
||||
position: absolute;
|
||||
@@ -551,67 +570,80 @@
|
||||
background: $theme-color;
|
||||
}
|
||||
|
||||
// ==================== 岗位关键词标签 ====================
|
||||
&__gap-keywords {
|
||||
// ==================== 步骤三:定制简历(v2 新版上下布局) ====================
|
||||
|
||||
// 定制简历整体容器(纵向布局)
|
||||
&__custom-v2 {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
gap: 0.32rem;
|
||||
}
|
||||
|
||||
// 每个区块
|
||||
&__custom-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.16rem;
|
||||
}
|
||||
|
||||
// 区块头部(标题+描述)
|
||||
&__custom-section-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.08rem;
|
||||
}
|
||||
|
||||
&__gap-kw-tag {
|
||||
font-size: 0.12rem;
|
||||
background: $text-middle;
|
||||
color: $bg-white;
|
||||
border-radius: 0.04rem;
|
||||
padding: 0.04rem 0.12rem;
|
||||
line-height: 1.2;
|
||||
// 区块标题
|
||||
&__custom-section-title {
|
||||
font-size: 0.18rem;
|
||||
font-weight: 600;
|
||||
color: #101828;
|
||||
margin: 0;
|
||||
line-height: 0.24rem;
|
||||
}
|
||||
|
||||
// ==================== 步骤三:定制简历 ====================
|
||||
|
||||
// 定制简历左右两栏布局
|
||||
&__custom {
|
||||
display: flex;
|
||||
gap: 0.16rem;
|
||||
height: 100%;
|
||||
// 区块描述
|
||||
&__custom-section-desc {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 400;
|
||||
color: #99A1AF;
|
||||
margin: 0;
|
||||
line-height: 0.19rem;
|
||||
}
|
||||
|
||||
// 左右面板
|
||||
&__custom-panel {
|
||||
flex: 1;
|
||||
background: $bg-main;
|
||||
border-radius: 0.12rem;
|
||||
padding: 0.28rem 0.24rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 面板标题
|
||||
&__custom-panel-title {
|
||||
font-size: 0.17rem;
|
||||
font-weight: 700;
|
||||
color: $text-dark;
|
||||
margin: 0 0 0.24rem 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
// 选项列表
|
||||
&__custom-options {
|
||||
// 卡片选项列表
|
||||
&__custom-cards {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
gap: 0.13rem;
|
||||
}
|
||||
|
||||
// 单个复选框行
|
||||
&__custom-checkbox {
|
||||
// 单个选项卡片
|
||||
&__custom-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.16rem 0.2rem;
|
||||
gap: 0.1rem;
|
||||
border: 1px solid #F0F0F0;
|
||||
border-radius: 0.12rem;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all 0.2s;
|
||||
background: $bg-white;
|
||||
|
||||
&:hover {
|
||||
border-color: transparent;
|
||||
background: #F3F4F6;
|
||||
}
|
||||
}
|
||||
|
||||
// 选中态:无背景填充,仅品牌色边框
|
||||
&__custom-card--checked {
|
||||
border-color: #52CAD1;
|
||||
background: $bg-white;
|
||||
}
|
||||
|
||||
// 隐藏原生 checkbox
|
||||
&__custom-input {
|
||||
&__custom-card-input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
@@ -619,63 +651,91 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// 自定义复选框样式
|
||||
&__custom-checkmark {
|
||||
width: 0.18rem;
|
||||
height: 0.18rem;
|
||||
border: 1.5px solid $border-color;
|
||||
border-radius: 0.04rem;
|
||||
// 圆形复选框容器
|
||||
&__custom-card-check {
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
background: $bg-white;
|
||||
}
|
||||
|
||||
// 选中态
|
||||
&__custom-input:checked + &__custom-checkmark {
|
||||
background: $accent;
|
||||
border-color: $accent;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0.05rem;
|
||||
height: 0.09rem;
|
||||
border: solid $bg-white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
margin-top: -0.04rem;
|
||||
}
|
||||
// 圆形复选框SVG
|
||||
&__custom-card-check-svg {
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
}
|
||||
|
||||
// 选项文字
|
||||
&__custom-label {
|
||||
font-size: 0.14rem;
|
||||
color: $text-dark;
|
||||
line-height: 1.2;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// 问号提示触发器
|
||||
&__custom-tooltip-trigger {
|
||||
// 文字内容区域
|
||||
&__custom-card-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 0.04rem;
|
||||
//cursor: help;
|
||||
cursor: pointer;
|
||||
gap: 0.12rem;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
// 问号图标
|
||||
&__custom-tooltip-icon {
|
||||
width: 0.15rem;
|
||||
height: 0.15rem;
|
||||
color: $text-light;
|
||||
transition: color 0.2s;
|
||||
// 选项主文字(加粗)
|
||||
&__custom-card-label {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
color: #1A1A1A;
|
||||
line-height: 0.19rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// 选项说明文字(灰色小字)
|
||||
&__custom-card-tip {
|
||||
font-size: 0.12rem;
|
||||
font-weight: 400;
|
||||
color: #6A7282;
|
||||
line-height: 0.16rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
// ==================== 底部双按钮布局(上一步 + 下一步) ====================
|
||||
&__footer-btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.24rem;
|
||||
}
|
||||
|
||||
// 上一步按钮(品牌色边框 + 品牌色文字)
|
||||
&__prev-btn {
|
||||
flex: 1;
|
||||
height: 0.45rem;
|
||||
background: $bg-white;
|
||||
border: 1px solid #52CAD1;
|
||||
border-radius: 0.12rem;
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
color: #52CAD1;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: $text-middle;
|
||||
background: #F6FCFC;
|
||||
}
|
||||
}
|
||||
|
||||
// 下一步按钮(品牌色填充 + 白色文字)
|
||||
&__next-btn {
|
||||
flex: 1;
|
||||
height: 0.45rem;
|
||||
background: #52CAD1;
|
||||
border: none;
|
||||
border-radius: 0.12rem;
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
color: $bg-white;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: #47b8bf;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -704,7 +764,6 @@
|
||||
align-items: center;
|
||||
gap: 0.12rem;
|
||||
padding: 0.2rem 0.1rem;
|
||||
border-bottom: 1px solid $border-color;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -731,23 +790,75 @@
|
||||
}
|
||||
|
||||
// ==================== 步骤四:返回按钮 ====================
|
||||
&__back-btn {
|
||||
// 顶部下载简历按钮区域
|
||||
&__top-download {
|
||||
position: absolute;
|
||||
top: 0.7rem;
|
||||
left: 0.2rem;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
&__top-download-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.04rem;
|
||||
width: 1.04rem;
|
||||
height: 0.36rem;
|
||||
background: $bg-white;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.06rem 0.2rem;
|
||||
border: 1px solid #52CAD1;
|
||||
border-radius: 0.08rem;
|
||||
font-size: 0.13rem;
|
||||
color: $text-dark;
|
||||
font-weight: 500;
|
||||
color: #52CAD1;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: #F6FCFC;
|
||||
}
|
||||
}
|
||||
|
||||
&__top-download-icon {
|
||||
width: 0.14rem;
|
||||
height: 0.14rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// 顶部下载下拉菜单(向下弹出)
|
||||
&__top-download-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
margin-top: 0.06rem;
|
||||
width: 1.04rem;
|
||||
background: $bg-white;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 0.08rem;
|
||||
box-shadow: 0 0.04rem 0.12rem rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&__top-download-option {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.1rem 0;
|
||||
font-size: 0.12rem;
|
||||
color: $text-dark;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: background 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: $bg-main;
|
||||
}
|
||||
|
||||
& + & {
|
||||
border-top: 1px solid $border-color;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 步骤四:预览布局 ====================
|
||||
@@ -1217,6 +1328,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤三底部:上一步按钮(品牌色边框样式)
|
||||
&__footer-prev-btn {
|
||||
flex: 1;
|
||||
height: 0.48rem;
|
||||
background: $bg-white;
|
||||
border: 1px solid #52CAD1;
|
||||
border-radius: 0.08rem;
|
||||
font-size: 0.15rem;
|
||||
font-weight: 500;
|
||||
color: #52CAD1;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: #F6FCFC;
|
||||
}
|
||||
}
|
||||
|
||||
// 立即去投递按钮
|
||||
&__submit-btn {
|
||||
flex: 1;
|
||||
@@ -1224,7 +1353,7 @@
|
||||
background: $btn-dark;
|
||||
color: $bg-white;
|
||||
border: none;
|
||||
border-radius: 0.24rem;
|
||||
border-radius: 0.08rem;
|
||||
font-size: 0.15rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
}
|
||||
|
||||
&__tab-item {
|
||||
font-size: 0.14rem;
|
||||
font-size: 0.18rem;
|
||||
color: $text-middle;
|
||||
cursor: pointer;
|
||||
padding-bottom: 0.06rem;
|
||||
@@ -86,7 +86,7 @@
|
||||
}
|
||||
|
||||
&__card-title {
|
||||
font-size: 0.16rem;
|
||||
font-size: 0.20rem;
|
||||
font-weight: 700;
|
||||
color: $text-dark;
|
||||
margin: 0;
|
||||
|
||||
@@ -638,8 +638,12 @@
|
||||
background: #FBFDF7;
|
||||
}
|
||||
|
||||
&--medium {
|
||||
background: #FFFBF5;
|
||||
}
|
||||
|
||||
&--low {
|
||||
background: #FBFDF7;
|
||||
background: #FFF6F5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,13 +690,22 @@
|
||||
color: #9FD051;
|
||||
}
|
||||
|
||||
// 中匹配度颜色
|
||||
&__job-match--medium &__match-score {
|
||||
color: #F59E0B;
|
||||
}
|
||||
|
||||
&__job-match--medium &__match-level {
|
||||
color: #F59E0B;
|
||||
}
|
||||
|
||||
// 低匹配度颜色
|
||||
&__job-match--low &__match-score {
|
||||
color: #9FD051;
|
||||
color: #FE6D52;
|
||||
}
|
||||
|
||||
&__job-match--low &__match-level {
|
||||
color: #9FD051;
|
||||
color: #FE6D52;
|
||||
}
|
||||
|
||||
// 未登录锁定态
|
||||
|
||||
@@ -22,18 +22,16 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { fetchFavoriteCount, fetchApplyCount } from '@/api/jobs'
|
||||
|
||||
// ==================== Props & Emits ====================
|
||||
|
||||
/** activeTab: 当前激活的 Tab key,由父组件通过 v-model 传入 */
|
||||
const props = defineProps<{
|
||||
activeTab: string
|
||||
/** 收藏总数,由父组件传入 */
|
||||
favoriteCount?: number
|
||||
/** 投递总数,由父组件传入 */
|
||||
applyCount?: number
|
||||
}>()
|
||||
|
||||
/** 双向绑定事件,在 Jobs 页面内切换 Tab 时触发 */
|
||||
@@ -45,14 +43,58 @@ const emit = defineEmits<{
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const store = useStore()
|
||||
|
||||
/** 登录状态 */
|
||||
const isAuthenticated = computed(() => store.state.isAuthenticated)
|
||||
|
||||
// ==================== 收藏 & 投递统计 ====================
|
||||
|
||||
/** 收藏总数 */
|
||||
const favoriteTotal = ref(0)
|
||||
|
||||
/** 投递总数 */
|
||||
const applyTotal = ref(0)
|
||||
|
||||
/** 加载收藏统计 */
|
||||
async function loadFavoriteCount() {
|
||||
try {
|
||||
const res = await fetchFavoriteCount()
|
||||
if (res.code === '0' && res.data) {
|
||||
favoriteTotal.value = res.data.totalCount
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载收藏统计失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载投递统计 */
|
||||
async function loadApplyCount() {
|
||||
try {
|
||||
const res = await fetchApplyCount()
|
||||
if (res.code === '0' && res.data) {
|
||||
applyTotal.value = res.data.totalCount
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载投递统计失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
/** 组件挂载时请求收藏和投递统计 */
|
||||
onMounted(() => {
|
||||
if (isAuthenticated.value) {
|
||||
loadFavoriteCount()
|
||||
loadApplyCount()
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== 常量数据 ====================
|
||||
|
||||
/** Tab 选项列表,count 用于显示圆形徽章计数 */
|
||||
const tabs = computed(() => [
|
||||
{ key: 'recommend', label: '推荐', count: undefined },
|
||||
{ key: 'collected', label: '收藏', count: props.favoriteCount ?? 0 },
|
||||
{ key: 'applied', label: '已投递', count: props.applyCount ?? 0 },
|
||||
{ key: 'collected', label: '收藏', count: favoriteTotal.value },
|
||||
{ key: 'applied', label: '已投递', count: applyTotal.value },
|
||||
])
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
|
||||
@@ -12,148 +12,168 @@
|
||||
</button>
|
||||
<h2 class="job-resume-custom-dialog__drawer-title">生成你的岗位专属简历</h2>
|
||||
</div>
|
||||
<!-- 返回按钮(步骤四显示) -->
|
||||
<button v-if="currentStep === 4" class="job-resume-custom-dialog__back-btn mt10" @click="goToStep(3)">返回</button>
|
||||
<!-- 下载简历按钮(步骤三显示,位于步骤指示器上方) -->
|
||||
<div v-if="currentStep === 4" class="job-resume-custom-dialog__top-download mt10">
|
||||
<button class="job-resume-custom-dialog__top-download-btn" @click="toggleDownloadMenu">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-resume-custom-dialog__top-download-icon">
|
||||
<path d="M8 2v8M8 10l-3-3M8 10l3-3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 12h10" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
下载简历
|
||||
</button>
|
||||
<!-- 下载下拉菜单 -->
|
||||
<div v-if="showDownloadMenu" class="job-resume-custom-dialog__top-download-menu">
|
||||
<button class="job-resume-custom-dialog__top-download-option" @click="handleDownload('pdf')">下载PDF</button>
|
||||
<button class="job-resume-custom-dialog__top-download-option" @click="handleDownload('word')">下载Word</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 步骤指示器 -->
|
||||
<div class="job-resume-custom-dialog__steps pt10">
|
||||
<div class="job-resume-custom-dialog__step" :class="{ 'job-resume-custom-dialog__step--active': currentStep === 2 }">
|
||||
<span class="job-resume-custom-dialog__step-num">1</span><span>差距分析</span>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__step-line"></div>
|
||||
<div class="job-resume-custom-dialog__step" :class="{ 'job-resume-custom-dialog__step--active': currentStep === 3 }">
|
||||
<span class="job-resume-custom-dialog__step-num">2</span><span>定制简历</span>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__step-line"></div>
|
||||
<div class="job-resume-custom-dialog__step" :class="{ 'job-resume-custom-dialog__step--active': currentStep === 4 }">
|
||||
<span class="job-resume-custom-dialog__step-num">3</span><span>简历预览</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 抽屉内容区(可滚动) -->
|
||||
<div class="job-resume-custom-dialog__drawer-body">
|
||||
<!-- 步骤二:差距分析 -->
|
||||
<!-- 步骤一:差距分析 -->
|
||||
<template v-if="currentStep === 2">
|
||||
<!-- 匹配度标题区域 -->
|
||||
<div class="job-resume-custom-dialog__gap-header">
|
||||
<div class="job-resume-custom-dialog__gap-left">
|
||||
<h3 class="job-resume-custom-dialog__gap-title">你的简历与该岗位的匹配度{{ isLowMatch ? '较低' : '较高' }}</h3>
|
||||
<div v-if="isLowMatch" class="job-resume-custom-dialog__gap-warn">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-resume-custom-dialog__warn-icon"><circle cx="8" cy="8" r="7" stroke="currentColor" stroke-width="1.2"/><path d="M8 5v3M8 10.5v.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||||
<span>匹配度低于 6.0 分的简历,在筛选环节可能会被优先淘汰。我们会帮你快速优化提升。</span>
|
||||
</div>
|
||||
<h3 class="job-resume-custom-dialog__gap-title">你的简历与该岗位的<span class="job-resume-custom-dialog__gap-title-highlight">匹配度{{ isLowMatch ? '较低' : '较高' }}</span></h3>
|
||||
<p class="job-resume-custom-dialog__gap-subtitle">匹配度低于 6.0 分的简历,在筛选环节可能会被优先淘汰。我们会帮你快速优化提升。</p>
|
||||
</div>
|
||||
<!-- 右侧匹配度分数 -->
|
||||
<div class="job-resume-custom-dialog__gap-score-area">
|
||||
<div class="job-resume-custom-dialog__match-ring">
|
||||
<svg viewBox="0 0 60 60" class="job-resume-custom-dialog__ring-svg"><circle cx="30" cy="30" r="24" stroke-width="4" stroke="#E8E8E8" fill="none" opacity="0.3"/><circle cx="30" cy="30" r="24" stroke-width="4" fill="none" stroke="#4FC2C9" stroke-linecap="round" :stroke-dasharray="2*Math.PI*24" :stroke-dashoffset="2*Math.PI*24*(1-jobInfo.matchScore/10)" transform="rotate(-90 30 30)"/></svg>
|
||||
<span class="job-resume-custom-dialog__match-score">{{ jobInfo.matchScore }}</span>
|
||||
<div class="job-resume-custom-dialog__score-circle">
|
||||
<svg viewBox="0 0 72 72" class="job-resume-custom-dialog__score-svg">
|
||||
<circle cx="36" cy="36" r="32" stroke-width="5" stroke="rgba(82,202,209,0.2)" fill="none"/>
|
||||
<circle cx="36" cy="36" r="32" stroke-width="5" fill="none" stroke="#52CAD1" stroke-linecap="round" :stroke-dasharray="2*Math.PI*32" :stroke-dashoffset="2*Math.PI*32*(1-jobInfo.matchScore/10)" transform="rotate(-90 36 36)"/>
|
||||
</svg>
|
||||
<span class="job-resume-custom-dialog__score-num">{{ jobInfo.matchScore }}</span>
|
||||
</div>
|
||||
<span class="job-resume-custom-dialog__match-label">{{ matchLevelText }}</span>
|
||||
<span class="job-resume-custom-dialog__score-tag">{{ matchLevelText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 对比卡片表格 -->
|
||||
<div class="job-resume-custom-dialog__gap-table">
|
||||
<!-- 第一行:概览 -->
|
||||
<div class="job-resume-custom-dialog__gap-row">
|
||||
<!-- 标签列 -->
|
||||
<div class="job-resume-custom-dialog__gap-cell job-resume-custom-dialog__gap-cell--label">
|
||||
<span class="job-resume-custom-dialog__gap-cell-title">概览</span>
|
||||
</div>
|
||||
<!-- 岗位信息列 -->
|
||||
<div class="job-resume-custom-dialog__gap-cell">
|
||||
<div class="job-resume-custom-dialog__gap-job-info">
|
||||
<div class="job-resume-custom-dialog__gap-company-icon">
|
||||
<img v-if="jobInfo.companyLogoUrl" :src="jobInfo.companyLogoUrl" :alt="jobInfo.company" class="job-resume-custom-dialog__gap-company-logo" />
|
||||
<svg v-else viewBox="0 0 24 24" fill="none" class="job-resume-custom-dialog__gap-company-svg"><rect x="3" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="14" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="3" y="14" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="14" y="14" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.2"/></svg>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__gap-job-text">
|
||||
<span class="job-resume-custom-dialog__gap-job-title">{{ jobInfo.title }}</span>
|
||||
<span class="job-resume-custom-dialog__gap-job-sub">{{ jobInfo.location }} · {{ jobInfo.company }} 独角兽</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 简历选择列 -->
|
||||
<div class="job-resume-custom-dialog__gap-cell">
|
||||
<div class="job-resume-custom-dialog__resume-selector">
|
||||
<div class="job-resume-custom-dialog__resume-info">
|
||||
<svg viewBox="0 0 24 24" fill="none" class="job-resume-custom-dialog__resume-file-icon"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M14 2v6h6" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
|
||||
<div class="job-resume-custom-dialog__resume-text">
|
||||
<span class="job-resume-custom-dialog__resume-label">你的简历</span>
|
||||
<span class="job-resume-custom-dialog__resume-name">{{ selectedResume.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="job-resume-custom-dialog__resume-select-btn" @click="toggleResumeDropdown">选择 <svg viewBox="0 0 12 12" fill="none" class="job-resume-custom-dialog__dropdown-arrow"><path d="M3 5l3 3 3-3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
|
||||
|
||||
<!-- 简历选择 -->
|
||||
<div class="job-resume-custom-dialog__section">
|
||||
<h4 class="job-resume-custom-dialog__section-title">简历选择</h4>
|
||||
<div class="job-resume-custom-dialog__resume-select-box" @click="toggleResumeDropdown">
|
||||
<span class="job-resume-custom-dialog__resume-select-name">{{ selectedResume.name }}</span>
|
||||
<svg viewBox="0 0 14 14" fill="none" class="job-resume-custom-dialog__resume-select-arrow"><path d="M4 6l3 3 3-3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
<!-- 简历下拉 -->
|
||||
<div v-if="showResumeDropdown" class="job-resume-custom-dialog__resume-dropdown">
|
||||
<div v-for="r in resumeList" :key="r.id" class="job-resume-custom-dialog__resume-option" :class="{'job-resume-custom-dialog__resume-option--active': r.id === selectedResume.id}" @click="selectResume(r)">{{ r.name }}</div>
|
||||
<div v-for="r in resumeList" :key="r.id" class="job-resume-custom-dialog__resume-option" :class="{'job-resume-custom-dialog__resume-option--active': r.id === selectedResume.id}" @click.stop="selectResume(r)">{{ r.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 岗位概览 -->
|
||||
<div class="job-resume-custom-dialog__section">
|
||||
<h4 class="job-resume-custom-dialog__section-title">岗位概览</h4>
|
||||
<div class="job-resume-custom-dialog__overview-card">
|
||||
<div class="job-resume-custom-dialog__overview-title">{{ jobInfo.company }} · {{ jobInfo.title }}</div>
|
||||
<div class="job-resume-custom-dialog__overview-sub">{{ jobInfo.location }} · {{ jobInfo.company }}</div>
|
||||
<div class="job-resume-custom-dialog__overview-tags">
|
||||
<span v-for="kw in jobInfo.keywords" :key="kw" class="job-resume-custom-dialog__overview-tag">{{ kw }}</span>
|
||||
</div>
|
||||
<!-- 第二行:岗位名称 -->
|
||||
<div class="job-resume-custom-dialog__gap-row">
|
||||
<div class="job-resume-custom-dialog__gap-cell job-resume-custom-dialog__gap-cell--label">
|
||||
<span>岗位名称</span>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__gap-cell">
|
||||
<span class="job-resume-custom-dialog__gap-value">{{ jobInfo.title }}</span>
|
||||
</div>
|
||||
<!-- <div class="job-resume-custom-dialog__gap-cell">
|
||||
<span class="job-resume-custom-dialog__gap-value">{{ selectedResume.targetJob || '—' }}</span>
|
||||
</div> -->
|
||||
|
||||
<!-- 差距分析列表 -->
|
||||
<div class="job-resume-custom-dialog__section">
|
||||
<h4 class="job-resume-custom-dialog__section-title">差距分析</h4>
|
||||
<div class="job-resume-custom-dialog__gap-list">
|
||||
<div v-for="(gap, idx) in gapItems" :key="idx" class="job-resume-custom-dialog__gap-item">
|
||||
<div class="job-resume-custom-dialog__gap-item-num">
|
||||
<span>{{ idx + 1 }}</span>
|
||||
</div>
|
||||
<!-- 第三行:岗位关键词 -->
|
||||
<div class="job-resume-custom-dialog__gap-row">
|
||||
<div class="job-resume-custom-dialog__gap-cell job-resume-custom-dialog__gap-cell--label">
|
||||
<span>岗位关键词</span>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__gap-cell job-resume-custom-dialog__gap-cell--keywords">
|
||||
<div class="job-resume-custom-dialog__gap-keywords">
|
||||
<span v-for="kw in jobInfo.keywords" :key="kw" class="job-resume-custom-dialog__gap-kw-tag">{{ kw }}</span>
|
||||
<div class="job-resume-custom-dialog__gap-item-content">
|
||||
<div class="job-resume-custom-dialog__gap-item-title">{{ gap.title }}</div>
|
||||
<div class="job-resume-custom-dialog__gap-item-desc">{{ gap.description }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 步骤三:定制简历 -->
|
||||
<!-- 步骤二:定制简历 -->
|
||||
<template v-if="currentStep === 3">
|
||||
<div class="job-resume-custom-dialog__custom">
|
||||
<!-- 左侧:选择要优化的部分 -->
|
||||
<div class="job-resume-custom-dialog__custom-panel">
|
||||
<h3 class="job-resume-custom-dialog__custom-panel-title">1.选择你要优化的部分</h3>
|
||||
<div class="job-resume-custom-dialog__custom-options">
|
||||
<label v-for="item in optimizeSections" :key="item.key" class="job-resume-custom-dialog__custom-checkbox">
|
||||
<input type="checkbox" v-model="item.checked" class="job-resume-custom-dialog__custom-input" />
|
||||
<span class="job-resume-custom-dialog__custom-checkmark"></span>
|
||||
<span class="job-resume-custom-dialog__custom-label">{{ item.label }}</span>
|
||||
<!-- 技能和工作经验的问号提示(使用 el-tooltip) -->
|
||||
<el-tooltip
|
||||
v-if="item.tooltip"
|
||||
:content="item.tooltip"
|
||||
placement="right"
|
||||
:show-arrow="true"
|
||||
:popper-options="{ strategy: 'fixed' }"
|
||||
effect="dark"
|
||||
<div class="job-resume-custom-dialog__custom-v2">
|
||||
<!-- 第一部分:选择你要优化的部分 -->
|
||||
<div class="job-resume-custom-dialog__custom-section">
|
||||
<div class="job-resume-custom-dialog__custom-section-header">
|
||||
<h3 class="job-resume-custom-dialog__custom-section-title">选择你要优化的部分</h3>
|
||||
<p class="job-resume-custom-dialog__custom-section-desc">选择后,系统会基于岗位要求自动重写对应模块,提升简历通过初筛的概率</p>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__custom-cards">
|
||||
<label
|
||||
v-for="item in optimizeSections"
|
||||
:key="item.key"
|
||||
class="job-resume-custom-dialog__custom-card"
|
||||
:class="{ 'job-resume-custom-dialog__custom-card--checked': item.checked }"
|
||||
>
|
||||
<span class="job-resume-custom-dialog__custom-tooltip-trigger">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-resume-custom-dialog__custom-tooltip-icon">
|
||||
<circle cx="8" cy="8" r="7" stroke="currentColor" stroke-width="1.2"/>
|
||||
<path d="M6.5 6.5a1.5 1.5 0 112.12 1.37c-.42.18-.62.5-.62.88V9.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||
<circle cx="8" cy="11.5" r="0.6" fill="currentColor"/>
|
||||
<input type="checkbox" v-model="item.checked" class="job-resume-custom-dialog__custom-card-input" />
|
||||
<!-- 自定义复选框圆形 -->
|
||||
<span class="job-resume-custom-dialog__custom-card-check">
|
||||
<svg v-if="item.checked" viewBox="0 0 20 20" fill="none" class="job-resume-custom-dialog__custom-card-check-svg">
|
||||
<circle cx="10" cy="10" r="10" fill="#52CAD1"/>
|
||||
<path d="M6 10.5l2.5 2.5 5.5-5.5" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<svg v-else viewBox="0 0 20 20" fill="none" class="job-resume-custom-dialog__custom-card-check-svg">
|
||||
<circle cx="10" cy="10" r="9.33" stroke="#6A7282" stroke-width="1.33"/>
|
||||
</svg>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
<!-- 文字内容 -->
|
||||
<span class="job-resume-custom-dialog__custom-card-text">
|
||||
<span class="job-resume-custom-dialog__custom-card-label">{{ item.label }}</span>
|
||||
<span v-if="item.tooltip" class="job-resume-custom-dialog__custom-card-tip">{{ item.tooltip }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 右侧:选择要新增的技能关键词 -->
|
||||
<div class="job-resume-custom-dialog__custom-panel">
|
||||
<h3 class="job-resume-custom-dialog__custom-panel-title">2.选择你要新增的技能关键词</h3>
|
||||
<div class="job-resume-custom-dialog__custom-options">
|
||||
<label v-for="skill in newSkillOptions" :key="skill.name" class="job-resume-custom-dialog__custom-checkbox">
|
||||
<input type="checkbox" v-model="skill.checked" class="job-resume-custom-dialog__custom-input" />
|
||||
<span class="job-resume-custom-dialog__custom-checkmark"></span>
|
||||
<span class="job-resume-custom-dialog__custom-label">{{ skill.name }}</span>
|
||||
<!-- 第二部分:选择你要新增的技能关键词 -->
|
||||
<div class="job-resume-custom-dialog__custom-section">
|
||||
<div class="job-resume-custom-dialog__custom-section-header">
|
||||
<h3 class="job-resume-custom-dialog__custom-section-title">选择你要新增的技能关键词</h3>
|
||||
<p class="job-resume-custom-dialog__custom-section-desc">关键词会被自然融入经历描述中,避免生硬堆砌</p>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__custom-cards">
|
||||
<label
|
||||
v-for="skill in newSkillOptions"
|
||||
:key="skill.name"
|
||||
class="job-resume-custom-dialog__custom-card"
|
||||
:class="{ 'job-resume-custom-dialog__custom-card--checked': skill.checked }"
|
||||
>
|
||||
<input type="checkbox" v-model="skill.checked" class="job-resume-custom-dialog__custom-card-input" />
|
||||
<!-- 自定义复选框圆形 -->
|
||||
<span class="job-resume-custom-dialog__custom-card-check">
|
||||
<svg v-if="skill.checked" viewBox="0 0 20 20" fill="none" class="job-resume-custom-dialog__custom-card-check-svg">
|
||||
<circle cx="10" cy="10" r="10" fill="#52CAD1"/>
|
||||
<path d="M6 10.5l2.5 2.5 5.5-5.5" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<svg v-else viewBox="0 0 20 20" fill="none" class="job-resume-custom-dialog__custom-card-check-svg">
|
||||
<circle cx="10" cy="10" r="9.33" stroke="#6A7282" stroke-width="1.33"/>
|
||||
</svg>
|
||||
</span>
|
||||
<!-- 文字内容 -->
|
||||
<span class="job-resume-custom-dialog__custom-card-text">
|
||||
<span class="job-resume-custom-dialog__custom-card-label">{{ skill.name }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 步骤四:预览 -->
|
||||
<!-- 步骤三:简历预览 -->
|
||||
<template v-if="currentStep === 4">
|
||||
<div class="job-resume-custom-dialog__preview">
|
||||
<!-- 左侧:简历模板预览(支持差异对比模式) -->
|
||||
@@ -280,18 +300,22 @@
|
||||
</div>
|
||||
<!-- 抽屉底部按钮 -->
|
||||
<div v-if="currentStep < 4" class="job-resume-custom-dialog__drawer-footer">
|
||||
<!-- 步骤二(定制简历):上一步 + 下一步 -->
|
||||
<template v-if="currentStep === 3">
|
||||
<div class="job-resume-custom-dialog__footer-btns">
|
||||
<button class="job-resume-custom-dialog__prev-btn" @click="goToStep(2)">上一步</button>
|
||||
<button class="job-resume-custom-dialog__next-btn" @click="handleDrawerNext">下一步</button>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 步骤一(差距分析):立即定制简历 -->
|
||||
<template v-else>
|
||||
<button class="job-resume-custom-dialog__primary-btn" @click="handleDrawerNext">立即定制简历</button>
|
||||
</template>
|
||||
</div>
|
||||
<!-- 步骤四专属底部:下载简历 + 立即去投递 -->
|
||||
<!-- 步骤三专属底部:下载简历 + 立即去投递 -->
|
||||
<div v-if="currentStep === 4" class="job-resume-custom-dialog__preview-footer mt10 pt30">
|
||||
<!-- 左侧:下载简历按钮(带下拉) -->
|
||||
<div class="job-resume-custom-dialog__download-wrap">
|
||||
<button class="job-resume-custom-dialog__download-btn" @click="toggleDownloadMenu">下载简历</button>
|
||||
<div v-if="showDownloadMenu" class="job-resume-custom-dialog__download-menu">
|
||||
<button class="job-resume-custom-dialog__download-option" @click="handleDownload('pdf')">下载PDF</button>
|
||||
<button class="job-resume-custom-dialog__download-option" @click="handleDownload('word')">下载Word</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 左侧:上一步按钮 -->
|
||||
<button class="job-resume-custom-dialog__footer-prev-btn" @click="goToStep(3)">上一步</button>
|
||||
<!-- 右侧:立即去投递按钮 -->
|
||||
<button class="job-resume-custom-dialog__submit-btn" @click="handleSubmit">立即去投递</button>
|
||||
</div>
|
||||
@@ -318,7 +342,7 @@ import JobResumeTemplate from '@/components/JobResumeTemplate.vue'
|
||||
import JobResumeCustomEditPanel from '@/components/JobResumeCustomEditPanel.vue'
|
||||
import type { ResumeTemplateData } from '@/components/JobResumeTemplate.vue'
|
||||
import { exportResumePdf, exportResumeWord } from '@/utils/resumeExport'
|
||||
import { fetchResumeList } from '@/api/resume'
|
||||
import { fetchResumeList, setDefaultResume } from '@/api/resume'
|
||||
import type { ResumeListItem } from '@/api/resume'
|
||||
import { fetchCustomizeResume, generateCustomizeResume, aiEditResume, rollbackCustomizeResume } from '@/api/jobs'
|
||||
import type { CustomizeResumeData, AiEditChatMessage } from '@/api/jobs'
|
||||
@@ -334,6 +358,13 @@ interface ResumeOption {
|
||||
targetJob: string
|
||||
}
|
||||
|
||||
/** 差距分析项 */
|
||||
interface GapItem {
|
||||
keyword: string
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
/** 岗位信息 */
|
||||
interface JobInfo {
|
||||
title: string
|
||||
@@ -344,6 +375,8 @@ interface JobInfo {
|
||||
missingSkills: string[]
|
||||
keywords: string[]
|
||||
sourceUrl: string
|
||||
/** 差距分析列表(来自 skill-gap 接口) */
|
||||
gaps: GapItem[]
|
||||
/** 默认简历信息(来自 skill-gap 接口) */
|
||||
defaultResume: { resumeId: string; resumeName: string; targetPosition: string } | null
|
||||
}
|
||||
@@ -400,6 +433,9 @@ const matchLevelText = computed(() => {
|
||||
/** 是否为低匹配度(低于6分) */
|
||||
const isLowMatch = computed(() => props.jobInfo.matchScore < 6)
|
||||
|
||||
/** 差距分析列表(来自 jobInfo.gaps) */
|
||||
const gapItems = computed(() => props.jobInfo.gaps || [])
|
||||
|
||||
/** 跳转到指定步骤 */
|
||||
function goToStep(step: number) {
|
||||
// 从步骤4返回步骤3时,清理步骤4的状态数据(避免下次进入被旧数据影响)
|
||||
@@ -440,6 +476,11 @@ async function handleDrawerNext() {
|
||||
initSkillOptions()
|
||||
currentStep.value++
|
||||
} else if (currentStep.value === 3) {
|
||||
// 校验:至少勾选一个优化部分
|
||||
if (selectedSectionKeys.value.length === 0) {
|
||||
ElMessage.warning('请选择你要优化的部分')
|
||||
return
|
||||
}
|
||||
// 步骤3 → 步骤4:先调用定制简历接口
|
||||
await fetchAndLoadCustomResume()
|
||||
} else {
|
||||
@@ -576,7 +617,7 @@ function handleClose() {
|
||||
|
||||
|
||||
|
||||
// ==================== 定制简历选项(步骤三) ====================
|
||||
// ==================== 定制简历选项(步骤一) ====================
|
||||
|
||||
/** 优化部分选项 */
|
||||
interface OptimizeSection {
|
||||
@@ -594,19 +635,19 @@ interface SkillOption {
|
||||
|
||||
/** 左侧:可优化的简历部分 */
|
||||
const optimizeSections = ref<OptimizeSection[]>([
|
||||
{ key: 'summary', label: '个人概述', checked: false },
|
||||
{ key: 'skills', label: '技能', checked: false, tooltip: '我们将把您勾选的技能补充进简历的技能模块。这对于简历能否通过ATS关键词筛选至关重要。' },
|
||||
{ key: 'experience', label: '工作经验', checked: false, tooltip: '我们将把您选择的技能融入工作经历中,并对关键词进行润色,最大程度提升简历与岗位的匹配度。' },
|
||||
{ key: 'summary', label: '个人概述', checked: true },
|
||||
{ key: 'skills', label: '技能', checked: true, tooltip: '我们将把您勾选的技能补充进简历的技能模块。这对于简历能否通过ATS关键词筛选至关重要。' },
|
||||
{ key: 'experience', label: '工作经验', checked: true, tooltip: '我们将把您选择的技能融入工作经历中,并对关键词进行润色,最大程度提升简历与岗位的匹配度。' },
|
||||
])
|
||||
|
||||
/** 右侧:可新增的技能关键词 */
|
||||
const newSkillOptions = ref<SkillOption[]>([])
|
||||
|
||||
/** 根据缺失技能初始化技能选项 */
|
||||
/** 根据缺失技能初始化技能选项(默认全选) */
|
||||
function initSkillOptions() {
|
||||
newSkillOptions.value = missingSkills.value.map(skill => ({
|
||||
name: skill,
|
||||
checked: false,
|
||||
checked: true,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -620,7 +661,7 @@ const selectedNewSkills = computed(() =>
|
||||
newSkillOptions.value.filter(s => s.checked).map(s => s.name)
|
||||
)
|
||||
|
||||
// ==================== 简历选择(步骤二) ====================
|
||||
// ==================== 简历选择(步骤一) ====================
|
||||
|
||||
/** 简历列表(从接口获取) */
|
||||
const resumeList = ref<ResumeOption[]>([])
|
||||
@@ -680,13 +721,17 @@ function toggleResumeDropdown() {
|
||||
showResumeDropdown.value = !showResumeDropdown.value
|
||||
}
|
||||
|
||||
/** 选择简历 */
|
||||
/** 选择简历并设为默认 */
|
||||
function selectResume(r: ResumeOption) {
|
||||
selectedResume.value = r
|
||||
showResumeDropdown.value = false
|
||||
// 调用设为默认简历接口
|
||||
setDefaultResume(r.id).catch(e => {
|
||||
console.error('[JobResumeCustomDialog] 设为默认简历失败', e)
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== 步骤四:预览相关 ====================
|
||||
// ==================== 步骤一:预览相关 ====================
|
||||
|
||||
/** 简历模板组件引用 */
|
||||
const resumeTemplateRef = ref()
|
||||
@@ -715,7 +760,7 @@ const customResumeRawData = ref<CustomizeResumeData>({
|
||||
/** 当前预览右侧tab:ai-AI帮写 / edit-编辑 */
|
||||
const previewTab = ref<'ai' | 'edit'>('ai')
|
||||
|
||||
/** AI优化结果列表(根据步骤三勾选的优化项动态生成) */
|
||||
/** AI优化结果列表(根据步骤一勾选的优化项动态生成) */
|
||||
const aiOptimizeResults = computed<string[]>(() => {
|
||||
const results: string[] = []
|
||||
|
||||
|
||||
@@ -401,9 +401,11 @@ const resumeCustomJobInfo = computed(() => ({
|
||||
companyLogoUrl: job.companyLogoUrl,
|
||||
location: job.location,
|
||||
matchScore: skillGapData.value?.score ?? +(job.matchScore / 10).toFixed(1),
|
||||
missingSkills: skillGapData.value?.missingSkills || [],
|
||||
missingSkills: skillGapData.value?.missingSkills || skillGapData.value?.gaps?.map(g => g.keyword) || [],
|
||||
keywords: skillGapData.value?.job?.skillTags || job.requiredSkills.map(s => s.name),
|
||||
sourceUrl: job.sourceUrl,
|
||||
/** 差距分析列表(来自 skill-gap 接口) */
|
||||
gaps: skillGapData.value?.gaps || [],
|
||||
/** 默认简历信息(来自 skill-gap 接口) */
|
||||
defaultResume: skillGapData.value?.resume || null,
|
||||
}))
|
||||
|
||||
+9
-6
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 页面标题 + Tab 切换 -->
|
||||
<JobPageHeader v-model:activeTab="activeTab" :favoriteCount="favoriteTotal" :applyCount="applyTotal" />
|
||||
<JobPageHeader v-model:activeTab="activeTab" />
|
||||
|
||||
<!-- 筛选条件 -->
|
||||
<div v-if="isRecommendTab" class="jobs-page__filters-bar">
|
||||
@@ -214,7 +214,7 @@
|
||||
<template v-if="isAuthenticated">
|
||||
<div class="jobs-page__match-ring">
|
||||
<svg viewBox="0 0 80 80" class="jobs-page__ring-svg">
|
||||
<circle cx="40" cy="40" r="34" stroke-width="5" stroke="#9FD051" fill="none" opacity="0.3"/>
|
||||
<circle cx="40" cy="40" r="34" stroke-width="5" :stroke="matchRingColor(job.matchScore)" fill="none" opacity="0.3"/>
|
||||
<circle cx="40" cy="40" r="34" stroke-width="5" fill="none"
|
||||
:stroke="matchRingColor(job.matchScore)"
|
||||
stroke-linecap="round"
|
||||
@@ -1194,20 +1194,23 @@ async function handleRemoveFromList(job: JobItem) {
|
||||
|
||||
/** 根据匹配分数返回对应的 CSS 类名 */
|
||||
function matchClass(score: number) {
|
||||
return score >= 80 ? 'jobs-page__job-match--high' : 'jobs-page__job-match--low'
|
||||
if (score >= 80) return 'jobs-page__job-match--high'
|
||||
if (score >= 40) return 'jobs-page__job-match--medium'
|
||||
return 'jobs-page__job-match--low'
|
||||
}
|
||||
|
||||
/** 根据匹配分数返回环形颜色 */
|
||||
function matchRingColor(score: number) {
|
||||
if (score >= 80) return '#9FD051'
|
||||
return '#9FD051'
|
||||
if (score >= 40) return '#F59E0B'
|
||||
return '#FE6D52'
|
||||
}
|
||||
|
||||
/** 根据匹配分数返回匹配等级文案 */
|
||||
function matchLevelText(score: number) {
|
||||
if (score >= 80) return '高匹配度'
|
||||
if (score >= 60) return '一般匹配'
|
||||
return '匹配度偏低'
|
||||
if (score >= 40) return '中匹配度'
|
||||
return '低匹配度'
|
||||
}
|
||||
|
||||
/** 跳转到岗位详情页 — 未登录弹登录框,登录后再跳转 */
|
||||
|
||||
Reference in New Issue
Block a user