登陆信息同步和接口联调
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { config } from "~config"
|
import { config } from "~config"
|
||||||
import { createHttp } from "./request"
|
import { createHttp } from "./request"
|
||||||
|
import type { ResumeData } from "~lib/types"
|
||||||
|
|
||||||
/** Python AI 后端接口 */
|
/** Python AI 后端接口 */
|
||||||
const http = createHttp(config.aiBaseApi)
|
const http = createHttp(config.aiBaseApi)
|
||||||
@@ -8,3 +9,8 @@ const http = createHttp(config.aiBaseApi)
|
|||||||
export function healthCheck() {
|
export function healthCheck() {
|
||||||
return http.get('/health/')
|
return http.get('/health/')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 查询岗位定制简历 */
|
||||||
|
export function getCustomizeResume(jobId: number) {
|
||||||
|
return http.get<ResumeData>('/job/customize-resume', { params: { job_id: String(jobId) } })
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -1,12 +1,13 @@
|
|||||||
import { config } from "~config"
|
import { config } from "~config"
|
||||||
import { createHttp } from "./request"
|
import { createHttp } from "./request"
|
||||||
|
import type { JobInfo } from "~lib/types"
|
||||||
|
|
||||||
/** Java 后端接口 */
|
/** Java 后端接口 */
|
||||||
const http = createHttp(config.dataBaseApi)
|
const http = createHttp(config.dataBaseApi)
|
||||||
|
|
||||||
/** 根据岗位来源地址查询岗位信息 */
|
/** 根据岗位来源地址查询岗位信息 */
|
||||||
export function findJobBySourceUrl(sourceUrl: string) {
|
export function findJobBySourceUrl(sourceUrl: string) {
|
||||||
return http.get('/job/findByUrl', { params: { sourceUrl } })
|
return http.get<JobInfo>('/job/findByUrl', { params: { sourceUrl } })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 校验登录状态 */
|
/** 校验登录状态 */
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ $radius-sm: 12px;
|
|||||||
top: 10px;
|
top: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
width: 384px;
|
width: 384px;
|
||||||
|
min-height: 480px;
|
||||||
max-height: calc(100vh - 20px);
|
max-height: calc(100vh - 20px);
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: $radius-lg;
|
border-radius: $radius-lg;
|
||||||
@@ -251,3 +252,35 @@ $radius-sm: 12px;
|
|||||||
transition: width 0.3s;
|
transition: width 0.3s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 未登录提示区域 */
|
||||||
|
.op-login-prompt {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.op-login-text {
|
||||||
|
font-size: 15px;
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.op-login-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
background: $primary;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: $radius-md;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
&:hover { opacity: 0.85; }
|
||||||
|
}
|
||||||
|
|||||||
+100
-40
@@ -4,25 +4,33 @@
|
|||||||
* 包含:职位信息卡片、自动填写按钮、简历管理、填写进度等功能区域
|
* 包含:职位信息卡片、自动填写按钮、简历管理、填写进度等功能区域
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState } from "react"
|
import { useState, useEffect } from "react"
|
||||||
|
import { getCookieValue } from "~utils/cookie"
|
||||||
|
import { getCustomizeResume } from "~api/aiApi"
|
||||||
import { fillMatchedField, delay, isSearchPickerField, fillSearchPickerField, isTimePeriodField, isTimeSingleField, fillTimePeriodField, fillTimeSingleField } from "~lib/autofill"
|
import { fillMatchedField, delay, isSearchPickerField, fillSearchPickerField, isTimePeriodField, isTimeSingleField, fillTimePeriodField, fillTimeSingleField } from "~lib/autofill"
|
||||||
import { extractDomStructure, detectPageLanguage, isJobApplicationForm } from "~lib/dom"
|
import { extractDomStructure, detectPageLanguage, isJobApplicationForm } from "~lib/dom"
|
||||||
import { matchFormFields, matchFormFieldsInRange, matchMainFields } from "~lib/formMatcher"
|
import { matchFormFields, matchFormFieldsInRange, matchMainFields } from "~lib/formMatcher"
|
||||||
import { detectPickerField } from "~lib/pickerDetector"
|
import { detectPickerField } from "~lib/pickerDetector"
|
||||||
import { detectAndUploadResume } from "~lib/resumeUpload"
|
import { detectAndUploadResume } from "~lib/resumeUpload"
|
||||||
import { getMockResumeData } from "~lib/constants"
|
import { getMockResumeData2 } from "~lib/constants"
|
||||||
import { getResumeFieldValue } from "~lib/resumeDataHelper"
|
import { getResumeFieldValue } from "~lib/resumeDataHelper"
|
||||||
import { locateExperienceSections, expandExperienceSections, sortExperienceByTime, relocateSegmentContainer } from "~lib/experienceSection"
|
import { locateExperienceSections, expandExperienceSections, sortExperienceByTime, relocateSegmentContainer } from "~lib/experienceSection"
|
||||||
import type { MatchedFormField, ResumeData, ExperienceSection } from "~lib/types"
|
import type { MatchedFormField, ResumeData, ExperienceSection, JobInfo } from "~lib/types"
|
||||||
import "./SidebarPanel.scss"
|
import "./SidebarPanel.scss"
|
||||||
|
|
||||||
/** 侧边栏面板的 Props */
|
/** 侧边栏面板的 Props */
|
||||||
interface SidebarPanelProps {
|
interface SidebarPanelProps {
|
||||||
|
/** 当前标签页的来源链接 */
|
||||||
|
sourceUrl: string
|
||||||
|
/** 岗位信息(由 sidebar 层查询后传入) */
|
||||||
|
jobInfo: JobInfo | null
|
||||||
/** 关闭面板的回调函数 */
|
/** 关闭面板的回调函数 */
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SidebarPanel({ onClose }: SidebarPanelProps) {
|
export function SidebarPanel({ sourceUrl, jobInfo, onClose }: SidebarPanelProps) {
|
||||||
|
/** 是否已登录(有 Token) */
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState<boolean | null>(null)
|
||||||
/** 是否正在执行自动填写 */
|
/** 是否正在执行自动填写 */
|
||||||
const [filling, setFilling] = useState(false)
|
const [filling, setFilling] = useState(false)
|
||||||
/** 页面语言类型:中文 / 英文 */
|
/** 页面语言类型:中文 / 英文 */
|
||||||
@@ -34,6 +42,33 @@ export function SidebarPanel({ onClose }: SidebarPanelProps) {
|
|||||||
/** 当前使用的简历数据 */
|
/** 当前使用的简历数据 */
|
||||||
const [resumeData, setResumeData] = useState<ResumeData | null>(null)
|
const [resumeData, setResumeData] = useState<ResumeData | null>(null)
|
||||||
|
|
||||||
|
/** 页面加载时检查 Token,有岗位信息则查询定制简历 */
|
||||||
|
useEffect(() => {
|
||||||
|
getCookieValue("Token").then((token) => {
|
||||||
|
console.log("[OfferPie] SidebarPanel 获取到的 Token:", token)
|
||||||
|
setIsLoggedIn(!!token)
|
||||||
|
|
||||||
|
// 有 Token 且有岗位 ID 时,查询定制简历
|
||||||
|
if (token && jobInfo?.id) {
|
||||||
|
getCustomizeResume(jobInfo.id).then((resumeRes: any) => {
|
||||||
|
console.log("[OfferPie] 定制简历数据:", resumeRes)
|
||||||
|
// 接口返回的 resume 字段映射为 main
|
||||||
|
const mappedData: ResumeData = {
|
||||||
|
main: resumeRes.resume || {},
|
||||||
|
education: resumeRes.education || [],
|
||||||
|
work: resumeRes.work || [],
|
||||||
|
internship: resumeRes.internship || [],
|
||||||
|
project: resumeRes.project || [],
|
||||||
|
competition: resumeRes.competition || [],
|
||||||
|
}
|
||||||
|
setResumeData(mappedData)
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn("[OfferPie] 查询定制简历失败:", err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自动填写按钮点击处理
|
* 自动填写按钮点击处理
|
||||||
* 流程:提取 DOM → 检测语言 → 判断是否表单页 → 检测简历上传 → 匹配字段 → 识别选择器 → 填充测试数据
|
* 流程:提取 DOM → 检测语言 → 判断是否表单页 → 检测简历上传 → 匹配字段 → 识别选择器 → 填充测试数据
|
||||||
@@ -58,17 +93,16 @@ export function SidebarPanel({ onClose }: SidebarPanelProps) {
|
|||||||
console.log(`===== OfferPie: 是否为职位申请表单页面 = ${isForm} =====`)
|
console.log(`===== OfferPie: 是否为职位申请表单页面 = ${isForm} =====`)
|
||||||
|
|
||||||
if (isForm) {
|
if (isForm) {
|
||||||
// 4. 获取简历数据(当前使用 mock 数据,后续替换为接口调用)
|
// 4. 获取简历数据(优先使用接口数据,无接口数据时 fallback 到 mock)
|
||||||
// TODO: 替换为真实接口 → const res = await javaApi.get<{resume: ..., education: ..., ...}>("/resume/detail")
|
const currentResumeData = resumeData || getMockResumeData2()
|
||||||
const currentResumeData = getMockResumeData()
|
if (!resumeData) setResumeData(currentResumeData)
|
||||||
setResumeData(currentResumeData)
|
|
||||||
console.log(`===== OfferPie: 已加载简历数据,教育${currentResumeData.education.length}段 工作${currentResumeData.work.length}段 实习${currentResumeData.internship.length}段 项目${currentResumeData.project.length}段 竞赛${currentResumeData.competition.length}段 =====`)
|
console.log(`===== OfferPie: 已加载简历数据,教育${currentResumeData.education.length}段 工作${currentResumeData.work.length}段 实习${currentResumeData.internship.length}段 项目${currentResumeData.project.length}段 竞赛${currentResumeData.competition.length}段 =====`)
|
||||||
|
|
||||||
// // 4.1 检测并上传简历文件
|
// 4.1 检测并上传简历文件
|
||||||
// const resumeUrl = "https://offerpie.oss-cn-guangzhou.aliyuncs.com/%E4%BA%8E%E5%A4%A7%E6%98%A5.pdf"
|
const resumeUrl = "https://offerpie.oss-cn-guangzhou.aliyuncs.com/%E4%BA%8E%E5%A4%A7%E6%98%A5.pdf"
|
||||||
// const uploaded = await detectAndUploadResume(resumeUrl)
|
const uploaded = await detectAndUploadResume(resumeUrl)
|
||||||
// console.log(`===== OfferPie: 简历上传 ${uploaded ? "成功" : "跳过(未找到上传按钮或失败)"} =====`)
|
console.log(`===== OfferPie: 简历上传 ${uploaded ? "成功" : "跳过(未找到上传按钮或失败)"} =====`)
|
||||||
// if (uploaded) await delay(1000) // 等待网站解析简历
|
if (uploaded) await delay(1000) // 等待网站解析简历
|
||||||
|
|
||||||
// 4.5 定位经历区块并统计已展开段数
|
// 4.5 定位经历区块并统计已展开段数
|
||||||
const sectionResults = locateExperienceSections(document.body, lang)
|
const sectionResults = locateExperienceSections(document.body, lang)
|
||||||
@@ -307,11 +341,10 @@ export function SidebarPanel({ onClose }: SidebarPanelProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="op-container">
|
<div className="op-container">
|
||||||
{/* 顶部操作栏:反馈、设置、关闭按钮 */}
|
{/* 顶部操作栏:关闭按钮始终显示,反馈和设置仅登录后显示 */}
|
||||||
<div className="op-header">
|
<div className="op-header">
|
||||||
<span className="op-header-link">反馈123456789</span>
|
{isLoggedIn && <span className="op-header-link">反馈12</span>}
|
||||||
<span className="op-header-link">设置</span>
|
{isLoggedIn && <span className="op-header-link">设置</span>}
|
||||||
{/* 关闭按钮:点击后隐藏侧边栏 */}
|
|
||||||
<button className="op-close-btn" onClick={onClose}>
|
<button className="op-close-btn" onClick={onClose}>
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
|
||||||
<circle cx="12" cy="12" r="10" />
|
<circle cx="12" cy="12" r="10" />
|
||||||
@@ -323,38 +356,65 @@ export function SidebarPanel({ onClose }: SidebarPanelProps) {
|
|||||||
{/* 插件标题 */}
|
{/* 插件标题 */}
|
||||||
<div className="op-title">大学生求职助手</div>
|
<div className="op-title">大学生求职助手</div>
|
||||||
|
|
||||||
{/* 职位信息卡片:展示当前页面识别到的职位信息和匹配度 */}
|
{/* 未登录状态:显示登录提示 */}
|
||||||
|
{!isLoggedIn && isLoggedIn !== null && (
|
||||||
|
<div className="op-login-prompt">
|
||||||
|
<p className="op-login-text">请先前往 Offer派 官网进行登录</p>
|
||||||
|
<button
|
||||||
|
className="op-login-btn"
|
||||||
|
onClick={() => {
|
||||||
|
onClose()
|
||||||
|
window.open("http://localhost:5173/jobs", "_blank")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
前往 Offer派
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 已登录状态:显示完整功能 */}
|
||||||
|
{isLoggedIn && (
|
||||||
|
<>
|
||||||
|
{/* 职位信息卡片 */}
|
||||||
<div className="op-job-card">
|
<div className="op-job-card">
|
||||||
{/* 职位图标 */}
|
|
||||||
<div className="op-job-icon">
|
<div className="op-job-icon">
|
||||||
|
{jobInfo?.companyLogoUrl ? (
|
||||||
|
<img src={jobInfo.companyLogoUrl} alt="logo" style={{ width: 40, height: 40, borderRadius: 10, objectFit: "cover" }} />
|
||||||
|
) : (
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="#666">
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="#666">
|
||||||
<rect x="3" y="3" width="7" height="7" rx="1" />
|
<rect x="3" y="3" width="7" height="7" rx="1" />
|
||||||
<rect x="14" y="3" width="7" height="7" rx="1" />
|
<rect x="14" y="3" width="7" height="7" rx="1" />
|
||||||
<rect x="3" y="14" width="7" height="7" rx="1" />
|
<rect x="3" y="14" width="7" height="7" rx="1" />
|
||||||
<rect x="14" y="14" width="7" height="7" rx="1" />
|
<rect x="14" y="14" width="7" height="7" rx="1" />
|
||||||
</svg>
|
</svg>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* 职位名称和公司信息 */}
|
|
||||||
<div className="op-job-info">
|
<div className="op-job-info">
|
||||||
<div className="op-job-title">数据产品经理</div>
|
<div className="op-job-title">{jobInfo?.title || "未匹配到岗位信息"}</div>
|
||||||
<div className="op-job-meta">北京 · 字节跳动 独角兽</div>
|
<div className="op-job-meta">
|
||||||
|
{jobInfo
|
||||||
|
? [jobInfo.regionName, jobInfo.companyShortName || jobInfo.companyName, jobInfo.companyType]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" · ") || "—"
|
||||||
|
: "当前页面暂未关联岗位"}
|
||||||
</div>
|
</div>
|
||||||
{/* 匹配度环形进度条 */}
|
</div>
|
||||||
|
{jobInfo?.matchScore != null && (
|
||||||
<div className="op-match-score">
|
<div className="op-match-score">
|
||||||
<svg width="48" height="48" viewBox="0 0 48 48">
|
<svg width="48" height="48" viewBox="0 0 48 48">
|
||||||
{/* 背景圆环 */}
|
|
||||||
<circle cx="24" cy="24" r="20" fill="none" stroke="#f0f0f0" strokeWidth="3" />
|
<circle cx="24" cy="24" r="20" fill="none" stroke="#f0f0f0" strokeWidth="3" />
|
||||||
{/* 进度圆环:60% 匹配度 */}
|
|
||||||
<circle cx="24" cy="24" r="20" fill="none" stroke="#000" strokeWidth="3"
|
<circle cx="24" cy="24" r="20" fill="none" stroke="#000" strokeWidth="3"
|
||||||
strokeDasharray={`${0.6 * 2 * Math.PI * 20} ${2 * Math.PI * 20}`}
|
strokeDasharray={`${(jobInfo.matchScore) / 100 * 2 * Math.PI * 20} ${2 * Math.PI * 20}`}
|
||||||
strokeLinecap="round" transform="rotate(-90 24 24)" />
|
strokeLinecap="round" transform="rotate(-90 24 24)" />
|
||||||
{/* 百分比文字 */}
|
<text x="24" y="26" textAnchor="middle" fontSize="13" fontWeight="700" fill="#000">
|
||||||
<text x="24" y="26" textAnchor="middle" fontSize="13" fontWeight="700" fill="#000">60%</text>
|
{jobInfo.matchScore}%
|
||||||
|
</text>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 自动填写按钮:点击后获取页面结构,后续会调用 AI 接口自动填表 */}
|
{/* 自动填写按钮 */}
|
||||||
<button className="op-autofill-btn" onClick={handleAutoFill} disabled={filling}>
|
<button className="op-autofill-btn" onClick={handleAutoFill} disabled={filling}>
|
||||||
{filling ? "分析中..." : "自动填写"}
|
{filling ? "分析中..." : "自动填写"}
|
||||||
</button>
|
</button>
|
||||||
@@ -367,18 +427,17 @@ export function SidebarPanel({ onClose }: SidebarPanelProps) {
|
|||||||
|
|
||||||
{/* 简历区域 */}
|
{/* 简历区域 */}
|
||||||
<div className="op-section-label">简历</div>
|
<div className="op-section-label">简历</div>
|
||||||
{/* 简历卡片:展示当前选中的简历信息 */}
|
|
||||||
<div className="op-resume-card">
|
<div className="op-resume-card">
|
||||||
{/* 简历头像 */}
|
<div className="op-resume-avatar">
|
||||||
<div className="op-resume-avatar">B</div>
|
{resumeData?.main?.name ? resumeData.main.name.charAt(0) : "—"}
|
||||||
{/* 简历名称和标签 */}
|
|
||||||
<div className="op-resume-info">
|
|
||||||
<span className="op-resume-name">李华-产品经理</span>
|
|
||||||
<span className="op-resume-tag">默认</span>
|
|
||||||
<span className="op-resume-tag">竞争力分析</span>
|
|
||||||
</div>
|
</div>
|
||||||
{/* 更改简历按钮 */}
|
<div className="op-resume-info">
|
||||||
<span className="op-resume-change">更改</span>
|
<span className="op-resume-name">
|
||||||
|
{resumeData?.main?.name || "暂无简历"}
|
||||||
|
</span>
|
||||||
|
{resumeData && <span className="op-resume-tag">默认</span>}
|
||||||
|
</div>
|
||||||
|
{/* <span className="op-resume-change">更改</span> */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 针对性优化简历按钮 */}
|
{/* 针对性优化简历按钮 */}
|
||||||
@@ -389,10 +448,11 @@ export function SidebarPanel({ onClose }: SidebarPanelProps) {
|
|||||||
<span className="op-progress-label">填写进度</span>
|
<span className="op-progress-label">填写进度</span>
|
||||||
<span className="op-progress-value">50%</span>
|
<span className="op-progress-value">50%</span>
|
||||||
</div>
|
</div>
|
||||||
{/* 进度条 */}
|
|
||||||
<div className="op-progress-bar-bg">
|
<div className="op-progress-bar-bg">
|
||||||
<div className="op-progress-bar-fill" />
|
<div className="op-progress-bar-fill" />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const envConfigs: Record<string, {
|
|||||||
}> = {
|
}> = {
|
||||||
dev: {
|
dev: {
|
||||||
dataBaseApi: 'http://localhost:8080/api',
|
dataBaseApi: 'http://localhost:8080/api',
|
||||||
|
// aiBaseApi: 'http://192.168.31.133:8000',
|
||||||
aiBaseApi: 'http://localhost:8000',
|
aiBaseApi: 'http://localhost:8000',
|
||||||
cookieSourceUrl: 'http://192.168.31.135:5173',
|
cookieSourceUrl: 'http://192.168.31.135:5173',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,8 +6,11 @@
|
|||||||
|
|
||||||
import styleText from "data-text:~components/SidebarPanel.scss"
|
import styleText from "data-text:~components/SidebarPanel.scss"
|
||||||
import type { PlasmoCSConfig, PlasmoGetStyle } from "plasmo"
|
import type { PlasmoCSConfig, PlasmoGetStyle } from "plasmo"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState, useRef } from "react"
|
||||||
import { SidebarPanel } from "~components/SidebarPanel"
|
import { SidebarPanel } from "~components/SidebarPanel"
|
||||||
|
import { getCookieValue } from "~utils/cookie"
|
||||||
|
import { findJobBySourceUrl } from "~api/dataApi"
|
||||||
|
import type { JobInfo } from "~lib/types"
|
||||||
|
|
||||||
/** Content Script 配置:匹配所有网页 */
|
/** Content Script 配置:匹配所有网页 */
|
||||||
export const config: PlasmoCSConfig = {
|
export const config: PlasmoCSConfig = {
|
||||||
@@ -31,8 +34,38 @@ export const getStyle: PlasmoGetStyle = () => {
|
|||||||
function Sidebar() {
|
function Sidebar() {
|
||||||
/** 侧边栏是否可见 */
|
/** 侧边栏是否可见 */
|
||||||
const [visible, setVisible] = useState(false)
|
const [visible, setVisible] = useState(false)
|
||||||
|
/** 当前标签页的来源链接 */
|
||||||
|
const [sourceUrl, setSourceUrl] = useState("")
|
||||||
|
/** 岗位信息(在 sidebar 层查询,传给 SidebarPanel) */
|
||||||
|
const [jobInfo, setJobInfo] = useState<JobInfo | null>(null)
|
||||||
|
/** 是否已自动打开过面板(避免重复打开) */
|
||||||
|
const autoOpenedRef = useRef(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// 页面打开时获取 Token 和当前页面 URL 作为 sourceUrl
|
||||||
|
const currentUrl = window.location.href
|
||||||
|
setSourceUrl(currentUrl)
|
||||||
|
console.log("[OfferPie] 当前页面 sourceUrl:", currentUrl)
|
||||||
|
|
||||||
|
getCookieValue("Token").then((token) => {
|
||||||
|
console.log("[OfferPie] 页面加载时获取到的 Token:", token)
|
||||||
|
if (!token) return
|
||||||
|
|
||||||
|
// 有 token 时,先查询岗位信息,有岗位才自动打开面板
|
||||||
|
findJobBySourceUrl(currentUrl).then((data) => {
|
||||||
|
console.log("[OfferPie] 岗位信息:", data)
|
||||||
|
if (data && data.id) {
|
||||||
|
setJobInfo(data)
|
||||||
|
if (!autoOpenedRef.current) {
|
||||||
|
autoOpenedRef.current = true
|
||||||
|
setVisible(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn("[OfferPie] 查询岗位信息失败:", err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息监听器:接收来自 background 或 popup 的消息
|
* 消息监听器:接收来自 background 或 popup 的消息
|
||||||
* 当收到 TOGGLE_SIDEBAR 消息时,切换侧边栏的显示状态
|
* 当收到 TOGGLE_SIDEBAR 消息时,切换侧边栏的显示状态
|
||||||
@@ -40,6 +73,9 @@ function Sidebar() {
|
|||||||
const handler = (message: any) => {
|
const handler = (message: any) => {
|
||||||
if (message.type === "TOGGLE_SIDEBAR") {
|
if (message.type === "TOGGLE_SIDEBAR") {
|
||||||
setVisible((prev) => !prev)
|
setVisible((prev) => !prev)
|
||||||
|
getCookieValue("Token").then((token) => {
|
||||||
|
console.log("[OfferPie] 点击插件按钮时获取到的 Token:", token)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chrome.runtime.onMessage.addListener(handler)
|
chrome.runtime.onMessage.addListener(handler)
|
||||||
@@ -50,7 +86,7 @@ function Sidebar() {
|
|||||||
// 不可见时不渲染任何内容
|
// 不可见时不渲染任何内容
|
||||||
if (!visible) return null
|
if (!visible) return null
|
||||||
|
|
||||||
return <SidebarPanel onClose={() => setVisible(false)} />
|
return <SidebarPanel sourceUrl={sourceUrl} jobInfo={jobInfo} onClose={() => setVisible(false)} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Sidebar
|
export default Sidebar
|
||||||
|
|||||||
@@ -268,10 +268,10 @@ export const EXPERIENCE_SECTION_CONFIGS: ExperienceSectionConfig[] = [
|
|||||||
// ============ Mock 简历数据(模拟接口返回) ============
|
// ============ Mock 简历数据(模拟接口返回) ============
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模拟后端接口返回的完整简历数据
|
* 模拟后端接口返回的完整简历数据(测试用)
|
||||||
* 后续会替换为真实接口调用:javaApi.get<ResumeData>("/resume/detail")
|
* 已被接口替代,保留用于离线调试
|
||||||
*/
|
*/
|
||||||
export function getMockResumeData(): ResumeData {
|
export function getMockResumeData2(): ResumeData {
|
||||||
return {
|
return {
|
||||||
main: {
|
main: {
|
||||||
avatarUrl: "",
|
avatarUrl: "",
|
||||||
|
|||||||
@@ -3,6 +3,54 @@
|
|||||||
* 包含简历数据接口、表单标签接口、匹配字段接口、UI组件库配置接口等所有共享类型
|
* 包含简历数据接口、表单标签接口、匹配字段接口、UI组件库配置接口等所有共享类型
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// ============ 岗位信息类型 ============
|
||||||
|
|
||||||
|
/** 岗位匹配度详情 */
|
||||||
|
export interface JobMatchScoreDto {
|
||||||
|
/** 教育得分(0-100) */
|
||||||
|
educationScore?: number
|
||||||
|
/** 技能得分(0-100) */
|
||||||
|
skillScore?: number
|
||||||
|
/** 经验得分(0-100) */
|
||||||
|
experienceScore?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 岗位信息 */
|
||||||
|
export interface JobInfo {
|
||||||
|
/** 岗位ID */
|
||||||
|
id?: number
|
||||||
|
/** 岗位名称 */
|
||||||
|
title?: string
|
||||||
|
/** 薪资描述 */
|
||||||
|
salary?: string
|
||||||
|
/** 公司名称 */
|
||||||
|
companyName?: string
|
||||||
|
/** 公司简称 */
|
||||||
|
companyShortName?: string
|
||||||
|
/** 公司类型 */
|
||||||
|
companyType?: string
|
||||||
|
/** 公司Logo */
|
||||||
|
companyLogoUrl?: string
|
||||||
|
/** 地区名称 */
|
||||||
|
regionName?: string
|
||||||
|
/** 岗位类型名称 */
|
||||||
|
categoryName?: string
|
||||||
|
/** 岗位标签 */
|
||||||
|
tags?: string[]
|
||||||
|
/** 来源链接 */
|
||||||
|
sourceUrl?: string
|
||||||
|
/** 是否收藏 */
|
||||||
|
isFavorite?: boolean
|
||||||
|
/** 投递状态(null=未投递,0=已投递 1=面试中 2=有Offer 3=未通过 4=已结束) */
|
||||||
|
applicationStatus?: number | null
|
||||||
|
/** 岗位状态(0=有效 1=已下架 2=已过期) */
|
||||||
|
status?: number
|
||||||
|
/** 匹配总分(0-90) */
|
||||||
|
matchScore?: number
|
||||||
|
/** 匹配度详情 */
|
||||||
|
matchDetail?: JobMatchScoreDto
|
||||||
|
}
|
||||||
|
|
||||||
// ============ 简历数据类型 ============
|
// ============ 简历数据类型 ============
|
||||||
|
|
||||||
/** 描述段落(富文本段落) */
|
/** 描述段落(富文本段落) */
|
||||||
|
|||||||
Reference in New Issue
Block a user