导师相关接口换offer派接口,职位列表和职位详情,Nova聊天侧边栏UI重写
This commit is contained in:
@@ -49,6 +49,8 @@ export interface JobListItem {
|
||||
matchDetail: MatchDetail
|
||||
/** 招聘分类:0=社招 1=校招 2=实习 */
|
||||
recruitCategory?: number
|
||||
/** 学历要求 0=不限 1=大专 2=本科 3=硕士 4=博士 */
|
||||
education?: number
|
||||
}
|
||||
|
||||
// ==================== 分页结构 ====================
|
||||
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
import jianshiRequest from '@/utils/jianshiRequest'
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 分页查询导师列表请求参数
|
||||
@@ -81,10 +81,10 @@ export interface MentorPageResult {
|
||||
|
||||
/**
|
||||
* 分页查询导师列表
|
||||
* POST /public/website/pageSelectMentor
|
||||
* POST /public/mentor/page(offerpai 后端)
|
||||
*/
|
||||
export function fetchMentorList(params: MentorListParams): Promise<MentorPageResult> {
|
||||
return jianshiRequest.post('/public/website/pageSelectMentor', params)
|
||||
return request.post('/public/mentor/page', params)
|
||||
}
|
||||
|
||||
// ==================== 导师详情接口 ====================
|
||||
@@ -189,16 +189,16 @@ export interface MentorTalk {
|
||||
|
||||
/**
|
||||
* 获取导师详情信息
|
||||
* GET /public/website/selectMentorFullInfo?mentorId=xxx
|
||||
* GET /public/mentor/info?mentorId=xxx(offerpai 后端)
|
||||
*/
|
||||
export function fetchMentorFullInfo(mentorId: string): Promise<MentorFullInfo> {
|
||||
return jianshiRequest.get('/public/website/selectMentorFullInfo', { params: { mentorId } })
|
||||
return request.get('/public/mentor/info', { params: { mentorId } })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取导师话题列表
|
||||
* GET /public/website/selectTalk?teacherId=xxx
|
||||
* GET /public/mentor/talk?teacherId=xxx(offerpai 后端)
|
||||
*/
|
||||
export function fetchMentorTalks(teacherId: string): Promise<MentorTalk[]> {
|
||||
return jianshiRequest.get('/public/website/selectTalk', { params: { teacherId } })
|
||||
return request.get('/public/mentor/talk', { params: { teacherId } })
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
@@ -3,10 +3,67 @@ html{
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
// 全局滚动条美化(必须在 body 包裹外,才能作用到 body/html 自身的滚动条)
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #D4D4D4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
html, body {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #D4D4D4 transparent;
|
||||
}
|
||||
|
||||
// body 滚动条在缩放模式下会被 transform scale 放大变丑,强制隐藏
|
||||
body {
|
||||
&::-webkit-scrollbar {
|
||||
width: 0 !important;
|
||||
display: none !important;
|
||||
}
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
// 用 body:not(#_) 包裹,提升选择器权重到 id 级别,确保工具类优先级最高
|
||||
// :not(#_) 永远为 true,不影响匹配,只提升权重
|
||||
body:not(#_) {
|
||||
|
||||
// 统一细滚动条工具类(4px 宽、圆角、无按钮)
|
||||
.scrollbar-thin {
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #D4D4D4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #D4D4D4 transparent;
|
||||
}
|
||||
|
||||
//组件定制样式
|
||||
.box-shadow-head{
|
||||
box-shadow: 0px 6px 16px rgba(0, 0, 0, 0.08), 0px 3px 6px -4px rgba(0, 0, 0, 0.12), 0px -3px 6px -4px rgba(0, 0, 0, 0.12);
|
||||
|
||||
@@ -1,84 +1,119 @@
|
||||
// 颜色变量已移至全局 variables.scss
|
||||
@use '../variables' as *;
|
||||
|
||||
.ai-chat {
|
||||
width: var(--chat-width, 4.0rem);
|
||||
max-width: 4.0rem;
|
||||
min-width: 1.5rem;
|
||||
font-size: 0.14rem;
|
||||
width: var(--chat-width, 3.6rem);
|
||||
height: var(--app-height, 100vh);
|
||||
background: #f3f4f6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
background: $bg-white;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border-left: 1px solid #e5e7eb;
|
||||
|
||||
&__banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #0F172B;
|
||||
color: #fff;
|
||||
padding: 0.12rem 0.14rem;
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
border-radius: 0.2rem;
|
||||
margin: 0.15rem;
|
||||
height: 0.41rem;
|
||||
}
|
||||
|
||||
&__banner-arrow {
|
||||
font-size: 0.16rem;
|
||||
padding-bottom: 0.05rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
|
||||
// ==================== 顶部头部区域 ====================
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.1rem;
|
||||
justify-content: space-between;
|
||||
padding: 0.16rem;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
height: 0.80rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&__header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.12rem;
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
width: 0.44rem;
|
||||
height: 0.44rem;
|
||||
border-radius: 50%;
|
||||
background: #e5e7eb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.2rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&__info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__name {
|
||||
font-size: 0.16rem;
|
||||
font-weight: 600;
|
||||
color: #1a1a2e;
|
||||
font-size: 0.15rem;
|
||||
font-weight: 520;
|
||||
line-height: 0.22rem;
|
||||
color: $text-dark;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
font-size: 0.12rem;
|
||||
color: #6b7280;
|
||||
margin-top: 0.02rem;
|
||||
font-weight: 330;
|
||||
line-height: 0.18rem;
|
||||
color: #99A1AF;
|
||||
}
|
||||
|
||||
// 在线状态标签
|
||||
&__online-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.04rem 0.08rem;
|
||||
gap: 0.04rem;
|
||||
background: #52CAD1;
|
||||
border-radius: 0.12rem;
|
||||
font-size: 0.12rem;
|
||||
font-weight: 330;
|
||||
line-height: 0.16rem;
|
||||
color: $bg-white;
|
||||
}
|
||||
|
||||
&__online-dot {
|
||||
width: 0.06rem;
|
||||
height: 0.06rem;
|
||||
background: $bg-white;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
// ==================== 聊天消息区域 ====================
|
||||
&__messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0.16rem;
|
||||
padding: 0 0.16rem;
|
||||
padding-bottom: 0.16rem;
|
||||
}
|
||||
|
||||
// 欢迎气泡
|
||||
&__welcome-bubble {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 0.16rem;
|
||||
gap: 0.08rem;
|
||||
background: #F4FBFB;
|
||||
border: 1px solid #AEE6EA;
|
||||
border-radius: 0.12rem;
|
||||
margin-bottom: 0.17rem;
|
||||
}
|
||||
|
||||
&__welcome-title {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 520;
|
||||
line-height: 0.16rem;
|
||||
color: $text-dark;
|
||||
}
|
||||
|
||||
&__welcome-text {
|
||||
font-size: 0.12rem;
|
||||
font-weight: 330;
|
||||
line-height: 0.18rem;
|
||||
color: #99A1AF;
|
||||
}
|
||||
|
||||
// ==================== 消息气泡 ====================
|
||||
&__msg {
|
||||
margin-bottom: 0.12rem;
|
||||
margin-bottom: 0.17rem;
|
||||
display: flex;
|
||||
|
||||
&--ai {
|
||||
@@ -91,13 +126,14 @@
|
||||
}
|
||||
|
||||
&__msg-bubble {
|
||||
border-radius: 0.1rem;
|
||||
padding: 0.12rem 0.14rem;
|
||||
font-size: 0.13rem;
|
||||
line-height: 1.6;
|
||||
max-width: 85%;
|
||||
max-width: 2.8rem;
|
||||
padding: 0.08rem 0.12rem;
|
||||
font-size: 0.14rem;
|
||||
font-weight: 330;
|
||||
line-height: 0.24rem;
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.04);
|
||||
|
||||
// ===== Markdown 渲染样式 =====
|
||||
// Markdown 渲染样式
|
||||
p {
|
||||
margin: 0 0 0.08rem;
|
||||
&:last-child { margin-bottom: 0; }
|
||||
@@ -174,123 +210,183 @@
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background: #fafbfc;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
margin: 0.1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__msg--ai &__msg-bubble {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
// 用户消息气泡 — 品牌色背景,右上圆角小
|
||||
&__msg--user &__msg-bubble {
|
||||
background: #fff;
|
||||
color: #1a1a2e;
|
||||
background: #52CAD1;
|
||||
color: $bg-white;
|
||||
border-radius: 0.12rem 0.04rem 0.12rem 0.12rem;
|
||||
}
|
||||
|
||||
&__msg-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.14rem;
|
||||
margin-bottom: 0.06rem;
|
||||
color: #1a1a2e;
|
||||
// AI 消息气泡 — 白色背景绿色边框,左上圆角小
|
||||
&__msg--ai &__msg-bubble {
|
||||
background: $bg-white;
|
||||
color: $text-dark;
|
||||
border: 1px solid #AEE6EA;
|
||||
box-shadow: 0px 2px 4px rgba(82, 202, 209, 0.1);
|
||||
border-radius: 0.04rem 0.12rem 0.12rem 0.12rem;
|
||||
}
|
||||
|
||||
&__msg-text {
|
||||
color: #6b7280;
|
||||
font-size: 0.12rem;
|
||||
// ==================== 三点等待动效 ====================
|
||||
&__typing-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.14rem 0.12rem;
|
||||
gap: 0.06rem;
|
||||
background: $bg-white;
|
||||
border: 1px solid #AEE6EA;
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.04);
|
||||
border-radius: 0.04rem 0.12rem 0.12rem 0.12rem;
|
||||
}
|
||||
|
||||
&__quick-questions {
|
||||
&__typing-dot {
|
||||
width: 0.06rem;
|
||||
height: 0.06rem;
|
||||
border-radius: 50%;
|
||||
background: #AEE6EA;
|
||||
animation: typingBounce 1.4s infinite ease-in-out both;
|
||||
|
||||
&:nth-child(1) {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
background: rgba(174, 230, 234, 0.6);
|
||||
}
|
||||
&:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
background: rgba(174, 230, 234, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes typingBounce {
|
||||
0%, 80%, 100% {
|
||||
transform: scale(1);
|
||||
opacity: 0.6;
|
||||
}
|
||||
40% {
|
||||
transform: scale(1.3);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 话题推荐模块 ====================
|
||||
&__topics {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.08rem;
|
||||
margin-top: 0.12rem;
|
||||
align-items: flex-start;
|
||||
padding: 0.16rem 0.16rem 0.16rem 0.16rem;
|
||||
gap: 0.12rem;
|
||||
background: linear-gradient(180deg, #EDF9FA 0%, #FFFFFF 20.63%);
|
||||
border: 1px solid #AEE6EA;
|
||||
border-radius: 0.12rem;
|
||||
margin: 0 0.16rem;
|
||||
margin-bottom: 0.12rem;
|
||||
}
|
||||
|
||||
&__quick-item {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
&__topics-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.04rem;
|
||||
}
|
||||
|
||||
&__topics-icon {
|
||||
width: 0.16rem;
|
||||
height: 0.16rem;
|
||||
}
|
||||
|
||||
&__topics-title {
|
||||
font-size: 0.14rem;
|
||||
font-weight: 520;
|
||||
line-height: 0.19rem;
|
||||
color: #52CAD1;
|
||||
}
|
||||
|
||||
&__topic-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.08rem 0.12rem;
|
||||
background: $bg-white;
|
||||
border: 1px solid #F0F0F0;
|
||||
border-radius: 0.08rem;
|
||||
padding: 0.1rem 0.14rem;
|
||||
color: $text-middle;
|
||||
font-size: 0.12rem;
|
||||
font-weight: 330;
|
||||
line-height: 0.16rem;
|
||||
color: $text-middle;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
max-width: 80%;
|
||||
|
||||
&:hover {
|
||||
background: #f9fafb;
|
||||
color: #374151;
|
||||
background: $accent;
|
||||
color: #fff;
|
||||
|
||||
}
|
||||
|
||||
/* AI 提问靠左 */
|
||||
&--ai {
|
||||
align-self: flex-start;
|
||||
}
|
||||
// 第一个按钮用品牌色
|
||||
&--primary {
|
||||
background: #52CAD1;
|
||||
border-color: #52CAD1;
|
||||
color: $bg-white;
|
||||
|
||||
/* 用户提问靠右 */
|
||||
&--user {
|
||||
align-self: flex-end;
|
||||
background: #f0f3f6;
|
||||
border-color: #e0e3e6;
|
||||
&:hover {
|
||||
background: #47b8bf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 底部输入框 ====================
|
||||
&__input-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.08rem 0.12rem;
|
||||
gap: 0.08rem;
|
||||
padding: 0.12rem 0.16rem;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
background: #fff;
|
||||
margin: 0 0.16rem;
|
||||
margin-bottom: 0.16rem;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 1px solid #AEE6EA;
|
||||
border-radius: 0.08rem;
|
||||
backdrop-filter: blur(12px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&__input {
|
||||
flex: 1;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 0.06rem;
|
||||
padding: 0.1rem 0.12rem;
|
||||
color: #1a1a2e;
|
||||
font-size: 0.12rem;
|
||||
border: none;
|
||||
outline: none;
|
||||
background: transparent;
|
||||
font-size: 0.12rem;
|
||||
font-weight: 400;
|
||||
line-height: 0.15rem;
|
||||
color: $text-dark;
|
||||
|
||||
&::placeholder {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: #9ca3af;
|
||||
color: #99A1AF;
|
||||
}
|
||||
}
|
||||
|
||||
&__send-btn {
|
||||
width: 0.36rem;
|
||||
height: 0.36rem;
|
||||
border-radius: 50%;
|
||||
background: #1a1a2e;
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-size: 0.16rem;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 0.32rem;
|
||||
height: 0.32rem;
|
||||
background: #52CAD1;
|
||||
border-radius: 0.08rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: #2e3142;
|
||||
background: #47b8bf;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@use '../variables' as *;
|
||||
|
||||
.job-page-header {
|
||||
margin-bottom: 0;
|
||||
|
||||
|
||||
&__title {
|
||||
font-size: 0.24rem;
|
||||
font-size: 0.18rem;
|
||||
font-weight: 700;
|
||||
color: $text-dark;
|
||||
margin: 0 !important;
|
||||
@@ -15,8 +15,6 @@
|
||||
&__subtitle {
|
||||
font-size: 0.13rem;
|
||||
color: $text-light;
|
||||
margin: 0.02rem 0 0 0 !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
@@ -24,7 +22,6 @@
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 0.04rem;
|
||||
margin: 0.1rem 0 0.16rem 0;
|
||||
background: $bg-white;
|
||||
border-radius: 0.22rem;
|
||||
padding: 0.04rem;
|
||||
@@ -51,13 +48,16 @@
|
||||
|
||||
&__tab {
|
||||
font-size: 0.13rem;
|
||||
color: $text-light;
|
||||
color: $text-middle;
|
||||
cursor: pointer;
|
||||
padding: 0.06rem 0.16rem;
|
||||
border-radius: 0.18rem;
|
||||
padding: 0.06rem 0.20rem;
|
||||
border-radius: 0.08rem;
|
||||
transition: all 0.25s ease;
|
||||
user-select: none;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.06rem;
|
||||
|
||||
&:hover {
|
||||
color: $text-dark;
|
||||
@@ -76,4 +76,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 计数圆形徽章 */
|
||||
&__tab-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0.20rem;
|
||||
height: 0.20rem;
|
||||
padding: 0 0.05rem;
|
||||
border-radius: 50%;
|
||||
background: #F3F4F6;
|
||||
font-size: 0.11rem;
|
||||
font-weight: 500;
|
||||
color: $text-middle;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,15 +224,15 @@
|
||||
&__portfolio-link {
|
||||
font-size: 0.13rem;
|
||||
line-height: 1.6;
|
||||
color: $accent;
|
||||
color: $text-middle;
|
||||
word-break: break-all;
|
||||
text-decoration: none;
|
||||
text-decoration: underline;
|
||||
transition: color 0.2s;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
|
||||
&:hover {
|
||||
color: $accent-hover;
|
||||
text-decoration: underline;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
.job-detail {
|
||||
&__content {
|
||||
margin-left: 2rem;
|
||||
margin-right: var(--chat-width, 4.0rem);
|
||||
margin-right: var(--chat-width, 3.6rem);
|
||||
flex: 1;
|
||||
padding: 0.12rem 0.12rem 0.12rem 0.12rem;
|
||||
height: var(--app-height, 100vh);
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
@@ -14,155 +13,168 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 0.14rem;
|
||||
|
||||
// 手动包裹的两层 div,需要撑满剩余高度并允许内部滚动
|
||||
> .bg-white {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
> .bg-main {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 页面标题 ----
|
||||
&__header {
|
||||
margin-bottom: 0;
|
||||
// ---- 主内容卡片 ----
|
||||
&__main-card {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
margin: 0.16rem 0.24rem;
|
||||
background: $bg-white;
|
||||
border: 1px solid #F0F0F0;
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.04);
|
||||
border-radius: 0.08rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__page-title {
|
||||
font-size: 0.24rem;
|
||||
font-weight: 700;
|
||||
color: $text-dark;
|
||||
margin: 0;
|
||||
line-height: 1.1;
|
||||
// ---- 返回按钮 ----
|
||||
&__back-row {
|
||||
padding: 0.24rem 0.32rem 0;
|
||||
}
|
||||
|
||||
&__page-subtitle {
|
||||
font-size: 0.13rem;
|
||||
color: $text-light;
|
||||
margin: 0.02rem 0 0 0;
|
||||
}
|
||||
|
||||
// ---- Tab 切换 ----
|
||||
&__tabs {
|
||||
display: inline-flex;
|
||||
&__back-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.04rem;
|
||||
margin: 0.1rem 0 0.16rem 0;
|
||||
background: $bg-white;
|
||||
border-radius: 0.22rem;
|
||||
padding: 0.04rem;
|
||||
}
|
||||
|
||||
&__tab {
|
||||
font-size: 0.13rem;
|
||||
color: $text-light;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0.06rem 0.22rem;
|
||||
border-radius: 0.18rem;
|
||||
transition: all 0.25s ease;
|
||||
user-select: none;
|
||||
font-weight: 500;
|
||||
color: $text-middle;
|
||||
font-size: 0.14rem;
|
||||
line-height: 0.19rem;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
color: $text-dark;
|
||||
background: $theme-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__back-icon {
|
||||
width: 0.16rem;
|
||||
height: 0.16rem;
|
||||
}
|
||||
|
||||
// ---- 内容区域(可滚动) ----
|
||||
&__body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0.24rem 0.32rem 0.4rem;
|
||||
}
|
||||
|
||||
// ---- 岗位头部信息区域 ----
|
||||
&__header-section {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.24rem;
|
||||
}
|
||||
|
||||
// ---- 左侧基本信息 ----
|
||||
&__basic-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.12rem;
|
||||
margin-bottom: 0.08rem;
|
||||
}
|
||||
|
||||
&__job-title {
|
||||
font-size: 0.24rem;
|
||||
font-weight: 600;
|
||||
line-height: 0.32rem;
|
||||
color: $text-dark;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__fav-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
flex-shrink: 0;
|
||||
color: #8C8C8C;
|
||||
transition: color 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
&--active {
|
||||
background: $accent;
|
||||
color: $bg-white;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 0.02rem 0.08rem rgba(79, 194, 201, 0.3);
|
||||
color: $danger;
|
||||
|
||||
&:hover {
|
||||
background: $accent-hover;
|
||||
color: $bg-white;
|
||||
.job-detail__fav-icon path {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 顶部操作栏 ----
|
||||
&__toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.14rem;
|
||||
&__fav-icon {
|
||||
width: 0.18rem;
|
||||
height: 0.18rem;
|
||||
}
|
||||
|
||||
&__close-btn {
|
||||
width: 0.32rem;
|
||||
height: 0.32rem;
|
||||
border-radius: 50%;
|
||||
border: 1px solid $border-color;
|
||||
background: $bg-white;
|
||||
color: $text-dark;
|
||||
cursor: pointer;
|
||||
&__meta-row {
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.16rem;
|
||||
color: $text-middle;
|
||||
margin-bottom: 0.12rem;
|
||||
}
|
||||
|
||||
&__meta-dot {
|
||||
margin: 0 0.06rem;
|
||||
color: $text-middle;
|
||||
}
|
||||
|
||||
// ---- 操作按钮 ----
|
||||
&__action-btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.16rem;
|
||||
}
|
||||
|
||||
&__nova-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.08rem 0.16rem;
|
||||
border: 1px solid $accent;
|
||||
border-radius: 0.08rem;
|
||||
background: $bg-white;
|
||||
color: $accent;
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
line-height: 0.19rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: $theme-color;
|
||||
border-color: $accent;
|
||||
color: $accent;
|
||||
}
|
||||
}
|
||||
|
||||
&__close-icon {
|
||||
width: 0.14rem;
|
||||
height: 0.14rem;
|
||||
}
|
||||
|
||||
&__toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.1rem;
|
||||
}
|
||||
|
||||
&__tool-btn {
|
||||
width: 0.32rem;
|
||||
height: 0.32rem;
|
||||
border-radius: 50%;
|
||||
border: 1px solid $border-color;
|
||||
background: $bg-white;
|
||||
color: $text-light;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: $accent;
|
||||
border-color: $accent;
|
||||
background: $theme-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__tool-icon {
|
||||
width: 0.14rem;
|
||||
height: 0.14rem;
|
||||
}
|
||||
|
||||
&__apply-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.08rem 0.16rem;
|
||||
background: $accent;
|
||||
border: 1px solid $accent;
|
||||
border-radius: 0.08rem;
|
||||
color: $bg-white;
|
||||
border: none;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.07rem 0.22rem;
|
||||
font-size: 0.13rem;
|
||||
font-weight: 500;
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
line-height: 0.19rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
|
||||
@@ -171,225 +183,59 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 内容区域 ----
|
||||
&__body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.14rem;
|
||||
padding-bottom: 0.4rem;
|
||||
padding-right: 0.05rem;
|
||||
}
|
||||
|
||||
// ---- 导航 Tab ----
|
||||
&__nav-tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.2rem;
|
||||
margin-bottom: 0.04rem;
|
||||
}
|
||||
|
||||
&__nav-tab {
|
||||
font-size: 0.16rem;
|
||||
font-weight: 600;
|
||||
color: $text-light;
|
||||
cursor: pointer;
|
||||
padding-bottom: 0.06rem;
|
||||
border-bottom: 2px solid transparent;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: $text-dark;
|
||||
}
|
||||
|
||||
&--active {
|
||||
color: $text-dark;
|
||||
border-bottom-color: $text-dark;
|
||||
}
|
||||
}
|
||||
|
||||
&__nav-tab-right {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.16rem;
|
||||
padding-right: 0.05rem;
|
||||
}
|
||||
|
||||
&__link-btn {
|
||||
font-size: 0.12rem;
|
||||
color: $text-light;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: $accent;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 卡片 ----
|
||||
&__card {
|
||||
background: $bg-white;
|
||||
border-radius: 0.12rem;
|
||||
padding: 0.22rem 0.24rem;
|
||||
}
|
||||
|
||||
&__card-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
&__card-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
// ---- 公司 & 职位头部 ----
|
||||
&__company-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.1rem;
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
&__company-icon {
|
||||
width: 0.36rem;
|
||||
height: 0.36rem;
|
||||
// ---- 右侧匹配度卡片 ----
|
||||
&__match-card {
|
||||
flex-shrink: 0;
|
||||
width: 2.06rem;
|
||||
background: #FBFDF7;
|
||||
border: 1px solid #9FD051;
|
||||
border-radius: 0.08rem;
|
||||
background: $theme-color;
|
||||
padding: 0.16rem 0.24rem;
|
||||
}
|
||||
|
||||
&__match-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid $border-color;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__company-svg {
|
||||
width: 0.2rem;
|
||||
height: 0.2rem;
|
||||
color: $accent;
|
||||
}
|
||||
|
||||
&__company-name {
|
||||
font-size: 0.15rem;
|
||||
font-weight: 600;
|
||||
color: $text-dark;
|
||||
}
|
||||
|
||||
&__job-title {
|
||||
font-size: 0.2rem;
|
||||
font-weight: 700;
|
||||
color: $text-dark;
|
||||
margin: 0 0 0.1rem 0;
|
||||
}
|
||||
|
||||
&__job-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&__meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.05rem;
|
||||
font-size: 0.12rem;
|
||||
color: $text-light;
|
||||
}
|
||||
|
||||
&__meta-icon {
|
||||
width: 0.14rem;
|
||||
height: 0.14rem;
|
||||
color: $text-light;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// ---- 匹配度 ----
|
||||
&__match-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__match-ring {
|
||||
position: relative;
|
||||
width: 0.72rem;
|
||||
height: 0.72rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__ring-svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
align-items: baseline;
|
||||
gap: 0.08rem;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
|
||||
&__match-score {
|
||||
font-size: 0.18rem;
|
||||
font-weight: 700;
|
||||
color: $accent-hover;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
font-size: 0.3rem;
|
||||
font-weight: 630;
|
||||
line-height: 0.4rem;
|
||||
color: #9FD051;
|
||||
}
|
||||
|
||||
&__match-label {
|
||||
font-size: 0.1rem;
|
||||
color: $text-light;
|
||||
margin-top: 0.04rem;
|
||||
font-size: 0.13rem;
|
||||
font-weight: 520;
|
||||
line-height: 0.17rem;
|
||||
color: #9FD051;
|
||||
}
|
||||
|
||||
&__match-details {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.12rem;
|
||||
margin-top: 0.12rem;
|
||||
}
|
||||
|
||||
&__match-item {
|
||||
&__match-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.03rem;
|
||||
gap: 0.12rem;
|
||||
}
|
||||
|
||||
&__match-mini-ring {
|
||||
position: relative;
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
&__match-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
&__mini-ring-svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
&__match-row-label {
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.16rem;
|
||||
color: #6B7280;
|
||||
}
|
||||
|
||||
&__mini-score {
|
||||
font-size: 0.1rem;
|
||||
font-weight: 600;
|
||||
&__match-row-score {
|
||||
font-size: 0.12rem;
|
||||
line-height: 0.16rem;
|
||||
color: $text-dark;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&__match-item-label {
|
||||
font-size: 0.09rem;
|
||||
color: $text-light;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// ---- 优化简历提示条 ----
|
||||
@@ -397,93 +243,70 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(90deg, #94EF9E , #53D9C8 );
|
||||
color: $bg-white;
|
||||
border-radius: 0.1rem;
|
||||
padding: 0.14rem 0.2rem;
|
||||
font-size: 0.13rem;
|
||||
background: linear-gradient(270deg, rgba(159, 208, 81, 0.1) 0%, rgba(82, 202, 209, 0.1) 62.02%);
|
||||
border-radius: 0.08rem;
|
||||
padding: 0.12rem 0.24rem;
|
||||
margin-bottom: 0.32rem;
|
||||
}
|
||||
|
||||
&__optimize-text {
|
||||
font-size: 0.16rem;
|
||||
font-weight: 600;
|
||||
line-height: 0.21rem;
|
||||
color: $text-dark;
|
||||
}
|
||||
|
||||
&__optimize-btn {
|
||||
background: $bg-white;
|
||||
color: $text-dark;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.08rem 0.16rem;
|
||||
background: #52CAD1;
|
||||
border: none;
|
||||
border-radius: 0.06rem;
|
||||
padding: 0.07rem 0.18rem;
|
||||
font-size: 0.12rem;
|
||||
font-weight: 500;
|
||||
border-radius: 0.08rem;
|
||||
color: $bg-white;
|
||||
font-size: 0.14rem;
|
||||
font-weight: 600;
|
||||
line-height: 0.19rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
transition: background 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: $theme-color;
|
||||
color: $accent-hover;
|
||||
background: $btn-dark-hover;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 描述文本 ----
|
||||
&__desc-text {
|
||||
font-size: 0.13rem;
|
||||
color: $text-dark;
|
||||
line-height: 1.8;
|
||||
margin: 0 0 0.12rem 0;
|
||||
// ---- 内容区块 ----
|
||||
&__section {
|
||||
margin-bottom: 0.32rem;
|
||||
}
|
||||
|
||||
// ---- 标签 ----
|
||||
&__tag-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.08rem;
|
||||
}
|
||||
|
||||
&__tag {
|
||||
font-size: 0.12rem;
|
||||
color: $text-dark;
|
||||
background: $bg-main;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 0.04rem;
|
||||
padding: 0.04rem 0.14rem;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: $accent;
|
||||
color: $accent;
|
||||
background: $theme-color;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 区块标题 ----
|
||||
&__section-title {
|
||||
font-size: 0.17rem;
|
||||
font-weight: 700;
|
||||
font-size: 0.18rem;
|
||||
font-weight: 600;
|
||||
line-height: 0.24rem;
|
||||
color: $text-dark;
|
||||
margin: 0 0 0.14rem 0;
|
||||
margin: 0 0 0.16rem 0;
|
||||
}
|
||||
|
||||
&__section-header {
|
||||
&__section-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.14rem;
|
||||
|
||||
.job-detail__section-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
flex-direction: column;
|
||||
gap: 0.12rem;
|
||||
}
|
||||
|
||||
&__skill-hint {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.04rem;
|
||||
font-size: 0.11rem;
|
||||
color: $text-light;
|
||||
&__section-item {
|
||||
font-size: 0.14rem;
|
||||
line-height: 0.19rem;
|
||||
color: $text-middle;
|
||||
}
|
||||
|
||||
&__hint-icon {
|
||||
width: 0.12rem;
|
||||
height: 0.12rem;
|
||||
flex-shrink: 0;
|
||||
&__bonus-item {
|
||||
font-size: 0.14rem;
|
||||
line-height: 0.19rem;
|
||||
color: $text-dark;
|
||||
margin-top: 0.12rem;
|
||||
}
|
||||
|
||||
// ---- 技能标签 ----
|
||||
@@ -491,161 +314,81 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.08rem;
|
||||
margin-bottom: 0.16rem;
|
||||
margin-bottom: 0.12rem;
|
||||
}
|
||||
|
||||
&__skill-tag {
|
||||
display: inline-flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.04rem;
|
||||
justify-content: center;
|
||||
padding: 0.04rem 0.06rem;
|
||||
font-size: 0.12rem;
|
||||
color: $text-dark;
|
||||
background: $bg-main;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.05rem 0.14rem;
|
||||
transition: all 0.2s;
|
||||
line-height: 0.18rem;
|
||||
color: $text-middle;
|
||||
background: #F3F4F6;
|
||||
border-radius: 0.04rem;
|
||||
|
||||
&--matched {
|
||||
background: $theme-color;
|
||||
border-color: $accent;
|
||||
color: $accent-hover;
|
||||
color: $accent;
|
||||
}
|
||||
}
|
||||
|
||||
&__skill-close {
|
||||
width: 0.1rem;
|
||||
height: 0.1rem;
|
||||
color: $accent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// ---- 列表 ----
|
||||
&__list {
|
||||
font-size: 0.13rem;
|
||||
color: $text-dark;
|
||||
line-height: 2;
|
||||
margin: 0;
|
||||
padding-left: 0.2rem;
|
||||
|
||||
li {
|
||||
padding-left: 0.04rem;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 公司信息 ----
|
||||
&__company-info {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
&__company-info-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__company-header {
|
||||
// ---- 公司概况 ----
|
||||
&__company-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.1rem;
|
||||
gap: 0.12rem;
|
||||
margin-bottom: 0.12rem;
|
||||
}
|
||||
|
||||
&__company-logo {
|
||||
width: 0.36rem;
|
||||
height: 0.36rem;
|
||||
border-radius: 50%;
|
||||
background: $bg-main;
|
||||
width: 0.48rem;
|
||||
height: 0.48rem;
|
||||
border-radius: 0.08rem;
|
||||
background: #EDF9FA;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid $border-color;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__company-info-name {
|
||||
font-size: 0.16rem;
|
||||
&__company-logo-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 0.08rem;
|
||||
}
|
||||
|
||||
&__company-svg {
|
||||
width: 0.32rem;
|
||||
height: 0.32rem;
|
||||
color: #52CAD1;
|
||||
}
|
||||
|
||||
&__company-name-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.06rem;
|
||||
}
|
||||
|
||||
&__company-name {
|
||||
font-size: 0.18rem;
|
||||
font-weight: 600;
|
||||
line-height: 0.24rem;
|
||||
color: $text-dark;
|
||||
}
|
||||
|
||||
&__company-sub {
|
||||
font-size: 0.14rem;
|
||||
line-height: 0.19rem;
|
||||
color: $text-middle;
|
||||
}
|
||||
|
||||
&__company-desc {
|
||||
font-size: 0.12rem;
|
||||
color: $text-dark;
|
||||
line-height: 1.8;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__company-info-right {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.08rem;
|
||||
max-width: 45%;
|
||||
}
|
||||
|
||||
&__company-meta-item {
|
||||
font-size: 0.12rem;
|
||||
color: $text-dark;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
&__meta-label {
|
||||
color: $text-light;
|
||||
}
|
||||
|
||||
&__company-link {
|
||||
color: $accent;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
|
||||
&:hover {
|
||||
color: $accent-hover;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 融资 ----
|
||||
&__funding-label {
|
||||
font-weight: 600;
|
||||
color: $text-dark;
|
||||
}
|
||||
|
||||
// ---- 最新动态 ----
|
||||
&__news-list {
|
||||
display: flex;
|
||||
gap: 0.14rem;
|
||||
}
|
||||
|
||||
&__news-item {
|
||||
flex: 1;
|
||||
background: $bg-main;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 0.08rem;
|
||||
padding: 0.14rem 0.16rem;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
border-color: $accent;
|
||||
background: $theme-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__news-title {
|
||||
font-size: 0.13rem;
|
||||
font-weight: 600;
|
||||
color: $text-dark;
|
||||
margin: 0 0 0.06rem 0;
|
||||
}
|
||||
|
||||
&__news-desc {
|
||||
font-size: 0.11rem;
|
||||
font-size: 0.14rem;
|
||||
line-height: 0.24rem;
|
||||
color: $text-middle;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
@use '../variables' as *;
|
||||
|
||||
.jobs-page {
|
||||
display: flex;
|
||||
|
||||
&__content {
|
||||
margin-left: 2rem;
|
||||
margin-right: var(--chat-width, 4.0rem);
|
||||
margin-right: var(--chat-width, 3.6rem);
|
||||
flex: 1;
|
||||
padding: 0.12rem 0.18rem;
|
||||
padding-bottom: 0.18rem;
|
||||
height: var(--app-height, 100vh);
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
@@ -79,10 +81,8 @@
|
||||
|
||||
// 筛选条件外层白色背景条
|
||||
&__filters-bar {
|
||||
background: $bg-white;
|
||||
border-radius: 0.12rem;
|
||||
padding: 0.14rem 0.18rem;
|
||||
margin-bottom: 0.2rem;
|
||||
margin: 0 0.24rem 0.2rem 0.24rem;
|
||||
}
|
||||
|
||||
// 筛选条件
|
||||
@@ -103,11 +103,11 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.04rem;
|
||||
background: $bg-main;
|
||||
background: #fff;
|
||||
border: 1px solid #E8E8E8;
|
||||
border-radius: 0.2rem;
|
||||
padding: 0.05rem 0.14rem;
|
||||
color: $text-dark;
|
||||
border-radius: 0.08rem;
|
||||
padding: 0.15rem 0.12rem;
|
||||
color: $text-middle;
|
||||
font-size: 0.12rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
@@ -116,9 +116,11 @@
|
||||
height: 0.234rem;
|
||||
|
||||
&:hover {
|
||||
border-color: $accent;
|
||||
color: $accent;
|
||||
background: $theme-color;
|
||||
color: #fff;
|
||||
background: $btn-dark;
|
||||
svg{
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +209,7 @@
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 0.2rem;
|
||||
margin: 0 0.24rem;
|
||||
|
||||
// 列表中每张卡片间距用 margin-bottom 替代 gap
|
||||
& > .jobs-page__job-card {
|
||||
|
||||
@@ -303,6 +303,10 @@
|
||||
|
||||
// 查看详情按钮
|
||||
&__detail-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.06rem;
|
||||
align-items: center;
|
||||
height: 0.32rem;
|
||||
line-height: 0.32rem;
|
||||
width: 0.92rem;
|
||||
|
||||
@@ -9,7 +9,7 @@ $theme-color: #F6FCFC;
|
||||
$selected-color: #F6FCFC;
|
||||
|
||||
// 页面主背景色
|
||||
$bg-main: #F3F4F5;
|
||||
$bg-main: #F3F4F6;
|
||||
|
||||
// 页面中间色景色(用于页面主题色背景和白色背景分隔,用于某些模块组件的组件外层背景等)
|
||||
$bg-middle: #E6E6E6;
|
||||
|
||||
+82
-74
@@ -1,40 +1,32 @@
|
||||
<template>
|
||||
<!-- Nova AI 对话面板 -->
|
||||
<div class="ai-chat">
|
||||
<!-- 顶部会员横幅 — 点击打开会员购买弹窗 -->
|
||||
<div class="ai-chat__banner" @click="showMemberDialog = true">
|
||||
<span class="dflex-start aliite-c"> <img src="@/assets/images/bule-flash.png" alt="Offer派" class="w16 h19 mr5" /> <span>升级 Pro 开启求职加速</span></span>
|
||||
<span class="ai-chat__banner-arrow">›</span>
|
||||
</div>
|
||||
|
||||
<!-- AI 头像和名称 -->
|
||||
<!-- 顶部头部区域 -->
|
||||
<div class="ai-chat__header">
|
||||
<div class="ai-chat__avatar">👤</div>
|
||||
<div class="ai-chat__info">
|
||||
<div class="ai-chat__name">Nova</div>
|
||||
<div class="ai-chat__desc">你的AI求职助手</div>
|
||||
<div class="ai-chat__header-left">
|
||||
<!-- Nova 头像 -->
|
||||
<img class="ai-chat__avatar" src="@/assets/images/nova-logo.png" alt="Nova" />
|
||||
<!-- 名称和副标题 -->
|
||||
<div class="ai-chat__info">
|
||||
<div class="ai-chat__name">Nova</div>
|
||||
<div class="ai-chat__desc">你的AI求职助理</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 在线状态标签 -->
|
||||
<div class="ai-chat__online-tag">
|
||||
<span class="ai-chat__online-dot"></span>
|
||||
<span>在线</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 聊天消息区域 -->
|
||||
<div class="ai-chat__messages" ref="messagesRef">
|
||||
<!-- AI 欢迎消息 -->
|
||||
<div class="ai-chat__msg ai-chat__msg--ai">
|
||||
<div class="ai-chat__msg-bubble">
|
||||
<div class="ai-chat__msg-title">欢迎回来,{{nickName}}!</div>
|
||||
<div class="ai-chat__msg-text">很高兴再次见到你,让我们继续您通往理想工作的旅程吧。</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-chat__messages scrollbar-thin" ref="messagesRef">
|
||||
|
||||
<!-- 快捷问题(仅岗位详情页显示,3个用户提问靠右) -->
|
||||
<div v-if="userQuestions.length > 0" class="ai-chat__quick-questions">
|
||||
<div
|
||||
v-for="(q, i) in userQuestions"
|
||||
:key="i"
|
||||
class="ai-chat__quick-item ai-chat__quick-item--user"
|
||||
@click="sendQuickQuestion(q)"
|
||||
>
|
||||
{{ q }}
|
||||
</div>
|
||||
<!-- 固定欢迎气泡 -->
|
||||
<div class="ai-chat__welcome-bubble">
|
||||
<div class="ai-chat__welcome-title">欢迎使用 Offer派!👋</div>
|
||||
<div class="ai-chat__welcome-text">从找岗位到投递跟进,我会陪你一步步拿下理想Offer。</div>
|
||||
</div>
|
||||
|
||||
<!-- 动态消息列表 -->
|
||||
@@ -47,8 +39,33 @@
|
||||
<div class="ai-chat__msg-bubble" v-html="formatContent(msg.content)"></div>
|
||||
</div>
|
||||
|
||||
<!-- AI 正在思考中加载指示器 -->
|
||||
<AiThinkingIndicator v-if="aiLoading" text="AI正在思考中" />
|
||||
<!-- AI 等待中 3 点动效 -->
|
||||
<div v-if="aiLoading" class="ai-chat__msg ai-chat__msg--ai">
|
||||
<div class="ai-chat__typing-indicator">
|
||||
<span class="ai-chat__typing-dot"></span>
|
||||
<span class="ai-chat__typing-dot"></span>
|
||||
<span class="ai-chat__typing-dot"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 话题推荐模块(固定在输入框上方) -->
|
||||
<div class="ai-chat__topics">
|
||||
<div class="ai-chat__topics-header">
|
||||
<svg class="ai-chat__topics-icon" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path d="M8 1L10 6L15 6.5L11.5 10L12.5 15L8 12.5L3.5 15L4.5 10L1 6.5L6 6L8 1Z" fill="#52CAD1"/>
|
||||
</svg>
|
||||
<span class="ai-chat__topics-title">话题推荐</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="(topic, i) in topicList"
|
||||
:key="i"
|
||||
class="ai-chat__topic-btn"
|
||||
@click="sendQuickQuestion(topic)"
|
||||
>
|
||||
{{ topic }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部输入框 -->
|
||||
@@ -56,17 +73,17 @@
|
||||
<input
|
||||
v-model="inputText"
|
||||
class="ai-chat__input"
|
||||
placeholder="搜索职位、公司或关键词..."
|
||||
placeholder="请输入你想问的问题…"
|
||||
@keyup.enter="sendMessage"
|
||||
:disabled="aiLoading"
|
||||
/>
|
||||
<button class="ai-chat__send-btn" @click="sendMessage" :disabled="aiLoading">
|
||||
<span>➤</span>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M4 10L16 4L13 10L16 16L4 10Z" fill="#FFFFFF"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 会员购买弹窗 -->
|
||||
<MemberDialog v-model="showMemberDialog" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -75,8 +92,6 @@ import { ref, computed, watch, nextTick, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import markdownit from 'markdown-it'
|
||||
import MemberDialog from '@/components/MemberDialog.vue'
|
||||
import AiThinkingIndicator from '@/components/tools/AiThinkingIndicator.vue'
|
||||
import { sendNovaChat } from '@/utils/aiRequest'
|
||||
import type { NovaChatHistoryItem } from '@/utils/aiRequest'
|
||||
import { fetchResumeList } from '@/api/resume'
|
||||
@@ -85,9 +100,9 @@ import { fetchResumeList } from '@/api/resume'
|
||||
|
||||
/** markdown-it 实例,用于将 AI 回复的 Markdown 文本渲染为 HTML */
|
||||
const md = markdownit({
|
||||
html: false, // 禁止原始 HTML(防 XSS)
|
||||
breaks: true, // 将换行符转为 <br>
|
||||
linkify: true, // 自动识别链接
|
||||
html: false,
|
||||
breaks: true,
|
||||
linkify: true,
|
||||
})
|
||||
|
||||
// ==================== Props ====================
|
||||
@@ -98,23 +113,16 @@ const props = defineProps<{
|
||||
jobId?: string
|
||||
}>()
|
||||
|
||||
/** 当前路由(用于判断是否在岗位详情页) */
|
||||
/** 当前路由 */
|
||||
const currentRoute = useRoute()
|
||||
const store = useStore()
|
||||
|
||||
|
||||
/** 用户昵称 — 从全局 store 读取 */
|
||||
const nickName = computed(() => store.state.userInfo?.nick || '')
|
||||
|
||||
// ==================== 状态 ====================
|
||||
|
||||
/** 会员购买弹窗的显示状态 */
|
||||
const showMemberDialog = ref(false)
|
||||
|
||||
/** 输入框内容 */
|
||||
const inputText = ref('')
|
||||
|
||||
/** 消息列表容器 DOM 引用(用于滚动控制) */
|
||||
/** 消息列表容器 DOM 引用 */
|
||||
const messagesRef = ref<HTMLElement>()
|
||||
|
||||
/** AI 是否正在请求中 */
|
||||
@@ -123,13 +131,20 @@ const aiLoading = ref(false)
|
||||
/** 默认简历 ID */
|
||||
const defaultResumeId = ref<string>('')
|
||||
|
||||
// ==================== 话题推荐列表 ====================
|
||||
|
||||
/** 固定话题推荐(始终显示) */
|
||||
const topicList = [
|
||||
'我适合投哪些岗位?',
|
||||
'帮我看看我的简历可以怎么优化?',
|
||||
'近期有哪些岗位值得投?',
|
||||
]
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
|
||||
/** 聊天消息类型 */
|
||||
interface ChatMsg {
|
||||
/** 角色:assistant-AI助手 user-用户 */
|
||||
role: 'assistant' | 'user'
|
||||
/** 消息内容 */
|
||||
content: string
|
||||
}
|
||||
|
||||
@@ -137,8 +152,6 @@ interface ChatMsg {
|
||||
|
||||
/** 获取当前用户的缓存 key */
|
||||
function getCacheKey(): string {
|
||||
// 用 sessionStorage 中的登录状态 + 一个简单标识区分用户
|
||||
// 如果有更精确的用户 ID 可以替换
|
||||
const userId = sessionStorage.getItem('userId') || 'anonymous'
|
||||
return `nova_chat_${userId}`
|
||||
}
|
||||
@@ -175,19 +188,6 @@ const messages = ref<ChatMsg[]>([])
|
||||
/** 是否在岗位详情页 */
|
||||
const isJobDetailPage = computed(() => currentRoute.name === 'JobDetail' || currentRoute.path.startsWith('/jobs/'))
|
||||
|
||||
/** 快捷问题列表(仅岗位详情页显示的 3 个用户提问) */
|
||||
const quickQuestions = computed(() => {
|
||||
if (!isJobDetailPage.value) return []
|
||||
return [
|
||||
'告诉我这个工作为什么适合我?',
|
||||
'我想修改这个岗位,怎么优化简历?',
|
||||
'帮我针对这个岗位生成一份面试攻略',
|
||||
]
|
||||
})
|
||||
|
||||
/** 用户提问列表(等同于 quickQuestions) */
|
||||
const userQuestions = computed(() => quickQuestions.value)
|
||||
|
||||
// ==================== 内容格式化 ====================
|
||||
|
||||
/** 将消息内容通过 markdown-it 渲染为 HTML */
|
||||
@@ -208,7 +208,7 @@ function scrollToBottom() {
|
||||
|
||||
// ==================== 加载默认简历 ID ====================
|
||||
|
||||
/** 加载默认简历 ID(用于 Nova 对话接口) */
|
||||
/** 加载默认简历 ID */
|
||||
async function loadDefaultResumeId() {
|
||||
try {
|
||||
const res = await fetchResumeList()
|
||||
@@ -223,13 +223,13 @@ async function loadDefaultResumeId() {
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
|
||||
/** 点击快捷问题,填入输入框并发送 */
|
||||
/** 点击话题推荐,填入输入框并发送 */
|
||||
function sendQuickQuestion(question: string) {
|
||||
inputText.value = question
|
||||
sendMessage()
|
||||
}
|
||||
|
||||
/** 发送消息(回车或点击发送按钮触发) */
|
||||
/** 发送消息 */
|
||||
async function sendMessage() {
|
||||
if (!inputText.value.trim() || aiLoading.value) return
|
||||
|
||||
@@ -256,8 +256,8 @@ async function sendMessage() {
|
||||
jobId: props.jobId || undefined,
|
||||
history,
|
||||
})
|
||||
// 添加 AI 回复消息
|
||||
const aiReply = res?.data?.message || res?.message || '抱歉,我暂时无法回答这个问题。'
|
||||
// 从 res.data.message 取 AI 回复(接口返回结构:{ code, msg, data: { message } })
|
||||
const aiReply = res?.data?.message || '抱歉,我暂时无法回答这个问题。'
|
||||
messages.value.push({ role: 'assistant', content: aiReply })
|
||||
} catch (e) {
|
||||
console.error('Nova 对话请求失败', e)
|
||||
@@ -269,8 +269,6 @@ async function sendMessage() {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 动态宽度:小屏缩放时逐渐缩小 ====================
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
|
||||
onMounted(() => {
|
||||
@@ -285,13 +283,23 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
// 监听 jobId 变化(Jobs 页面点击"问助手"按钮时触发),自动插入 AI 提问消息
|
||||
// 监听 jobId 变化
|
||||
watch(() => props.jobId, (newId, oldId) => {
|
||||
if (newId && newId !== oldId && !isJobDetailPage.value) {
|
||||
// Jobs 页面:以 AI 身份插入提问到对话记录
|
||||
messages.value.push({ role: 'assistant', content: '你想知道关于这个岗位的什么信息?' })
|
||||
saveChatToCache()
|
||||
scrollToBottom()
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== 对外暴露方法 ====================
|
||||
|
||||
/** 触发"问Nova"交互:推送提示消息并聚焦输入框 */
|
||||
function triggerAsk() {
|
||||
messages.value.push({ role: 'assistant', content: '你想知道关于这个岗位的什么信息?' })
|
||||
saveChatToCache()
|
||||
scrollToBottom()
|
||||
}
|
||||
|
||||
defineExpose({ triggerAsk })
|
||||
</script>
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="job-page-header">
|
||||
<div class="bg-f mb16 prl24 ptb16">
|
||||
<div class="job-page-header dflex-start aliite-c">
|
||||
<h2 class="job-page-header__title">发现理想职位</h2>
|
||||
<p class="job-page-header__subtitle">找到最适合你的工作机会</p>
|
||||
</div>
|
||||
<div class="job-page-header__tabs mt20">
|
||||
<div
|
||||
v-for="(tab,index) in tabs"
|
||||
:key="tab.key"
|
||||
class="job-page-header__tab"
|
||||
:class="{ 'job-page-header__tab--active': activeTab === tab.key }"
|
||||
:style="index==0?'padding:0.06rem 0.30rem;':''"
|
||||
@click="handleTabClick(tab.key)"
|
||||
>
|
||||
{{ tab.label }}
|
||||
<div class="job-page-header__tabs ml30 ">
|
||||
<div
|
||||
v-for="(tab,index) in tabs"
|
||||
:key="tab.key"
|
||||
class="job-page-header__tab"
|
||||
:class="{ 'job-page-header__tab--active': activeTab === tab.key }"
|
||||
:style="index==0?'padding:0.06rem 0.20rem;':''"
|
||||
@click="handleTabClick(tab.key)"
|
||||
>
|
||||
{{ tab.label }}
|
||||
<!-- 收藏/已投递 计数圆形徽章 -->
|
||||
<span v-if="tab.count !== undefined" class="job-page-header__tab-badge">{{ tab.count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -46,11 +48,11 @@ const route = useRoute()
|
||||
|
||||
// ==================== 常量数据 ====================
|
||||
|
||||
/** Tab 选项列表 */
|
||||
/** Tab 选项列表,count 用于显示圆形徽章计数 */
|
||||
const tabs = computed(() => [
|
||||
{ key: 'recommend', label: '推荐' },
|
||||
{ key: 'collected', label: `收藏(${props.favoriteCount ?? 0})` },
|
||||
{ key: 'applied', label: `投递(${props.applyCount ?? 0})` },
|
||||
{ key: 'recommend', label: '推荐', count: undefined },
|
||||
{ key: 'collected', label: '收藏', count: props.favoriteCount ?? 0 },
|
||||
{ key: 'applied', label: '已投递', count: props.applyCount ?? 0 },
|
||||
])
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
|
||||
@@ -1,49 +1,10 @@
|
||||
<template>
|
||||
<!-- 岗位专属简历定制弹窗:步骤1居中弹窗,步骤2+右侧抽屉 -->
|
||||
<div v-if="modelValue" class="job-resume-custom-dialog" :class="{ 'job-resume-custom-dialog--drawer': currentStep >= 2 }">
|
||||
<!-- 岗位专属简历定制弹窗:直接显示右侧抽屉 -->
|
||||
<div v-if="modelValue" class="job-resume-custom-dialog job-resume-custom-dialog--drawer">
|
||||
<div class="job-resume-custom-dialog__overlay" @click="handleClose"></div>
|
||||
<!-- ===== 步骤一:居中弹窗 ===== -->
|
||||
<div v-if="currentStep === 1" class="job-resume-custom-dialog__panel">
|
||||
<div class="job-resume-custom-dialog__header">
|
||||
<h2 class="job-resume-custom-dialog__title">10s快速定制岗位专属简历</h2>
|
||||
<button class="job-resume-custom-dialog__close-btn" @click="handleClose" aria-label="关闭">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-resume-custom-dialog__close-icon"><path d="M12 4L4 12M4 4l8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="isLowMatch" class="job-resume-custom-dialog__tip-bar"><span>你与该岗位的匹配度较低,简历可能无法通过机筛。</span></div>
|
||||
<div class="job-resume-custom-dialog__job-card">
|
||||
<div class="job-resume-custom-dialog__job-left">
|
||||
<div class="job-resume-custom-dialog__company-icon">
|
||||
<img v-if="jobInfo.companyLogoUrl" :src="jobInfo.companyLogoUrl" :alt="jobInfo.company" class="job-resume-custom-dialog__company-logo-img" />
|
||||
<svg v-else viewBox="0 0 24 24" fill="none" class="job-resume-custom-dialog__company-svg"><rect x="3" y="7" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.5"/><path d="M7 7V5a2 2 0 012-2h6a2 2 0 012 2v2" stroke="currentColor" stroke-width="1.5"/><path d="M3 13h18" stroke="currentColor" stroke-width="1.5"/></svg>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__job-info">
|
||||
<span class="job-resume-custom-dialog__job-title">{{ jobInfo.title }}</span>
|
||||
<span class="job-resume-custom-dialog__job-sub">{{ jobInfo.location }} · {{ jobInfo.company }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__match-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>
|
||||
<span class="job-resume-custom-dialog__match-label">{{ matchLevelText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__skills-section">
|
||||
<p class="job-resume-custom-dialog__skills-title">缺少{{ missingSkills.length }}项技能</p>
|
||||
<div class="job-resume-custom-dialog__skills-list">
|
||||
<span v-for="skill in missingSkills" :key="skill" class="job-resume-custom-dialog__skill-tag">{{ skill }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-resume-custom-dialog__footer">
|
||||
<button class="job-resume-custom-dialog__primary-btn" @click="goToStep(2)">立即定制简历</button>
|
||||
<span class="job-resume-custom-dialog__skip-link" @click="handleSkip">不优化,直接投递</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== 步骤2+:右侧抽屉 ===== -->
|
||||
<div v-if="currentStep >= 2" class="job-resume-custom-dialog__drawer">
|
||||
<!-- ===== 右侧抽屉 ===== -->
|
||||
<div class="job-resume-custom-dialog__drawer">
|
||||
<!-- 抽屉头部 -->
|
||||
<div class="job-resume-custom-dialog__drawer-header">
|
||||
<button class="job-resume-custom-dialog__close-btn" @click="handleClose" aria-label="关闭">
|
||||
@@ -62,7 +23,7 @@
|
||||
<span class="job-resume-custom-dialog__step-num">2</span><span>定制简历</span>
|
||||
</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>
|
||||
<span class="job-resume-custom-dialog__step-num">3</span><span>简历预览</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 抽屉内容区(可滚动) -->
|
||||
@@ -423,8 +384,8 @@ const fullLoading = ref(false)
|
||||
/** 全屏加载文案 */
|
||||
const fullLoadingText = ref('')
|
||||
|
||||
/** 当前步骤:1-确认入口(居中弹窗) 2-差距分析(右侧抽屉) 3-定制简历 4-预览 */
|
||||
const currentStep = ref(1)
|
||||
/** 当前步骤:2-差距分析(右侧抽屉) 3-定制简历 4-预览 */
|
||||
const currentStep = ref(2)
|
||||
|
||||
/** 缺少的技能列表 */
|
||||
const missingSkills = computed(() => props.jobInfo.missingSkills || [])
|
||||
@@ -602,7 +563,7 @@ function onEditPanelUpdate(data: CustomizeResumeData) {
|
||||
|
||||
/** 关闭弹窗并重置步骤 */
|
||||
function handleClose() {
|
||||
currentStep.value = 1
|
||||
currentStep.value = 2
|
||||
showResumeDropdown.value = false
|
||||
showDownloadMenu.value = false
|
||||
// 重置AI对话和差异对比状态
|
||||
@@ -613,11 +574,7 @@ function handleClose() {
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
/** 不优化,直接投递 */
|
||||
function handleSkip() {
|
||||
handleClose()
|
||||
emit('skip')
|
||||
}
|
||||
|
||||
|
||||
// ==================== 定制简历选项(步骤三) ====================
|
||||
|
||||
|
||||
@@ -41,7 +41,13 @@
|
||||
</span>
|
||||
<!-- 作品集链接显示在个人信息卡片内 -->
|
||||
<span v-if="profile.portfolioUrl" class="profile-page-content__info-contacts">
|
||||
<span class="profile-page-content__contact-item">🔗 <a :href="profile.portfolioUrl" target="_blank" rel="noopener noreferrer" class="profile-page-content__portfolio-link">{{ profile.portfolioUrl }}</a></span>
|
||||
<span class="profile-page-content__contact-item dib">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="resume-detail__contact-icon mr6" width="0.15rem" height="0.15rem" viewBox="0 0 16 16">
|
||||
<path d="M0 0h16v16H0z" fill="none" />
|
||||
<path fill="none" stroke="currentColor" d="M14.5 8A6.5 6.5 0 0 1 8 14.5M14.5 8A6.5 6.5 0 0 0 8 1.5M14.5 8c0 1.657-2.91 3-6.5 3S1.5 9.657 1.5 8m13 0c0-1.657-2.91-3-6.5-3S1.5 6.343 1.5 8M8 14.5A6.5 6.5 0 0 1 1.5 8M8 14.5c1.657 0 3-2.91 3-6.5S9.657 1.5 8 1.5m0 13c-1.657 0-3-2.91-3-6.5s1.343-6.5 3-6.5M1.5 8A6.5 6.5 0 0 1 8 1.5" />
|
||||
</svg>
|
||||
<a :href="profile.portfolioUrl?.startsWith('http') ? profile.portfolioUrl : `http://${profile.portfolioUrl}`" target="_blank" rel="noopener noreferrer" class="profile-page-content__portfolio-link">{{ profile.portfolioUrl }}</a>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
<div class="industry-selector" ref="selectorRef">
|
||||
|
||||
<!-- 触发按钮:显示已选行业名称或默认文字"行业" -->
|
||||
<div class="industry-selector__trigger" :style="triggerStyle" @click="toggleDropdown">
|
||||
<div class="industry-selector__trigger" :style="{ ...triggerStyle, ...(isHovered || visible ? hoverStyle : {}) }" @click="toggleDropdown" @mouseenter="isHovered = true" @mouseleave="isHovered = false">
|
||||
<span class="industry-selector__display" :style="displayStyle" :title="displayText">{{ displayText }}</span>
|
||||
<svg
|
||||
class="industry-selector__arrow"
|
||||
:class="{ 'industry-selector__arrow--open': visible }"
|
||||
:style="(isHovered || visible) && hoverStyle?.color ? { color: hoverStyle.color } : {}"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
>
|
||||
@@ -147,6 +148,8 @@ const props = withDefaults(
|
||||
level?: 1 | 2
|
||||
/** 父组件传入的触发按钮自定义样式,用于在不同场景下覆盖默认外观 */
|
||||
triggerStyle?: Record<string, string>
|
||||
/** 父组件传入的触发按钮 hover 时的自定义样式 */
|
||||
hoverStyle?: Record<string, string>
|
||||
/** 父组件传入的显示文字自定义样式,用于覆盖 max-width 等默认样式 */
|
||||
displayStyle?: Record<string, string>
|
||||
}>(),
|
||||
@@ -170,6 +173,9 @@ const selectorRef = ref<HTMLElement | null>(null)
|
||||
/** 下拉面板是否可见 */
|
||||
const visible = ref(false)
|
||||
|
||||
/** 触发按钮是否处于 hover 状态 */
|
||||
const isHovered = ref(false)
|
||||
|
||||
/** 搜索关键词 */
|
||||
const searchText = ref('')
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
<div class="job-category-selector" ref="selectorRef">
|
||||
|
||||
<!-- 触发按钮:显示已选岗位名称或默认文字"岗位" -->
|
||||
<div class="job-category-selector__trigger" :style="triggerStyle" @click="toggleDropdown">
|
||||
<div class="job-category-selector__trigger" :style="{ ...triggerStyle, ...(isHovered || visible ? hoverStyle : {}) }" @click="toggleDropdown" @mouseenter="isHovered = true" @mouseleave="isHovered = false">
|
||||
<span class="job-category-selector__display" :style="displayStyle" :title="displayText">{{ displayText }}</span>
|
||||
<svg
|
||||
class="job-category-selector__arrow"
|
||||
:class="{ 'job-category-selector__arrow--open': visible }"
|
||||
:style="(isHovered || visible) && hoverStyle?.color ? { color: hoverStyle.color } : {}"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
>
|
||||
@@ -170,6 +171,8 @@ const props = withDefaults(
|
||||
level?: 1 | 2 | 3
|
||||
/** 父组件传入的触发按钮自定义样式,用于在不同场景下覆盖默认外观 */
|
||||
triggerStyle?: Record<string, string>
|
||||
/** 父组件传入的触发按钮 hover 时的自定义样式 */
|
||||
hoverStyle?: Record<string, string>
|
||||
/** 父组件传入的显示文字自定义样式,用于覆盖 max-width 等默认样式 */
|
||||
displayStyle?: Record<string, string>
|
||||
}>(),
|
||||
@@ -193,6 +196,9 @@ const selectorRef = ref<HTMLElement | null>(null)
|
||||
/** 下拉面板是否可见 */
|
||||
const visible = ref(false)
|
||||
|
||||
/** 触发按钮是否处于 hover 状态 */
|
||||
const isHovered = ref(false)
|
||||
|
||||
/** 搜索关键词 */
|
||||
const searchText = ref('')
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
<div class="region-selector" ref="selectorRef">
|
||||
|
||||
<!-- 触发按钮:显示已选地区名称或默认文字"城市" -->
|
||||
<div class="region-selector__trigger" :style="triggerStyle" @click="toggleDropdown">
|
||||
<div class="region-selector__trigger" :style="{ ...triggerStyle, ...(isHovered || visible ? hoverStyle : {}) }" @click="toggleDropdown" @mouseenter="isHovered = true" @mouseleave="isHovered = false">
|
||||
<span class="region-selector__display" :style="displayStyle" :title="displayText">{{ displayText }}</span>
|
||||
<svg
|
||||
class="region-selector__arrow"
|
||||
:class="{ 'region-selector__arrow--open': visible }"
|
||||
:style="(isHovered || visible) && hoverStyle?.color ? { color: hoverStyle.color } : {}"
|
||||
viewBox="0 0 12 12"
|
||||
fill="none"
|
||||
>
|
||||
@@ -157,6 +158,8 @@ const props = withDefaults(
|
||||
maxSelect?: number
|
||||
/** 父组件传入的触发按钮自定义样式,用于在不同场景下覆盖默认外观 */
|
||||
triggerStyle?: Record<string, string>
|
||||
/** 父组件传入的触发按钮 hover 时的自定义样式 */
|
||||
hoverStyle?: Record<string, string>
|
||||
/** 父组件传入的显示文字自定义样式,用于覆盖 max-width 等默认样式 */
|
||||
displayStyle?: Record<string, string>
|
||||
}>(),
|
||||
@@ -179,6 +182,9 @@ const selectorRef = ref<HTMLElement | null>(null)
|
||||
/** 下拉面板是否可见 */
|
||||
const visible = ref(false)
|
||||
|
||||
/** 触发按钮是否处于 hover 状态 */
|
||||
const isHovered = ref(false)
|
||||
|
||||
/** 搜索关键词 */
|
||||
const searchText = ref('')
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ const remAdaptPlugin: Plugin = {
|
||||
docEl.style.height = ''
|
||||
docEl.style.overflow = ''
|
||||
docEl.style.setProperty('--app-height', '100vh')
|
||||
docEl.style.setProperty('--chat-width', '4rem')
|
||||
docEl.style.setProperty('--chat-width', '3.6rem')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,21 @@ export function formatEmploymentType(type: number | undefined | null): string {
|
||||
return JOB_TYPE_MAP[type] ?? ''
|
||||
}
|
||||
|
||||
/** 学历要求映射:数字 → 中文标签(0=不限 1=大专 2=本科 3=硕士 4=博士) */
|
||||
export const EDUCATION_MAP: Record<number, string> = {
|
||||
0: '不限',
|
||||
1: '大专',
|
||||
2: '本科',
|
||||
3: '硕士',
|
||||
4: '博士',
|
||||
}
|
||||
|
||||
/** 根据 education 值获取中文标签,未匹配返回空字符串 */
|
||||
export function formatEducation(edu: number | undefined | null): string {
|
||||
if (edu === undefined || edu === null) return ''
|
||||
return EDUCATION_MAP[edu] ?? ''
|
||||
}
|
||||
|
||||
/** 职位列表页缓存数据(从详情页返回时恢复用) */
|
||||
export interface JobListCache {
|
||||
/** 缓存的职位列表 */
|
||||
|
||||
@@ -105,9 +105,10 @@ export interface NovaChatResponse {
|
||||
* Nova AI 对话
|
||||
* POST /nova-chat/chat
|
||||
* @param params 对话参数
|
||||
* @returns 完整响应体 { code, msg, data: { message } }
|
||||
*/
|
||||
export function sendNovaChat(params: NovaChatParams) {
|
||||
return aiService.post<NovaChatResponse>('/nova-chat/chat', params, {
|
||||
return aiService.post<AiResult<NovaChatResponse>>('/nova-chat/chat', params, {
|
||||
headers: {
|
||||
Token: DEFAULT_TOKEN,
|
||||
},
|
||||
|
||||
+109
-291
@@ -4,275 +4,128 @@
|
||||
<div class="job-detail__content">
|
||||
<!-- 页面标题 + Tab 切换 -->
|
||||
<JobPageHeader :activeTab="''" />
|
||||
<div class="bg-white p20 border-ra20">
|
||||
<div class="bg-main p20 border-ra20">
|
||||
<!-- 顶部操作栏 -->
|
||||
<div class="job-detail__toolbar">
|
||||
<button class="job-detail__close-btn" @click="goBack" aria-label="关闭">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-detail__close-icon">
|
||||
<path d="M12 4L4 12M4 4l8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="job-detail__toolbar-right">
|
||||
<button class="job-detail__tool-btn" aria-label="编辑" @click="openDislikeDialog">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-detail__tool-icon">
|
||||
<path d="M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="jobs-page__action-icon-btn" :class="{ 'job-detail__tool-btn--liked': job.isFavorite }" aria-label="收藏" @click="toggleFavorite">
|
||||
<svg viewBox="0 0 16 16" :fill="job.isFavorite ? 'currentColor' : 'none'" class="job-detail__tool-icon">
|
||||
<path d="M8 13.7l-1.1-1C3.6 9.8 1.5 7.9 1.5 5.7 1.5 3.9 2.9 2.5 4.7 2.5c1 0 2 .5 2.6 1.2h1.4c.6-.7 1.6-1.2 2.6-1.2 1.8 0 3.2 1.4 3.2 3.2 0 2.2-2.1 4.1-5.4 6.9L8 13.7z" stroke="currentColor" stroke-width="1"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button class="job-detail__apply-btn" @click="handleApply">去投递</button>
|
||||
<!-- 主内容卡片 -->
|
||||
<div class="job-detail__main-card">
|
||||
<!-- 返回按钮 -->
|
||||
<div class="job-detail__back-row">
|
||||
<button class="job-detail__back-btn" @click="goBack">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-detail__back-icon">
|
||||
<path d="M10 12L6 8l4-4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span>返回</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域(可滚动) -->
|
||||
<div class="job-detail__body scrollbar-thin">
|
||||
<!-- 岗位基本信息区 -->
|
||||
<div class="job-detail__header-section">
|
||||
<!-- 左侧:岗位信息 -->
|
||||
<div class="job-detail__basic-info">
|
||||
<!-- 岗位标题 + 收藏 -->
|
||||
<div class="job-detail__title-row">
|
||||
<h1 class="job-detail__job-title">{{ job.title }}</h1>
|
||||
<button class="job-detail__fav-btn" :class="{ 'job-detail__fav-btn--active': job.isFavorite }" aria-label="收藏" @click="toggleFavorite">
|
||||
<svg viewBox="0 0 16 16" class="job-detail__fav-icon">
|
||||
<path d="M8 13.7l-1.1-1C3.6 9.8 1.5 7.9 1.5 5.7 1.5 3.9 2.9 2.5 4.7 2.5c1 0 2 .5 2.6 1.2h1.4c.6-.7 1.6-1.2 2.6-1.2 1.8 0 3.2 1.4 3.2 3.2 0 2.2-2.1 4.1-5.4 6.9L8 13.7z" stroke="currentColor" stroke-width="1" fill="none"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 岗位 meta 信息 -->
|
||||
<div class="job-detail__meta-row">
|
||||
<span>{{ job.location }}</span>
|
||||
<span class="job-detail__meta-dot">·</span>
|
||||
<span>{{ job.experience }}</span>
|
||||
<span class="job-detail__meta-dot">·</span>
|
||||
<span>{{ job.education }}</span>
|
||||
<span class="job-detail__meta-dot">·</span>
|
||||
<span>{{ job.salary }}</span>
|
||||
</div>
|
||||
<!-- 操作按钮 -->
|
||||
<div class="job-detail__action-btns">
|
||||
<button class="job-detail__nova-btn" @click="handleAskNova">问Nova</button>
|
||||
<button class="job-detail__apply-btn" @click="handleApply">去投递</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:匹配度卡片 -->
|
||||
<div class="job-detail__match-card">
|
||||
<div class="job-detail__match-top">
|
||||
<span class="job-detail__match-score">{{ job.matchScore }}%</span>
|
||||
<span class="job-detail__match-label">高匹配度</span>
|
||||
</div>
|
||||
<div class="job-detail__match-list">
|
||||
<div class="job-detail__match-row" v-for="m in matchItems" :key="m.label">
|
||||
<span class="job-detail__match-row-label">{{ m.label }}</span>
|
||||
<span class="job-detail__match-row-score">{{ m.score }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域(可滚动) -->
|
||||
<div class="job-detail__body">
|
||||
<!-- 导航 Tab:岗位详情 / 公司概况 -->
|
||||
<div class="job-detail__nav-tabs">
|
||||
<div
|
||||
class="job-detail__nav-tab"
|
||||
:class="{ 'job-detail__nav-tab--active': activeSection === 'job' }"
|
||||
@click="activeSection = 'job'"
|
||||
>岗位详情</div>
|
||||
<div
|
||||
class="job-detail__nav-tab"
|
||||
:class="{ 'job-detail__nav-tab--active': activeSection === 'company' }"
|
||||
@click="scrollToCompany"
|
||||
>公司概况</div>
|
||||
<div class="job-detail__nav-tab-right">
|
||||
<span class="job-detail__link-btn" @click="handleFeedback">问题反馈</span>
|
||||
<span class="job-detail__link-btn" @click="handleReport">原链接</span>
|
||||
</div>
|
||||
<!-- 优化简历提示条 -->
|
||||
<div class="job-detail__optimize-bar">
|
||||
<span class="job-detail__optimize-text">优化简历大幅提升面试成功率</span>
|
||||
<button class="job-detail__optimize-btn" @click="handleGenerateResume">生成岗位专属简历</button>
|
||||
</div>
|
||||
|
||||
<!-- 岗位职责 -->
|
||||
<div class="job-detail__section">
|
||||
<h3 class="job-detail__section-title">岗位职责</h3>
|
||||
<div class="job-detail__section-list">
|
||||
<div class="job-detail__section-item" v-for="(item, i) in job.responsibilities" :key="i">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 岗位详情内容 -->
|
||||
<template v-if="activeSection === 'job'">
|
||||
<!-- 公司 & 职位头部 -->
|
||||
<div class="job-detail__card">
|
||||
<div class="job-detail__card-top">
|
||||
<div class="job-detail__card-left">
|
||||
<div class="job-detail__company-row">
|
||||
<div class="job-detail__company-icon">
|
||||
<img v-if="job.companyLogoUrl" :src="job.companyLogoUrl" :alt="job.company" class="job-detail__company-logo-img" />
|
||||
<svg v-else viewBox="0 0 24 24" fill="none" class="job-detail__company-svg">
|
||||
<rect x="3" y="7" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M7 7V5a2 2 0 012-2h6a2 2 0 012 2v2" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M3 13h18" stroke="currentColor" stroke-width="1.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="job-detail__company-name">{{ job.company }}</span>
|
||||
</div>
|
||||
<h3 class="job-detail__job-title">{{ job.title }}</h3>
|
||||
<div class="job-detail__job-meta">
|
||||
<span class="job-detail__meta-item">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-detail__meta-icon">
|
||||
<circle cx="8" cy="6.5" r="2.5" stroke="currentColor" stroke-width="1.2"/>
|
||||
<path d="M8 14s-5-4-5-7.5a5 5 0 0110 0C13 10 8 14 8 14z" stroke="currentColor" stroke-width="1.2"/>
|
||||
</svg>
|
||||
{{ job.location }}
|
||||
</span>
|
||||
<span class="job-detail__meta-item">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-detail__meta-icon">
|
||||
<rect x="2" y="3" width="12" height="11" rx="1.5" stroke="currentColor" stroke-width="1.2"/>
|
||||
<path d="M2 6.5h12" stroke="currentColor" stroke-width="1.2"/>
|
||||
</svg>
|
||||
{{ job.experience }}
|
||||
</span>
|
||||
<span class="job-detail__meta-item">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-detail__meta-icon">
|
||||
<rect x="2" y="2" width="12" height="12" rx="2" stroke="currentColor" stroke-width="1.2"/>
|
||||
<path d="M5 8h6M8 5v6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
{{ job.type }}
|
||||
</span>
|
||||
<span v-if="job.education" class="job-detail__meta-item">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-detail__meta-icon">
|
||||
<path d="M8 2L1 6l7 4 7-4-7-4z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
<path d="M3 7.5v4l5 3 5-3v-4" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
{{ job.education }}
|
||||
</span>
|
||||
<span v-if="job.salary" class="job-detail__meta-item">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="job-detail__meta-icon">
|
||||
<path d="M8 1v14M4 4h8M3 8h10M5 12h6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
{{ job.salary }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 匹配度环 -->
|
||||
<div class="job-detail__match-area">
|
||||
<div class="job-detail__match-ring">
|
||||
<svg viewBox="0 0 80 80" class="job-detail__ring-svg">
|
||||
<circle cx="40" cy="40" r="34" stroke-width="5" stroke="#E8E8E8" fill="none" opacity="0.3"/>
|
||||
<circle cx="40" cy="40" r="34" stroke-width="5" fill="none"
|
||||
stroke="#4FC2C9"
|
||||
stroke-linecap="round"
|
||||
:stroke-dasharray="2 * Math.PI * 34"
|
||||
:stroke-dashoffset="2 * Math.PI * 34 * (1 - job.matchScore / 100)"
|
||||
transform="rotate(-90 40 40)"
|
||||
/>
|
||||
</svg>
|
||||
<div class="job-detail__match-score">{{ job.matchScore }}%</div>
|
||||
</div>
|
||||
<div class="job-detail__match-label">岗位匹配值</div>
|
||||
<div class="job-detail__match-details">
|
||||
<div class="job-detail__match-item" v-for="m in matchItems" :key="m.label">
|
||||
<div class="job-detail__match-mini-ring">
|
||||
<svg viewBox="0 0 40 40" class="job-detail__mini-ring-svg">
|
||||
<circle cx="20" cy="20" r="16" stroke-width="3" stroke="#E8E8E8" fill="none" opacity="0.3"/>
|
||||
<circle cx="20" cy="20" r="16" stroke-width="3" fill="none"
|
||||
stroke="#BFBFBF"
|
||||
stroke-linecap="round"
|
||||
:stroke-dasharray="2 * Math.PI * 16"
|
||||
:stroke-dashoffset="2 * Math.PI * 16 * (1 - m.score / 100)"
|
||||
transform="rotate(-90 20 20)"
|
||||
/>
|
||||
</svg>
|
||||
<span class="job-detail__mini-score">{{ m.score }}%</span>
|
||||
</div>
|
||||
<span class="job-detail__match-item-label">{{ m.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 优化简历提示 -->
|
||||
<div class="job-detail__optimize-bar">
|
||||
<span>优化简历大幅提升面试成功率!</span>
|
||||
<button class="job-detail__optimize-btn" @click="handleGenerateResume">生成岗位专属简历</button>
|
||||
</div>
|
||||
|
||||
<!-- 岗位描述 -->
|
||||
<div class="job-detail__card">
|
||||
<p class="job-detail__desc-text">{{ job.companyInfo.summary }}</p>
|
||||
<div class="job-detail__tag-list">
|
||||
<span v-for="tag in job.companyInfo.tags" :key="tag" class="job-detail__tag">{{ tag }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 岗位职责 -->
|
||||
<div class="job-detail__card">
|
||||
<h3 class="job-detail__section-title">岗位职责</h3>
|
||||
<ol class="job-detail__list">
|
||||
<li v-for="(item, i) in job.responsibilities" :key="i">{{ item }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- 任职要求 -->
|
||||
<div class="job-detail__card">
|
||||
<div class="job-detail__section-header">
|
||||
<h3 class="job-detail__section-title">任职要求</h3>
|
||||
<span class="job-detail__skill-hint">
|
||||
|
||||
<!-- 深色为你拥有的技能-->
|
||||
</span>
|
||||
</div>
|
||||
<div class="job-detail__skill-tags">
|
||||
|
||||
<!-- 先不加点击选中 @click="skill.matched = !skill.matched"-->
|
||||
<!-- 任职要求 -->
|
||||
<div class="job-detail__section">
|
||||
<h3 class="job-detail__section-title">任职要求</h3>
|
||||
<!-- 技能标签 -->
|
||||
<div class="job-detail__skill-tags">
|
||||
<span
|
||||
v-for="skill in job.requiredSkills"
|
||||
:key="skill.name"
|
||||
class="job-detail__skill-tag cursor-po"
|
||||
class="job-detail__skill-tag"
|
||||
:class="{ 'job-detail__skill-tag--matched': skill.matched }"
|
||||
>{{ skill.name }}</span>
|
||||
</div>
|
||||
<!-- 要求列表 -->
|
||||
<div class="job-detail__section-list">
|
||||
<div class="job-detail__section-item" v-for="(item, i) in job.requirements" :key="i">{{ item }}</div>
|
||||
</div>
|
||||
<!-- 加分项 -->
|
||||
<div v-if="job.bonus" class="job-detail__bonus-item">加分项:{{ job.bonus }}</div>
|
||||
</div>
|
||||
|
||||
>
|
||||
{{ skill.name }}
|
||||
<svg v-if="skill.matched" viewBox="0 0 12 12" fill="none" class="job-detail__skill-close">
|
||||
<path d="M9 3L3 9M3 3l6 6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
<!-- 公司概况 -->
|
||||
<div class="job-detail__section">
|
||||
<h3 class="job-detail__section-title">公司概况</h3>
|
||||
<div class="job-detail__company-block">
|
||||
<!-- 公司 logo + 名称 + 标签 -->
|
||||
<div class="job-detail__company-head">
|
||||
<div class="job-detail__company-logo">
|
||||
<img v-if="job.companyLogoUrl" :src="job.companyLogoUrl" :alt="job.company" class="job-detail__company-logo-img" />
|
||||
<svg v-else viewBox="0 0 32 32" fill="none" class="job-detail__company-svg">
|
||||
<rect x="4" y="8" width="24" height="18" rx="3" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M10 8V6a3 3 0 013-3h6a3 3 0 013 3v2" stroke="currentColor" stroke-width="1.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
<ol class="job-detail__list">
|
||||
<li v-for="(item, i) in job.requirements" :key="i">{{ item }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<!-- 加分项 -->
|
||||
<div class="job-detail__card">
|
||||
<h3 class="job-detail__section-title">加分项</h3>
|
||||
<p class="job-detail__desc-text">{{ job.bonus }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 公司概况 -->
|
||||
<div ref="companySectionRef" class="job-detail__card">
|
||||
<h3 class="job-detail__section-title">公司概况</h3>
|
||||
<div class="job-detail__company-info">
|
||||
<div class="job-detail__company-info-left">
|
||||
<div class="job-detail__company-header">
|
||||
<div class="job-detail__company-logo">
|
||||
<img v-if="job.companyLogoUrl" :src="job.companyLogoUrl" :alt="job.company" class="job-detail__company-logo-img" />
|
||||
<svg v-else viewBox="0 0 24 24" fill="none" class="job-detail__company-svg">
|
||||
<rect x="3" y="7" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M7 7V5a2 2 0 012-2h6a2 2 0 012 2v2" stroke="currentColor" stroke-width="1.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="job-detail__company-info-name">{{ job.company }}</span>
|
||||
</div>
|
||||
<p class="job-detail__company-desc">{{ job.companyInfo.description }}</p>
|
||||
</div>
|
||||
<div class="job-detail__company-info-right pr50">
|
||||
<div class="job-detail__company-meta-item">
|
||||
<span class="job-detail__meta-label">成立时间:</span>
|
||||
<span>{{ job.companyInfo.founded }}</span>
|
||||
</div>
|
||||
<div class="job-detail__company-meta-item">
|
||||
<span class="job-detail__meta-label">公司地址:</span>
|
||||
<span>{{ job.companyInfo.address }}</span>
|
||||
</div>
|
||||
<div class="job-detail__company-meta-item">
|
||||
<span class="job-detail__meta-label">企业规模:</span>
|
||||
<span>{{ job.companyInfo.size }}</span>
|
||||
</div>
|
||||
<div class="job-detail__company-meta-item">
|
||||
<span class="job-detail__meta-label">官网:</span>
|
||||
<a :href="job.companyInfo.website" target="_blank" class="job-detail__company-link">{{ job.companyInfo.website }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-detail__company-name-info">
|
||||
<span class="job-detail__company-name">{{ job.company }}</span>
|
||||
<span class="job-detail__company-sub">{{ job.companyInfo.industryName }}{{ job.companyInfo.size ? ' · ' + job.companyInfo.size : '' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 融资 -->
|
||||
<div class="job-detail__card">
|
||||
<h3 class="job-detail__section-title">融资</h3>
|
||||
<p class="job-detail__desc-text">
|
||||
<span class="job-detail__funding-label">当前融资阶段:</span>{{ job.companyInfo.fundingStage }}
|
||||
<span class="job-detail__funding-label" style="margin-left: 0.3rem;">最新估值:</span>{{ job.companyInfo.valuation }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 最新动态 -->
|
||||
<div class="job-detail__card" v-if="job.companyInfo.news.length">
|
||||
<h3 class="job-detail__section-title">最新动态</h3>
|
||||
<div class="job-detail__news-list">
|
||||
<div v-for="(news, i) in job.companyInfo.news" :key="i" class="job-detail__news-item">
|
||||
<p class="job-detail__news-desc">{{ news }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 公司概况 Tab 已移除,点击直接滚动到岗位详情中的公司概况部分 -->
|
||||
<!-- 公司描述 -->
|
||||
<p class="job-detail__company-desc">{{ job.companyInfo.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<AiChat :job-id="jobId" />
|
||||
|
||||
<!-- 职位不感兴趣反馈弹窗 -->
|
||||
<JobDislikeDialog ref="dislikeDialogRef" v-model="showDislikeDialog" :job-id="jobId" />
|
||||
|
||||
<!-- 职位问题反馈弹窗 -->
|
||||
<JobFeedbackDialog ref="feedbackDialogRef" v-model="showFeedbackDialog" :job-id="jobId" />
|
||||
<AiChat ref="aiChatRef" :job-id="jobId" />
|
||||
|
||||
<!-- 岗位专属简历定制弹窗 -->
|
||||
<JobResumeCustomDialog v-model="showResumeCustomDialog" :job-info="resumeCustomJobInfo" :job-id="jobId" @skip="handleSkipToApply" />
|
||||
@@ -287,15 +140,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, nextTick, onMounted } from 'vue'
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { formatEmploymentType } from '@/stores/index'
|
||||
import SideNav from '@/components/SideNav.vue'
|
||||
import AiChat from '@/components/AiChat.vue'
|
||||
import JobPageHeader from '@/components/JobPageHeader.vue'
|
||||
import JobDislikeDialog from '@/components/JobDislikeDialog.vue'
|
||||
import JobFeedbackDialog from '@/components/JobFeedbackDialog.vue'
|
||||
import JobResumeCustomDialog from '@/components/JobResumeCustomDialog.vue'
|
||||
import MemberAccessDialog from '@/components/MemberAccessDialog.vue'
|
||||
import FullscreenLoading from '@/components/FullscreenLoading.vue'
|
||||
@@ -349,23 +200,11 @@ function splitNumberedText(text: string | undefined): string[] {
|
||||
|
||||
// ==================== 页面状态 ====================
|
||||
|
||||
/** 当前激活的内容区 Tab:job-岗位详情 / company-公司概况 */
|
||||
const activeSection = ref<'job' | 'company'>('job')
|
||||
|
||||
/** 公司概况卡片 ref,用于滚动定位 */
|
||||
const companySectionRef = ref<HTMLElement | null>(null)
|
||||
/** 当前激活的内容区 Tab(保留兼容性但不再使用) */
|
||||
|
||||
/** 是否正在加载 */
|
||||
const loading = ref(false)
|
||||
|
||||
/** 点击公司概况 tab 时,确保显示岗位详情内容并滚动到公司概况卡片 */
|
||||
function scrollToCompany() {
|
||||
activeSection.value = 'job'
|
||||
nextTick(() => {
|
||||
companySectionRef.value?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
||||
})
|
||||
}
|
||||
|
||||
// ==================== 岗位详情数据 ====================
|
||||
|
||||
/** 匹配度维度列表(岗位匹配环下方的子维度) */
|
||||
@@ -525,24 +364,8 @@ onMounted(() => {
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
|
||||
/** 不感兴趣弹窗状态 */
|
||||
const showDislikeDialog = ref(false)
|
||||
const dislikeDialogRef = ref<InstanceType<typeof JobDislikeDialog> | null>(null)
|
||||
|
||||
/** 问题反馈弹窗状态 */
|
||||
const showFeedbackDialog = ref(false)
|
||||
const feedbackDialogRef = ref<InstanceType<typeof JobFeedbackDialog> | null>(null)
|
||||
|
||||
/** 打开不感兴趣弹窗 */
|
||||
function openDislikeDialog() {
|
||||
dislikeDialogRef.value?.resetForm()
|
||||
showDislikeDialog.value = true
|
||||
}
|
||||
|
||||
/** 返回职位列表页 */
|
||||
function goBack() {
|
||||
// 使用 back() 回退,保证 Jobs 页能正确恢复缓存
|
||||
// 如果没有历史记录(直接访问详情页),则 push 到列表页
|
||||
if (window.history.length > 1) {
|
||||
router.back()
|
||||
} else {
|
||||
@@ -550,17 +373,12 @@ function goBack() {
|
||||
}
|
||||
}
|
||||
|
||||
/** 问题反馈 */
|
||||
function handleFeedback() {
|
||||
feedbackDialogRef.value?.resetForm()
|
||||
showFeedbackDialog.value = true
|
||||
}
|
||||
/** AiChat 组件引用 */
|
||||
const aiChatRef = ref<InstanceType<typeof AiChat> | null>(null)
|
||||
|
||||
/** 跳转到原链接 */
|
||||
function handleReport() {
|
||||
if (job.sourceUrl) {
|
||||
window.open(job.sourceUrl, '_blank')
|
||||
}
|
||||
/** 问 Nova — 触发 AI 聊天,推送提示消息 */
|
||||
function handleAskNova() {
|
||||
aiChatRef.value?.triggerAsk()
|
||||
}
|
||||
|
||||
// ==================== 岗位专属简历定制弹窗 ====================
|
||||
|
||||
+40
-24
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="jobs-page dflex">
|
||||
<div class="jobs-page">
|
||||
<SideNav />
|
||||
<div class="jobs-page__content">
|
||||
|
||||
@@ -16,25 +16,7 @@
|
||||
|
||||
<!-- 页面标题 + Tab 切换 -->
|
||||
<JobPageHeader v-model:activeTab="activeTab" :favoriteCount="favoriteTotal" :applyCount="applyTotal" />
|
||||
<!-- 收藏统计栏 -->
|
||||
<div v-if="activeTab === 'collected'" class="jobs-page__filters-bar">
|
||||
<div class="jobs-page__fav-stats">
|
||||
<span class="jobs-page__fav-stats-item">有效({{ favoriteValidCount }})</span>
|
||||
<span class="jobs-page__fav-stats-item">失效({{ favoriteInvalidCount }})</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 投递状态筛选栏 -->
|
||||
<div v-if="activeTab === 'applied'" class="jobs-page__filters-bar">
|
||||
<div class="jobs-page__fav-stats">
|
||||
<span
|
||||
v-for="tab in applyStatusTabs"
|
||||
:key="tab.status"
|
||||
class="jobs-page__fav-stats-item"
|
||||
:class="{ 'jobs-page__fav-stats-item--active': applyStatusFilter === tab.status }"
|
||||
@click="switchApplyStatus(tab.status)"
|
||||
>{{ tab.label }}({{ tab.count }})</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选条件 -->
|
||||
<div v-if="isRecommendTab" class="jobs-page__filters-bar">
|
||||
<div class="jobs-page__filters">
|
||||
@@ -47,6 +29,9 @@
|
||||
:regionCodes="selectedRegionCodes"
|
||||
:level="2"
|
||||
:maxSelect="3"
|
||||
:triggerStyle="selectorTriggerStyle"
|
||||
:hoverStyle="selectorHoverStyle"
|
||||
:displayStyle="selectorDisplayStyle"
|
||||
@update:regionCodes="onRegionChange"
|
||||
/>
|
||||
<!-- 岗位筛选:使用岗位选择组件 -->
|
||||
@@ -56,6 +41,9 @@
|
||||
:maxSelect="3"
|
||||
:level="3"
|
||||
:allowParentSelect="true"
|
||||
:triggerStyle="selectorTriggerStyle"
|
||||
:hoverStyle="selectorHoverStyle"
|
||||
:displayStyle="selectorDisplayStyle"
|
||||
@update:categoryIds="onCategoryChange"
|
||||
/>
|
||||
<!-- 行业筛选:使用行业选择组件 -->
|
||||
@@ -65,6 +53,9 @@
|
||||
:maxSelect="3"
|
||||
:level="2"
|
||||
:allowParentSelect="true"
|
||||
:triggerStyle="selectorTriggerStyle"
|
||||
:hoverStyle="selectorHoverStyle"
|
||||
:displayStyle="selectorDisplayStyle"
|
||||
@update:industryIds="onIndustryChange"
|
||||
/>
|
||||
<!-- 其他筛选条件 -->
|
||||
@@ -75,7 +66,7 @@
|
||||
>
|
||||
<span>{{ filter.selected || filter.label }}</span>
|
||||
<svg class="jobs-page__filter-arrow-icon" viewBox="0 0 12 12" fill="none">
|
||||
<path d="M3 4.5L6 7.5L9 4.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3 4.5L6 7.5L9 4.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<!-- 工作类型下拉菜单 -->
|
||||
<div
|
||||
@@ -112,7 +103,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 职位列表 -->
|
||||
<div ref="jobListRef" class="jobs-page__list pr5" :style="restoring ? { visibility: 'hidden' } : {}" @scroll="onListScroll">
|
||||
<div ref="jobListRef" class="jobs-page__list pr5 scrollbar-thin" :style="restoring ? { visibility: 'hidden' } : {}" @scroll="onListScroll">
|
||||
<div
|
||||
v-for="(job, index) in jobList"
|
||||
:key="index"
|
||||
@@ -151,7 +142,7 @@
|
||||
</svg>
|
||||
{{ job.regionName }}
|
||||
</span>
|
||||
<span class="jobs-page__meta-edu">本科</span>
|
||||
<span v-if="formatEducation(job.education)" class="jobs-page__meta-edu">{{ formatEducation(job.education) }}</span>
|
||||
<span v-if="formatEmploymentType(job.recruitCategory)" class="jobs-page__meta-recruit">{{ formatEmploymentType(job.recruitCategory) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -267,7 +258,7 @@
|
||||
import { ref, watch, onMounted, onBeforeUnmount, nextTick, computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import { JOB_TYPE_OPTIONS, formatEmploymentType } from '@/stores/index'
|
||||
import { JOB_TYPE_OPTIONS, formatEmploymentType, formatEducation } from '@/stores/index'
|
||||
import SideNav from '@/components/SideNav.vue'
|
||||
import AiChat from '@/components/AiChat.vue'
|
||||
import JobPageHeader from '@/components/JobPageHeader.vue'
|
||||
@@ -340,6 +331,31 @@ function askAssistant(job: JobItem) {
|
||||
currentAskJobId.value = job.id
|
||||
}
|
||||
|
||||
/** 选择器触发按钮的自定义样式,适配弹窗内布局(参考 ProfileEditDrawer 中 regionTriggerStyle) */
|
||||
const selectorTriggerStyle: Record<string, string> = {
|
||||
width: '100%',
|
||||
'box-sizing': 'border-box',
|
||||
padding: '0.08rem 0.12rem',
|
||||
'font-size': '0.13rem',
|
||||
color: '#666',
|
||||
background: '#fff',
|
||||
border: '1px solid transparent',
|
||||
'border-radius': '0.08rem',
|
||||
'max-width': 'none',
|
||||
'justify-content': 'space-between',
|
||||
}
|
||||
|
||||
/** 选择器显示文字的自定义样式,覆盖默认 max-width: 1.6rem */
|
||||
const selectorDisplayStyle: Record<string, string> = {
|
||||
'max-width': 'none',
|
||||
}
|
||||
|
||||
/** 选择器触发按钮 hover 时的样式 */
|
||||
const selectorHoverStyle: Record<string, string> = {
|
||||
background: '#12C7BE',
|
||||
color: '#fff',
|
||||
}
|
||||
|
||||
// ==================== 收藏统计 ====================
|
||||
|
||||
/** 收藏总数(用于 Tab 标签显示) */
|
||||
|
||||
@@ -100,7 +100,10 @@
|
||||
<button
|
||||
class="mentor-page__detail-btn"
|
||||
@click="goToDetail(mentor)"
|
||||
>查看详情 →</button>
|
||||
><span>查看详情</span> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.51612 4.07575C7.38279 3.94242 7.31879 3.78686 7.32412 3.60908C7.32945 3.43131 7.39901 3.27575 7.53278 3.14241C7.66612 3.02019 7.82167 2.95619 7.99946 2.95041C8.17723 2.94464 8.33278 3.00864 8.46612 3.14241L12.8661 7.54241C12.9328 7.60908 12.9801 7.6813 13.0081 7.75908C13.0361 7.83686 13.0497 7.92019 13.0488 8.00908C13.0479 8.09797 13.0339 8.18131 13.0068 8.25908C12.9797 8.33686 12.9326 8.40908 12.8654 8.47575L8.46545 12.8757C8.34323 12.998 8.19034 13.0591 8.00679 13.0591C7.82323 13.0591 7.66501 12.998 7.53212 12.8757C7.39879 12.7424 7.33212 12.584 7.33212 12.4004C7.33212 12.2169 7.39879 12.0586 7.53212 11.9257L10.7828 8.67575H3.33279C3.1439 8.67575 2.98546 8.61175 2.85745 8.48375C2.72945 8.35575 2.66567 8.19753 2.66612 8.00908C2.66656 7.82064 2.73056 7.6622 2.85812 7.53375C2.98568 7.40531 3.1439 7.34153 3.33279 7.34242L10.7828 7.34242L7.51612 4.07575Z" fill="#52CAD1"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -426,12 +426,14 @@ async function handleSaveEdit(data: Record<string, any>) {
|
||||
mobileNumber: data.phone,
|
||||
regionCode: data.location,
|
||||
wechatNumber: data.wechat,
|
||||
portfolioUrl: data.portfolioUrl,
|
||||
})
|
||||
// 接口成功后更新本地数据
|
||||
profile.value.name = data.name
|
||||
profile.value.email = data.email
|
||||
profile.value.phone = data.phone
|
||||
profile.value.wechat = data.wechat
|
||||
profile.value.portfolioUrl = data.portfolioUrl || ''
|
||||
/** 将城市编码存入 regionCode,先直接显示编码,后续联调获取接口再做名称转换 */
|
||||
profile.value.regionCode = data.location || ''
|
||||
ElMessage.success('个人信息保存成功')
|
||||
|
||||
+4
-4
@@ -27,8 +27,8 @@ export default defineConfig({
|
||||
host: '0.0.0.0',
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'https://test.offerpai.com.cn/',
|
||||
// target: 'http://127.0.0.1:8080',
|
||||
// target: 'https://test.offerpai.com.cn/',
|
||||
target: 'http://127.0.0.1:8080',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/jianshi-api': {
|
||||
@@ -37,8 +37,8 @@ export default defineConfig({
|
||||
rewrite: (path) => path.replace(/^\/jianshi-api/, '/proxy-api'),
|
||||
},
|
||||
'/ai-api': {
|
||||
target: 'https://test.offerpai.com.cn/',
|
||||
// target: 'http://127.0.0.1:8000',
|
||||
// target: 'https://test.offerpai.com.cn/',
|
||||
target: 'http://127.0.0.1:8000',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/ai-api/, ''),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user