You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5560 lines
205 KiB
5560 lines
205 KiB
/**
|
|
* 接口层:所有数据请求统一从这里发起。
|
|
*
|
|
* 约定:
|
|
* - 每个函数用 JSDoc 标注请求方式(@method)、URL(@url)、参数(@param)、返回值(@returns)
|
|
* - 所有 mock 数据统一在本文件管理;真实后端就绪后,仅替换函数体为 fetch 即可
|
|
* - 业务规则计算(等级映射等)与排序/筛选/搜索/分页在本层完成
|
|
*/
|
|
import Constants from 'expo-constants';
|
|
import type {
|
|
AbilityAssessment,
|
|
AITool,
|
|
Article,
|
|
Badge,
|
|
CompareData,
|
|
Conversation,
|
|
OfferCompareData,
|
|
DimensionScore,
|
|
EmployeeReview,
|
|
EnterpriseEntry,
|
|
HollandAssessment,
|
|
HollandIntro,
|
|
HollandType,
|
|
Interview,
|
|
Job,
|
|
JobDetail,
|
|
JobLevel,
|
|
JobListParams,
|
|
JobOverallScore,
|
|
JobPreference,
|
|
MatchResult,
|
|
MenuItem,
|
|
Notification,
|
|
OverallScore,
|
|
ConversationDetail,
|
|
ChatMessage,
|
|
RankOverview,
|
|
Resume,
|
|
ResumeExportFormat,
|
|
ResumeExportSettings,
|
|
ResumeOptimizeResult,
|
|
SalaryData,
|
|
SalaryBracket,
|
|
SalaryCalcInput,
|
|
SalaryCalcResult,
|
|
SalaryInsuranceItem,
|
|
SalaryStructureItem,
|
|
SalaryTaxItem,
|
|
UserProfile,
|
|
UserStats,
|
|
WeightDimension,
|
|
WeightSettings,
|
|
WeightTemplate,
|
|
SkillCloudData,
|
|
SkillCloudFilter,
|
|
SkillWord,
|
|
SkillCategory,
|
|
TopSkill,
|
|
ChecklistGroup,
|
|
OnboardingChecklist,
|
|
JobProgress,
|
|
IdentityOption,
|
|
AppRole,
|
|
AIInterviewData,
|
|
InterviewSession,
|
|
HREvaluation,
|
|
EvaluationAppealForm,
|
|
AppealDimension,
|
|
GameLevel,
|
|
GameLevelResult,
|
|
GameProgress,
|
|
SubmitGameLevelPayload,
|
|
LegalBrand,
|
|
LegalConversation,
|
|
LegalFAQ,
|
|
LegalMessage,
|
|
LegalMembership,
|
|
LegalOrder,
|
|
LegalPaymentChannel,
|
|
LegalPlan,
|
|
LegalService,
|
|
LegalServiceRequest,
|
|
LegalTemplate,
|
|
ToggleSetting,
|
|
ActionSetting,
|
|
AboutInfo,
|
|
UpdateTogglePayload,
|
|
PrivacyCenter,
|
|
AccountCancelData,
|
|
DeleteAllData,
|
|
DndSetting,
|
|
KeywordBlockSetting,
|
|
LegalDocument,
|
|
PrivacyDetailSetting,
|
|
PersonalDataExport,
|
|
CandidateAbility,
|
|
CandidateDetail,
|
|
CandidateReview,
|
|
EnterpriseOverview,
|
|
EnterpriseQuickAction,
|
|
EnterpriseStat,
|
|
InterviewScoreData,
|
|
InterviewScoreDimension,
|
|
InterviewScoreTag,
|
|
JobEvaluationDimension,
|
|
JobEvaluationManagement,
|
|
JobEvaluationSummary,
|
|
JobEvaluationEmptyState,
|
|
JobPublishFormData,
|
|
JobQualityEstimate,
|
|
JobReview,
|
|
JobManagement,
|
|
TalentRecommendation,
|
|
EnterpriseCommunication,
|
|
EnterpriseCertificationBenefit,
|
|
EnterpriseCertificationData,
|
|
EnterpriseCertificationForm,
|
|
EnterpriseCertificationStatus,
|
|
MatchModeOption,
|
|
OperationRules,
|
|
AuditReport,
|
|
WechatAccountStatus,
|
|
WechatLoginResult,
|
|
WechatBindPhonePayload,
|
|
WechatSession,
|
|
} from '../types';
|
|
import { DEFAULT_SOCIAL_PROFILE, MOCK_DELAY, SALARY_RANGES, SOCIAL_SECURITY_CITY_PROFILES, TOKEN_TTL_MS } from '../constants';
|
|
import { colors } from '../theme';
|
|
import { storageGet, storageSet, storageRemove } from '../lib/storage';
|
|
|
|
/** 接口地址(通过 expo-constants 从 app.config.js 的 extra 读取) */
|
|
export const API_BASE_URL: string =
|
|
Constants.expoConfig?.extra?.apiBaseUrl ?? 'https://dev.api.com';
|
|
|
|
/* ===================== 模拟数据(真实后端就绪后删除) ===================== */
|
|
|
|
/** 岗位列表(原始数据,不含 level;level 由接口层按分数计算) */
|
|
const MOCK_JOBS: Omit<Job, 'level'>[] = [
|
|
{
|
|
id: '1',
|
|
companyName: '字节跳动',
|
|
companyLocation: '北京 · 海淀区',
|
|
positionName: '高级前端工程师',
|
|
salaryMin: 25,
|
|
salaryMax: 40,
|
|
score: 92,
|
|
experience: '3-5年',
|
|
education: '本科',
|
|
industry: '互联网/IT',
|
|
city: '北京',
|
|
benefits: ['五险一金', '年终奖金', '弹性工作'],
|
|
},
|
|
{
|
|
id: '2',
|
|
companyName: '腾讯科技',
|
|
companyLocation: '深圳 · 南山区',
|
|
positionName: '产品经理',
|
|
salaryMin: 20,
|
|
salaryMax: 35,
|
|
score: 78,
|
|
experience: '3-5年',
|
|
education: '本科',
|
|
industry: '互联网/IT',
|
|
city: '深圳',
|
|
benefits: ['五险一金', '年终奖金', '弹性工作'],
|
|
},
|
|
{
|
|
id: '3',
|
|
companyName: '阿里巴巴',
|
|
companyLocation: '杭州 · 余杭区',
|
|
positionName: 'UI设计师',
|
|
salaryMin: 15,
|
|
salaryMax: 25,
|
|
score: 85,
|
|
experience: '3-5年',
|
|
education: '本科',
|
|
industry: '互联网/IT',
|
|
city: '杭州',
|
|
benefits: ['五险一金', '年终奖金', '弹性工作'],
|
|
},
|
|
{
|
|
id: '4',
|
|
companyName: '美团',
|
|
companyLocation: '北京 · 朝阳区',
|
|
positionName: 'Java开发工程师',
|
|
salaryMin: 18,
|
|
salaryMax: 30,
|
|
score: 65,
|
|
experience: '3-5年',
|
|
education: '本科',
|
|
industry: '互联网/IT',
|
|
city: '北京',
|
|
benefits: ['五险一金', '年终奖金', '弹性工作'],
|
|
},
|
|
{
|
|
id: '5',
|
|
companyName: '字节跳动',
|
|
companyLocation: '上海 · 浦东新区',
|
|
positionName: '前端实习生',
|
|
salaryMin: 6,
|
|
salaryMax: 9,
|
|
score: 60,
|
|
experience: '应届生',
|
|
education: '本科',
|
|
industry: '互联网/IT',
|
|
city: '上海',
|
|
benefits: ['五险一金', '弹性工作'],
|
|
},
|
|
{
|
|
id: '6',
|
|
companyName: '招商银行',
|
|
companyLocation: '深圳 · 福田区',
|
|
positionName: '金融分析师',
|
|
salaryMin: 30,
|
|
salaryMax: 50,
|
|
score: 88,
|
|
experience: '5-10年',
|
|
education: '硕士',
|
|
industry: '金融',
|
|
city: '深圳',
|
|
benefits: ['五险一金', '年终奖金', '股权激励'],
|
|
},
|
|
{
|
|
id: '7',
|
|
companyName: '新东方',
|
|
companyLocation: '北京 · 朝阳区',
|
|
positionName: '课程顾问',
|
|
salaryMin: 12,
|
|
salaryMax: 20,
|
|
score: 72,
|
|
experience: '1-3年',
|
|
education: '大专',
|
|
industry: '教育',
|
|
city: '北京',
|
|
benefits: ['五险一金', '带薪年假'],
|
|
},
|
|
{
|
|
id: '8',
|
|
companyName: '华润医疗',
|
|
companyLocation: '广州 · 天河区',
|
|
positionName: '医学代表',
|
|
salaryMin: 15,
|
|
salaryMax: 25,
|
|
score: 75,
|
|
experience: '3-5年',
|
|
education: '本科',
|
|
industry: '医疗健康',
|
|
city: '广州',
|
|
benefits: ['五险一金', '交通补助'],
|
|
},
|
|
{
|
|
id: '9',
|
|
companyName: '宁德时代',
|
|
companyLocation: '宁德 · 东侨区',
|
|
positionName: '电池工程师',
|
|
salaryMin: 28,
|
|
salaryMax: 45,
|
|
score: 90,
|
|
experience: '5-10年',
|
|
education: '硕士',
|
|
industry: '制造业',
|
|
city: '宁德',
|
|
benefits: ['五险一金', '年终奖金', '股权激励'],
|
|
},
|
|
];
|
|
|
|
/** 会话列表 */
|
|
const MOCK_CONVERSATIONS: Conversation[] = [
|
|
{
|
|
id: 'conv-1',
|
|
contactName: '张HR(字节跳动)',
|
|
companyName: '字节跳动',
|
|
avatar: '字',
|
|
lastMessage: '您的简历已通过初筛,方便约个时间面试吗?',
|
|
timestamp: '09:32',
|
|
unread: true,
|
|
matched: true,
|
|
},
|
|
{
|
|
id: 'conv-2',
|
|
contactName: '李HR(腾讯科技)',
|
|
companyName: '腾讯科技',
|
|
avatar: '腾',
|
|
lastMessage: '面试时间已确认:明天 10:00 视频面试',
|
|
timestamp: '昨天',
|
|
unread: true,
|
|
matched: true,
|
|
},
|
|
{
|
|
id: 'conv-3',
|
|
contactName: '王经理(阿里巴巴)',
|
|
companyName: '阿里巴巴',
|
|
avatar: '阿',
|
|
lastMessage: '感谢您的投递,我们会在 3 个工作日内反馈',
|
|
timestamp: '周一',
|
|
unread: false,
|
|
matched: false,
|
|
},
|
|
{
|
|
id: 'conv-4',
|
|
contactName: '招聘助手(美团)',
|
|
companyName: '美团',
|
|
avatar: '美',
|
|
lastMessage: '您有一条新的面试邀请待确认',
|
|
timestamp: '08:15',
|
|
unread: false,
|
|
matched: true,
|
|
},
|
|
];
|
|
|
|
/** 系统通知列表 */
|
|
const MOCK_NOTIFICATIONS: Notification[] = [
|
|
{
|
|
id: 'noti-1',
|
|
title: 'Offer 通知',
|
|
content: '恭喜!您收到了 2 个 Offer,请及时查看并处理',
|
|
timestamp: '10:20',
|
|
type: 'offer',
|
|
read: false,
|
|
},
|
|
{
|
|
id: 'noti-2',
|
|
title: '系统通知',
|
|
content: '您的简历已更新,更多企业将看到您的信息',
|
|
timestamp: '09:00',
|
|
type: 'system',
|
|
read: false,
|
|
},
|
|
{
|
|
id: 'noti-3',
|
|
title: '活动推广',
|
|
content: '本周六下午举办「求职面试技巧」线上讲座,欢迎报名',
|
|
timestamp: '昨天',
|
|
type: 'promotion',
|
|
read: true,
|
|
},
|
|
{
|
|
id: 'noti-4',
|
|
title: '系统通知',
|
|
content: '您的账号已通过企业认证',
|
|
timestamp: '周一',
|
|
type: 'system',
|
|
read: true,
|
|
},
|
|
];
|
|
|
|
/** 面试日程列表 */
|
|
const MOCK_INTERVIEWS: Interview[] = [
|
|
{
|
|
id: 'iv-1',
|
|
companyName: '字节跳动',
|
|
positionName: '前端开发终面',
|
|
time: '10:00',
|
|
type: 'video',
|
|
meetingUrl: 'https://meeting.example.com/abc123',
|
|
},
|
|
{
|
|
id: 'iv-2',
|
|
companyName: '腾讯科技',
|
|
positionName: '产品经理二面',
|
|
time: '14:30',
|
|
type: 'offline',
|
|
location: '深圳市南山区科技园南区',
|
|
},
|
|
];
|
|
|
|
/** 双向匹配结果 */
|
|
const MOCK_MATCH_RESULT: MatchResult = {
|
|
total: 3,
|
|
companies: ['字节跳动', '腾讯科技', '阿里巴巴'],
|
|
};
|
|
|
|
/** 霍兰德六维名称映射 */
|
|
const HOLLAND_NAMES: Record<HollandType, string> = {
|
|
R: '现实型',
|
|
I: '研究型',
|
|
A: '艺术型',
|
|
S: '社会型',
|
|
E: '企业型',
|
|
C: '常规型',
|
|
};
|
|
|
|
/** 霍兰德职业兴趣测评 */
|
|
const MOCK_HOLLAND: HollandAssessment = {
|
|
totalParticipants: 12846,
|
|
currentQuestionIndex: 11,
|
|
totalQuestions: 60,
|
|
question: {
|
|
id: 'hq-12',
|
|
order: 12,
|
|
text: '我喜欢动手操作机械和工具',
|
|
},
|
|
options: [
|
|
{ value: 1, label: '非常不喜欢' },
|
|
{ value: 2, label: '不喜欢' },
|
|
{ value: 3, label: '一般' },
|
|
{ value: 4, label: '喜欢' },
|
|
{ value: 5, label: '非常喜欢' },
|
|
],
|
|
dimensions: [
|
|
{ type: 'R', name: HOLLAND_NAMES.R, score: 72, percent: 72 },
|
|
{ type: 'I', name: HOLLAND_NAMES.I, score: 88, percent: 88 },
|
|
{ type: 'A', name: HOLLAND_NAMES.A, score: 45, percent: 45 },
|
|
{ type: 'S', name: HOLLAND_NAMES.S, score: 85, percent: 85 },
|
|
{ type: 'E', name: HOLLAND_NAMES.E, score: 60, percent: 60 },
|
|
{ type: 'C', name: HOLLAND_NAMES.C, score: 50, percent: 50 },
|
|
],
|
|
result: {
|
|
code: 'ISR',
|
|
name: '研究型 + 社会型 + 现实型',
|
|
dimensions: [
|
|
{ type: 'I', name: HOLLAND_NAMES.I, score: 88, percent: 88 },
|
|
{ type: 'S', name: HOLLAND_NAMES.S, score: 85, percent: 85 },
|
|
{ type: 'R', name: HOLLAND_NAMES.R, score: 72, percent: 72 },
|
|
{ type: 'E', name: HOLLAND_NAMES.E, score: 60, percent: 60 },
|
|
{ type: 'C', name: HOLLAND_NAMES.C, score: 50, percent: 50 },
|
|
{ type: 'A', name: HOLLAND_NAMES.A, score: 45, percent: 45 },
|
|
],
|
|
analyses: [
|
|
{
|
|
type: 'I',
|
|
name: HOLLAND_NAMES.I,
|
|
score: 88,
|
|
description: '你喜欢思考与分析,偏好独立研究、动手实验。适合技术类、研发类、数据分析类工作。',
|
|
careers: ['研发工程师', '数据分析师', '科学家'],
|
|
},
|
|
{
|
|
type: 'S',
|
|
name: HOLLAND_NAMES.S,
|
|
score: 85,
|
|
description: '你喜欢与人交流,乐于助人,关心他人感受。适合教育、培训、咨询、社工等岗位。',
|
|
careers: ['教师', '咨询师', 'HRBP'],
|
|
},
|
|
{
|
|
type: 'R',
|
|
name: HOLLAND_NAMES.R,
|
|
score: 72,
|
|
description: '你喜欢动手操作,注重实际效果,偏好具体任务。适合工程师、技术员、设计师等。',
|
|
careers: ['工程师', '技术员', '设计师'],
|
|
},
|
|
],
|
|
jobs: [
|
|
{
|
|
id: '1',
|
|
title: '数据分析师',
|
|
companyName: '字节跳动',
|
|
salary: '25-40K',
|
|
city: '北京',
|
|
tags: ['数据分析', 'SQL', 'Python'],
|
|
match: 95,
|
|
},
|
|
{
|
|
id: '2',
|
|
title: '前端开发工程师',
|
|
companyName: '腾讯科技',
|
|
salary: '22-35K',
|
|
city: '深圳',
|
|
tags: ['React', 'TypeScript'],
|
|
match: 92,
|
|
},
|
|
{
|
|
id: '3',
|
|
title: '产品经理',
|
|
companyName: '阿里巴巴',
|
|
salary: '20-35K',
|
|
city: '杭州',
|
|
tags: ['B端产品', 'SaaS'],
|
|
match: 88,
|
|
},
|
|
],
|
|
actions: [
|
|
{ id: 'a1', title: '完善技能图谱,精准匹配岗位', desc: '根据测评结果补充优势技能标签', icon: 'construct-outline', route: '/resume' },
|
|
{ id: 'a2', title: '优化简历,提升投递成功率', desc: '突出研究型与社会型项目经历', icon: 'document-text-outline', route: '/resume' },
|
|
{ id: 'a3', title: '查看行业新服务,合理定价', desc: '了解目标岗位市场薪资区间', icon: 'cash-outline', route: '/tools/salary-calculator' },
|
|
],
|
|
},
|
|
};
|
|
|
|
/** 四维能力测评(原始数据,不含 level;level 由接口层按总分计算) */
|
|
const MOCK_ABILITY: Omit<AbilityAssessment, 'level'> = {
|
|
overallScore: 86,
|
|
percentile: 78,
|
|
dimensions: [
|
|
{
|
|
label: '专业能力',
|
|
value: 88,
|
|
percentileText: '超过 80% 同岗位求职者',
|
|
subItems: [
|
|
{ id: 'pro-1', label: '核心技术掌握度', score: 90, maxScore: 100 },
|
|
{ id: 'pro-2', label: '工程化能力', score: 86, maxScore: 100 },
|
|
{ id: 'pro-3', label: '算法基础', score: 75, maxScore: 100 },
|
|
],
|
|
},
|
|
{
|
|
label: '项目经验',
|
|
value: 75,
|
|
percentileText: '超过 62% 同岗位求职者',
|
|
subItems: [
|
|
{ id: 'exp-1', label: '项目规模', score: 70, maxScore: 100 },
|
|
{ id: 'exp-2', label: '业务理解', score: 78, maxScore: 100 },
|
|
{ id: 'exp-3', label: '技术深度', score: 76, maxScore: 100 },
|
|
],
|
|
},
|
|
{
|
|
label: '技能广度',
|
|
value: 82,
|
|
percentileText: '超过 75% 同岗位求职者',
|
|
subItems: [
|
|
{ id: 'skill-1', label: '前端技术栈', score: 90, maxScore: 100 },
|
|
{ id: 'skill-2', label: '后端协作', score: 75, maxScore: 100 },
|
|
{ id: 'skill-3', label: '设计能力', score: 70, maxScore: 100 },
|
|
],
|
|
},
|
|
{
|
|
label: '成长潜力',
|
|
value: 90,
|
|
percentileText: '超过 85% 同岗位求职者',
|
|
subItems: [
|
|
{ id: 'grow-1', label: '学习敏锐度', score: 92, maxScore: 100 },
|
|
{ id: 'grow-2', label: '问题解决', score: 88, maxScore: 100 },
|
|
{ id: 'grow-3', label: '创新意识', score: 85, maxScore: 100 },
|
|
],
|
|
},
|
|
],
|
|
trends: [
|
|
{ month: '3月', professional: 68, project: 62, skill: 70, growth: 72 },
|
|
{ month: '4月', professional: 72, project: 65, skill: 74, growth: 76 },
|
|
{ month: '5月', professional: 76, project: 68, skill: 76, growth: 80 },
|
|
{ month: '6月', professional: 80, project: 70, skill: 78, growth: 82 },
|
|
{ month: '7月', professional: 84, project: 72, skill: 80, growth: 86 },
|
|
{ month: '8月', professional: 88, project: 75, skill: 82, growth: 90 },
|
|
],
|
|
improvements: [
|
|
{
|
|
id: 'imp-1',
|
|
title: '增加项目端到端经验',
|
|
description: '建议参与中大型项目全流程,提升技术深度和团队协作经验。',
|
|
icon: 'git-merge-outline',
|
|
route: '/game',
|
|
},
|
|
{
|
|
id: 'imp-2',
|
|
title: '夯实算法基础',
|
|
description: '每天刷 1-2 道算法题,重点关注数组、字符串与常见数据结构。',
|
|
icon: 'code-slash-outline',
|
|
route: '/game',
|
|
},
|
|
{
|
|
id: 'imp-3',
|
|
title: '补充跨端/全栈技能',
|
|
description: '学习 React Native 或 Node.js,拓展技能广度,提升全栈协作能力。',
|
|
icon: 'layers-outline',
|
|
route: '/game',
|
|
},
|
|
],
|
|
};
|
|
|
|
/** AI 求职工具 */
|
|
const MOCK_AI_TOOLS: AITool[] = [
|
|
{
|
|
id: 'ai-resume',
|
|
title: 'AI简历优化',
|
|
description: '一键优化,通过率提升80%',
|
|
action: '立即优化',
|
|
variant: 'primary',
|
|
icon: 'document-text-outline',
|
|
},
|
|
{
|
|
id: 'ai-interview',
|
|
title: 'AI模拟面试',
|
|
description: '真人感面试实战演练',
|
|
action: '开始练习',
|
|
variant: 'success',
|
|
icon: 'chatbubbles-outline',
|
|
},
|
|
];
|
|
|
|
/** 职场干货文章 */
|
|
const MOCK_ARTICLES: Article[] = [
|
|
{
|
|
id: 'art-1',
|
|
title: '如何写出让HR眼前一亮的简历',
|
|
tag: '职场攻略',
|
|
views: 23000,
|
|
thumbnailColor: 'violet',
|
|
icon: 'document-text-outline',
|
|
content: '简历往往是你与 HR 之间唯一的第一印象,而对方平均只花 6-10 秒扫读一份简历。核心原则只有三条:一页纸、结果导向、量化成就。\n\n先用「岗位关键词」反向定制:把 JD 里的高频能力词自然融入项目描述,让 ATS 筛选系统更容易命中。再套用 STAR 法则(情境-任务-行动-结果)拆解每段经历,例如「主导 X 项目,通过 Y 动作,带来 Z% 的留存提升」明显比「负责 X 项目」更有说服力。\n\n最后做减法:删掉与岗位无关的兴趣社团,把空间留给能证明你胜任的硬证据。有职「简历诊断」会基于行业评分模型给出逐项改进建议,投递前先自检一遍,能少走很多弯路。',
|
|
},
|
|
{
|
|
id: 'art-2',
|
|
title: '面试中最容易踩坑的10个问题',
|
|
tag: '面试技巧',
|
|
views: 18000,
|
|
thumbnailColor: 'orange',
|
|
icon: 'help-circle-outline',
|
|
content: '面试不是考试,而是一次双向匹配。最容易翻车的 10 个问题集中在:自我介绍、离职原因、期望薪资、职业规划、自身缺点、压力情境、对公司的了解、对前东家的评价、到岗时间,以及最后的「你还有什么想问的」。\n\n高频雷区有三个:把离职原因说成吐槽前公司、在薪资上先亮底牌、用「我没什么缺点」来回避成长话题。正确姿势是用「事实加反思」替代情绪,把每个问题都变成展示你方法论的机会。\n\n最后一题「你有什么想问的」最容易被忽略,其实它是你反向评估公司是否靠谱的关键窗口——问团队节奏、问岗位成功标准、问业务下一步,比问福利更有价值。有职 AI 模拟面试可针对你的目标岗位做专项陪练。',
|
|
},
|
|
{
|
|
id: 'art-3',
|
|
title: '2024年互联网行业薪资报告',
|
|
tag: '行业洞察',
|
|
views: 35000,
|
|
thumbnailColor: 'green',
|
|
icon: 'stats-chart-outline',
|
|
content: '薪资报告的价值不在数字本身,而在帮你建立谈薪的「锚点」与「区间」。2024 年互联网行业整体回归理性:中厂与垂直领域岗位增长稳定,纯补贴型高薪收缩,复合能力(业务加技术加沟通)溢价明显。\n\n看报告要学会三层拆解:一是按城市与年限看基线,二是按岗位序列看带宽(P25、P50、P75),三是评估自己的「可替代性」——越稀缺的能力,越能突破带宽上限。\n\n谈薪时不要只报一个数,给一个区间并说明依据(市场分位加你的独特价值),更容易落到上限。有职薪资计算器已内置城市与序列系数,可一键估算你的合理区间。',
|
|
},
|
|
];
|
|
|
|
/** 当前用户个人信息(我的页) */
|
|
const MOCK_USER_PROFILE: UserProfile = {
|
|
id: 'u-1',
|
|
name: '李明',
|
|
avatar: '李',
|
|
bio: '3年工作经验·本科·北京',
|
|
};
|
|
|
|
/** 用户统计数据(我的页四宫格) */
|
|
const MOCK_USER_STATS: UserStats = {
|
|
deliveries: 28,
|
|
favorites: 12,
|
|
chats: 8,
|
|
offers: 3,
|
|
};
|
|
|
|
/**
|
|
* 求职者端功能菜单
|
|
* 只含求职者相关功能;「企业入驻」作为切换到企业端的入口保留在此。
|
|
* 企业侧功能(发布岗位 / 岗位管理等)一律不在此下发,避免角色混淆。
|
|
*/
|
|
const MOCK_CANDIDATE_MENU_ITEMS: MenuItem[] = [
|
|
{ id: 'resume', title: '我的简历', icon: 'document-text-outline', route: '/resume' },
|
|
{ id: 'favorites', title: '我的收藏', icon: 'heart-outline', route: '/favorites' },
|
|
{ id: 'history', title: '浏览历史', icon: 'time-outline', route: '/history' },
|
|
{ id: 'compare', title: '岗位对比', icon: 'git-compare-outline', route: '/jobs/compare' },
|
|
{ id: 'offer-compare', title: 'Offer对比', icon: 'gift-outline', route: '/jobs/offer-compare' },
|
|
{ id: 'assessment', title: '我的测评', icon: 'bar-chart-outline', route: '/assessment' },
|
|
{ id: 'interview', title: 'AI模拟面试', icon: 'videocam-outline', route: '/interview' },
|
|
{ id: 'rank', title: '竞争力排名', icon: 'podium-outline', route: '/jobs/rank' },
|
|
{ id: 'hr-evaluation', title: 'HR评价详情', icon: 'ribbon-outline', route: '/hr-evaluation' },
|
|
{ id: 'onboarding', title: '入职准备清单', icon: 'checkmark-done-outline', route: '/onboarding' },
|
|
{ id: 'skill-cloud', title: '行业技能词云', icon: 'cloud-outline', route: '/tools/skill-cloud' },
|
|
{ id: 'salary', title: '薪资计算器', icon: 'calculator-outline', route: '/tools/salary-calculator' },
|
|
{ id: 'progress', title: '求职进度', icon: 'list-outline', route: '/progress' },
|
|
{ id: 'game', title: '通关小游戏', icon: 'trophy-outline', route: '/game' },
|
|
{ id: 'legal', title: '阿布阿诺法律服务', icon: 'shield-checkmark-outline', route: '/legal' },
|
|
{ id: 'enterprise-certification', title: '企业入驻', icon: 'business-outline', route: '/enterprise-certification' },
|
|
];
|
|
|
|
/**
|
|
* 企业端功能菜单
|
|
* 只含企业 / HR 相关功能,在企业端「我的 - 更多服务」下发。
|
|
* 不设「候选人详情」菜单入口:该页需要候选人 id,独立入口会落到兜底数据,
|
|
* 正确路径是「人才推荐 → 列表点击 → 带 id 进详情」。
|
|
* 运营规则配置 / 数据审计等后台项见 MOCK_ENTERPRISE_MANAGE_ACTIONS。
|
|
*/
|
|
const MOCK_ENTERPRISE_MENU_ITEMS: MenuItem[] = [
|
|
{ id: 'job-publish', title: '发布岗位', icon: 'add-circle-outline', route: '/job-publish' },
|
|
{ id: 'job-management', title: '岗位管理', icon: 'business-outline', route: '/job-management' },
|
|
{ id: 'job-evaluations', title: '岗位评价管理', icon: 'chatbubbles-outline', route: '/job-evaluations' },
|
|
{ id: 'talent-recommendation', title: '人才推荐', icon: 'people-outline', route: '/talent-recommendation' },
|
|
{ id: 'interview-score', title: '面试评分', icon: 'create-outline', route: '/interview-score' },
|
|
{ id: 'enterprise-chat', title: '企业沟通', icon: 'chatbubble-outline', route: '/enterprise-chat' },
|
|
];
|
|
|
|
/** 企业端入口 */
|
|
const MOCK_ENTERPRISE_ENTRY: EnterpriseEntry = {
|
|
title: '切换到企业端',
|
|
description: '如果你是HR或企业管理者,可以切换到企业端管理招聘',
|
|
action: '去企业端 →',
|
|
};
|
|
|
|
/** 综合评分 */
|
|
const MOCK_OVERALL_SCORE: OverallScore = {
|
|
score: 88,
|
|
level: '优秀',
|
|
rank: '超过85%的求职者',
|
|
};
|
|
|
|
/** 八大维度评分 */
|
|
const MOCK_DIMENSION_SCORES: DimensionScore[] = [
|
|
{ id: 'edu', name: '教育背景', score: 92, level: 'excellent' },
|
|
{ id: 'intern', name: '实习经历', score: 85, level: 'excellent' },
|
|
{ id: 'skill', name: '专业技能', score: 88, level: 'excellent' },
|
|
{ id: 'project', name: '项目经验', score: 78, level: 'good' },
|
|
{ id: 'cert', name: '证书资质', score: 72, level: 'good' },
|
|
{ id: 'stability', name: '工作稳定性', score: 90, level: 'excellent' },
|
|
{ id: 'integrity', name: '诚信记录', score: 95, level: 'excellent' },
|
|
{ id: 'salary', name: '薪资匹配度', score: 82, level: 'excellent' },
|
|
];
|
|
|
|
/** 求职偏好 */
|
|
const MOCK_JOB_PREFERENCE: JobPreference = {
|
|
position: '前端开发工程师',
|
|
city: '北京',
|
|
salary: '25-35K',
|
|
workType: '全职',
|
|
status: '积极求职中',
|
|
};
|
|
|
|
/** 勋章列表(已获得 + 未获得;未获得带获取进度) */
|
|
const MOCK_BADGES: Badge[] = [
|
|
{ id: 'badge-1', title: '求职达人', description: '投递满100次', icon: '🏅', obtained: true },
|
|
{ id: 'badge-2', title: '面试高手', description: '面试10次成功', icon: '🎯', obtained: true },
|
|
{ id: 'badge-3', title: '诚信之星', description: '诚信记录满分', icon: '⭐', obtained: true },
|
|
{ id: 'badge-4', title: 'offer收割机', description: '获得5个offer', icon: '🏆', obtained: false, progress: 3, target: 5 },
|
|
{ id: 'badge-5', title: '投递达人', description: '投递满200次', icon: '📮', obtained: false, progress: 128, target: 200 },
|
|
{ id: 'badge-6', title: '学习之星', description: '完成10次测评', icon: '📚', obtained: false, progress: 4, target: 10 },
|
|
];
|
|
|
|
/** 岗位详情附加/覆盖信息(id → 详情页专属字段;覆盖列表页未细化的字段) */
|
|
const MOCK_JOB_DETAILS: Record<string, Partial<JobDetail>> = {
|
|
'1': {
|
|
companyLocation: '北京 · 朝阳区',
|
|
industry: '互联网',
|
|
benefits: ['五险一金', '年终奖金', '弹性工作', '免费三餐'],
|
|
companySize: '10000人以上',
|
|
status: '刚发布',
|
|
views: 1286,
|
|
salaryMonths: 14,
|
|
},
|
|
};
|
|
|
|
/** 岗位综合评分(8 维度,供雷达图) */
|
|
const MOCK_JOB_OVERALL_SCORE: JobOverallScore = {
|
|
totalScore: 92,
|
|
level: '优秀',
|
|
rank: '超过91%同行业岗位',
|
|
dimensions: [
|
|
{ id: 'salary', name: '薪资竞争力', value: 92 },
|
|
{ id: 'growth', name: '发展空间', value: 90 },
|
|
{ id: 'atmosphere', name: '工作氛围', value: 86 },
|
|
{ id: 'benefit', name: '福利待遇', value: 84 },
|
|
{ id: 'team', name: '团队水平', value: 88 },
|
|
{ id: 'culture', name: '企业文化', value: 85 },
|
|
{ id: 'intensity', name: '工作强度', value: 78 },
|
|
{ id: 'authenticity', name: '岗位真实性', value: 95 },
|
|
],
|
|
};
|
|
|
|
/** 真实薪资区间 */
|
|
const MOCK_SALARY_DATA: SalaryData = {
|
|
ranges: [
|
|
{ experience: '1-3年', min: 15, max: 25 },
|
|
{ experience: '3-5年', min: 25, max: 40 },
|
|
{ experience: '5-10年', min: 40, max: 65 },
|
|
{ experience: '10年以上', min: 65, max: 100 },
|
|
],
|
|
dataSource: '基于2,846份真实简历数据',
|
|
totalCount: 2846,
|
|
};
|
|
|
|
/** 员工评价列表 */
|
|
const MOCK_EMPLOYEE_REVIEWS: EmployeeReview[] = [
|
|
{
|
|
id: 'r-1',
|
|
userName: '张**',
|
|
userRole: '在职员工,前端开发',
|
|
time: '3天前',
|
|
content: '团队氛围很好,技术栈比较新,成长空间大,就是加班有点多...',
|
|
tags: ['团队氛围好', '技术先进'],
|
|
},
|
|
{
|
|
id: 'r-2',
|
|
userName: '李**',
|
|
userRole: '离职员工,高级前端',
|
|
time: '2周前',
|
|
content: '薪资待遇不错,福利也还行,就是晋升通道不够清晰...',
|
|
tags: ['薪资高', '福利好'],
|
|
},
|
|
{
|
|
id: 'r-3',
|
|
userName: '王**',
|
|
userRole: '在职员工,技术主管',
|
|
time: '1个月前',
|
|
content: '公司文化很好,同事都很优秀,能学到很多东西...',
|
|
tags: ['文化好', '成长快'],
|
|
},
|
|
];
|
|
|
|
/** 岗位对比数据(3 个岗位:九大维度评分 + 基本信息 + 福利) */
|
|
const MOCK_COMPARE_DATA: CompareData = {
|
|
jobs: [
|
|
{ id: '1', title: '高级前端工程师', company: '字节跳动', salary: '25-40K', color: colors.primary },
|
|
{ id: '2', title: '产品经理', company: '腾讯科技', salary: '20-35K', color: colors.compareRed },
|
|
{ id: '3', title: 'UI设计师', company: '阿里巴巴', salary: '15-25K', color: colors.compareOrange },
|
|
],
|
|
dimensionScores: [
|
|
{ dimension: '综合评分', scores: [92, 85, 78] },
|
|
{ dimension: '薪资竞争力', scores: [88, 82, 70] },
|
|
{ dimension: '发展空间', scores: [90, 86, 75] },
|
|
{ dimension: '工作氛围', scores: [85, 88, 90] },
|
|
{ dimension: '福利待遇', scores: [86, 80, 72] },
|
|
{ dimension: '团队水平', scores: [91, 84, 76] },
|
|
{ dimension: '企业文化', scores: [84, 87, 80] },
|
|
{ dimension: '工作强度', scores: [72, 78, 85] },
|
|
{ dimension: '岗位真实性', scores: [95, 90, 88] },
|
|
],
|
|
basicInfo: {
|
|
fields: ['公司规模', '所在行业', '工作地点', '经验要求', '学历要求', '工作类型'],
|
|
values: [
|
|
['10000人以上', '10000人以上', '10000人以上'],
|
|
['互联网/IT', '互联网/IT', '互联网/IT'],
|
|
['北京', '深圳', '杭州'],
|
|
['3-5年', '3-5年', '3-5年'],
|
|
['本科', '本科', '本科'],
|
|
['全职', '全职', '全职'],
|
|
],
|
|
},
|
|
benefits: [
|
|
['五险一金', '年终奖金', '弹性工作', '免费三餐'],
|
|
['五险一金', '年终奖金', '弹性工作', '股票期权'],
|
|
['五险一金', '年终奖金', '弹性工作'],
|
|
],
|
|
};
|
|
|
|
/* ===================== 业务规则 ===================== */
|
|
|
|
/** 分数 → 等级映射(业务规则,接口层计算) */
|
|
function scoreToLevel(score: number): JobLevel {
|
|
if (score >= 85) return 'excellent';
|
|
if (score >= 70) return 'good';
|
|
return 'medium';
|
|
}
|
|
|
|
/** 能力总分 → 等级映射(能力测评阈值高于岗位匹配:≥90 优秀 / ≥75 良好) */
|
|
function abilityScoreToLevel(score: number): JobLevel {
|
|
if (score >= 90) return 'excellent';
|
|
if (score >= 75) return 'good';
|
|
return 'medium';
|
|
}
|
|
|
|
/* ===================== 接口函数 ===================== */
|
|
|
|
/** 模拟请求(用 setTimeout 模拟网络延迟;真实后端就绪后替换为 fetch) */
|
|
function mockRequest<T>(data: T): Promise<T> {
|
|
return new Promise((resolve) => setTimeout(() => resolve(data), MOCK_DELAY));
|
|
}
|
|
|
|
/**
|
|
* 获取岗位列表(含排序/筛选/搜索/分页,等级由分数在接口层计算)
|
|
* @method GET
|
|
* @url /api/jobs
|
|
* @param params { JobListParams } 排序/筛选/搜索/分页参数
|
|
* @returns Promise<{ jobs: Job[]; total: number }> 分页后的岗位数组与总数
|
|
*/
|
|
export async function fetchJobList(
|
|
params: JobListParams,
|
|
): Promise<{ jobs: Job[]; total: number }> {
|
|
// TODO: 真实请求(后端就绪后替换)
|
|
// const query = new URLSearchParams({ sortBy: params.sortBy, page: String(params.page ?? 1) });
|
|
// const res = await fetch(`${API_BASE_URL}/api/jobs?${query}`, { method: 'GET' });
|
|
// return res.json();
|
|
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
// 1. 从 mock 数据源复制
|
|
let result: Omit<Job, 'level'>[] = [...MOCK_JOBS];
|
|
|
|
// 2. 关键词搜索(岗位名 / 公司名)
|
|
if (params.keyword && params.keyword.trim() !== '') {
|
|
const keyword = params.keyword.trim().toLowerCase();
|
|
result = result.filter(
|
|
(job) =>
|
|
job.positionName.toLowerCase().includes(keyword) ||
|
|
job.companyName.toLowerCase().includes(keyword),
|
|
);
|
|
}
|
|
|
|
// 3. 筛选:行业
|
|
if (params.industry && params.industry !== '全部行业') {
|
|
result = result.filter((job) => job.industry === params.industry);
|
|
}
|
|
|
|
// 4. 筛选:城市
|
|
if (params.city && params.city !== '全部城市') {
|
|
const city = params.city;
|
|
result = result.filter((job) => job.city.includes(city));
|
|
}
|
|
|
|
// 筛选:薪资范围(岗位薪资区间与任一所选区间有交集即命中)
|
|
if (params.salary && params.salary.length > 0) {
|
|
const salary = params.salary;
|
|
result = result.filter((job) =>
|
|
salary.some((label) => {
|
|
const range = SALARY_RANGES.find((item) => item.label === label);
|
|
return range ? job.salaryMin <= range.max && job.salaryMax >= range.min : false;
|
|
}),
|
|
);
|
|
}
|
|
|
|
// 筛选:经验要求(多选,命中任一即保留)
|
|
if (params.experience && params.experience.length > 0) {
|
|
const experience = params.experience;
|
|
result = result.filter((job) => experience.includes(job.experience));
|
|
}
|
|
|
|
// 筛选:学历要求(多选,命中任一即保留)
|
|
if (params.education && params.education.length > 0) {
|
|
const education = params.education;
|
|
result = result.filter((job) => education.includes(job.education));
|
|
}
|
|
|
|
// 5. 排序
|
|
switch (params.sortBy) {
|
|
case 'salary':
|
|
result.sort((a, b) => b.salaryMax - a.salaryMax);
|
|
break;
|
|
case 'latest':
|
|
result.sort((a, b) => parseInt(b.id, 10) - parseInt(a.id, 10));
|
|
break;
|
|
case 'comprehensive':
|
|
default:
|
|
result.sort((a, b) => b.score - a.score);
|
|
break;
|
|
}
|
|
|
|
// 6. 计算总数
|
|
const total = result.length;
|
|
|
|
// 7. 分页
|
|
const page = params.page ?? 1;
|
|
const pageSize = params.pageSize ?? 10;
|
|
const startIndex = (page - 1) * pageSize;
|
|
const endIndex = Math.min(startIndex + pageSize, total);
|
|
const paginatedJobs = result.slice(startIndex, endIndex);
|
|
|
|
// 8. 等级映射(业务规则)
|
|
const jobs: Job[] = paginatedJobs.map((job) => ({
|
|
...job,
|
|
level: scoreToLevel(job.score),
|
|
}));
|
|
|
|
resolve({ jobs, total });
|
|
}, MOCK_DELAY);
|
|
});
|
|
}
|
|
|
|
/* ===================== 我的收藏 / 浏览历史(mock 数据) ===================== */
|
|
|
|
/** 收藏的职位 id 列表(mock 阶段取 MOCK_JOBS 子集) */
|
|
const MOCK_FAVORITE_IDS = ['1', '3', '6'];
|
|
/** 浏览历史的职位 id 列表(mock 阶段取 MOCK_JOBS 子集) */
|
|
const MOCK_HISTORY_IDS = ['2', '4', '5', '7'];
|
|
|
|
const MOCK_FAVORITES: Omit<Job, 'level'>[] = MOCK_JOBS.filter((job) =>
|
|
MOCK_FAVORITE_IDS.includes(job.id),
|
|
);
|
|
const MOCK_HISTORY: Omit<Job, 'level'>[] = MOCK_JOBS.filter((job) =>
|
|
MOCK_HISTORY_IDS.includes(job.id),
|
|
);
|
|
|
|
/**
|
|
* 获取我的收藏(职位)列表
|
|
* @method GET
|
|
* @url /api/user/favorites
|
|
* @returns Promise<Omit<Job, 'level'>[]> 收藏的职位数组
|
|
*/
|
|
export async function fetchFavorites(): Promise<Omit<Job, 'level'>[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/favorites`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_FAVORITES);
|
|
}
|
|
|
|
/**
|
|
* 获取浏览历史(职位)列表
|
|
* @method GET
|
|
* @url /api/user/history
|
|
* @returns Promise<Omit<Job, 'level'>[]> 浏览过的职位数组(按时间倒序)
|
|
*/
|
|
export async function fetchHistory(): Promise<Omit<Job, 'level'>[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/history`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_HISTORY);
|
|
}
|
|
|
|
/**
|
|
* 获取会话列表
|
|
* @method GET
|
|
* @url /api/messages/conversations
|
|
* @returns Promise<Conversation[]> 会话数组
|
|
*/
|
|
export async function fetchConversations(): Promise<Conversation[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/messages/conversations`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_CONVERSATIONS);
|
|
}
|
|
|
|
/**
|
|
* 获取系统通知列表
|
|
* @method GET
|
|
* @url /api/messages/notifications
|
|
* @returns Promise<Notification[]> 通知数组
|
|
*/
|
|
export async function fetchNotifications(): Promise<Notification[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/messages/notifications`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_NOTIFICATIONS);
|
|
}
|
|
|
|
/**
|
|
* 获取面试日程列表
|
|
* @method GET
|
|
* @url /api/messages/interviews
|
|
* @returns Promise<Interview[]> 面试日程数组
|
|
*/
|
|
export async function fetchInterviews(): Promise<Interview[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/messages/interviews`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_INTERVIEWS);
|
|
}
|
|
|
|
/**
|
|
* 获取双向匹配结果
|
|
* @method GET
|
|
* @url /api/messages/match-result
|
|
* @returns Promise<MatchResult> 匹配结果
|
|
*/
|
|
export async function fetchMatchResult(): Promise<MatchResult> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/messages/match-result`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_MATCH_RESULT);
|
|
}
|
|
|
|
/**
|
|
* 获取霍兰德职业兴趣测评信息
|
|
* @method GET
|
|
* @url /api/growth/holland
|
|
* @returns Promise<HollandAssessment> 测评参与信息
|
|
*/
|
|
export async function fetchHollandAssessment(): Promise<HollandAssessment> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/growth/holland`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_HOLLAND);
|
|
}
|
|
|
|
/**
|
|
* 提交霍兰德测评答案(mock 阶段返回下一题或最终结果)
|
|
* @method POST
|
|
* @url /api/growth/holland/answers
|
|
* @param questionId {string} 题目 id
|
|
* @param value {number} 选项分值
|
|
* @returns Promise<HollandAssessment> 更新后的测评进度或结果
|
|
*/
|
|
export async function submitHollandAnswer(
|
|
_questionId: string,
|
|
_value: number,
|
|
): Promise<HollandAssessment> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/growth/holland/answers`, { method: 'POST', body: JSON.stringify({ questionId, value }) }); return res.json();
|
|
return mockRequest(MOCK_HOLLAND);
|
|
}
|
|
|
|
/**
|
|
* 获取霍兰德测评结果
|
|
* @method GET
|
|
* @url /api/growth/holland/result
|
|
* @returns Promise<NonNullable<HollandAssessment['result']>> 测评结果
|
|
*/
|
|
export async function fetchHollandResult(): Promise<NonNullable<HollandAssessment['result']>> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/growth/holland/result`, { method: 'GET' }); return res.json();
|
|
if (!MOCK_HOLLAND.result) {
|
|
throw new Error('暂无测评结果');
|
|
}
|
|
return mockRequest(MOCK_HOLLAND.result);
|
|
}
|
|
|
|
/**
|
|
* 获取四维能力测评结果(等级由总分在接口层计算)
|
|
* @method GET
|
|
* @url /api/growth/ability
|
|
* @returns Promise<AbilityAssessment> 能力测评结果
|
|
*/
|
|
export async function fetchAbilityAssessment(): Promise<AbilityAssessment> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/growth/ability`, { method: 'GET' }); return res.json();
|
|
return mockRequest({
|
|
...MOCK_ABILITY,
|
|
level: abilityScoreToLevel(MOCK_ABILITY.overallScore),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 获取 AI 求职工具列表
|
|
* @method GET
|
|
* @url /api/growth/ai-tools
|
|
* @returns Promise<AITool[]> 工具数组
|
|
*/
|
|
export async function fetchAITools(): Promise<AITool[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/growth/ai-tools`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_AI_TOOLS);
|
|
}
|
|
|
|
/**
|
|
* 获取职场干货文章列表
|
|
* @method GET
|
|
* @url /api/growth/articles
|
|
* @returns Promise<Article[]> 文章数组
|
|
*/
|
|
export async function fetchArticles(): Promise<Article[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/growth/articles`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_ARTICLES);
|
|
}
|
|
|
|
/**
|
|
* 获取当前用户个人信息(我的页)
|
|
* @method GET
|
|
* @url /api/user/me
|
|
* @returns Promise<UserProfile> 用户个人信息
|
|
*/
|
|
export async function fetchUserProfile(): Promise<UserProfile> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/me`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_USER_PROFILE);
|
|
}
|
|
|
|
/**
|
|
* 获取用户统计数据(投递/收藏/沟通/Offer)
|
|
* @method GET
|
|
* @url /api/user/stats
|
|
* @returns Promise<UserStats> 用户统计数据
|
|
*/
|
|
export async function fetchUserStats(): Promise<UserStats> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/stats`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_USER_STATS);
|
|
}
|
|
|
|
/**
|
|
* 获取个人中心功能菜单列表
|
|
* @method GET
|
|
* @url /api/user/menus
|
|
* @param role { AppRole } 当前角色(端),按角色下发不同菜单,避免求职者端出现企业功能、反之亦然
|
|
* @returns Promise<MenuItem[]> 菜单数组
|
|
*/
|
|
export async function fetchMenuItems(role: AppRole = 'candidate'): Promise<MenuItem[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/menus?role=${role}`, { method: 'GET' }); return res.json();
|
|
const items = role === 'enterprise' ? MOCK_ENTERPRISE_MENU_ITEMS : MOCK_CANDIDATE_MENU_ITEMS;
|
|
return mockRequest(items);
|
|
}
|
|
|
|
/**
|
|
* 获取企业端入口信息
|
|
* @method GET
|
|
* @url /api/user/enterprise-entry
|
|
* @returns Promise<EnterpriseEntry> 企业端入口
|
|
*/
|
|
export async function fetchEnterpriseEntry(): Promise<EnterpriseEntry> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/enterprise-entry`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_ENTERPRISE_ENTRY);
|
|
}
|
|
|
|
/**
|
|
* 获取综合评分
|
|
* @method GET
|
|
* @url /api/user/overall-score
|
|
* @returns Promise<OverallScore> 综合评分
|
|
*/
|
|
export async function fetchOverallScore(): Promise<OverallScore> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/overall-score`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_OVERALL_SCORE);
|
|
}
|
|
|
|
/**
|
|
* 获取八大维度评分
|
|
* @method GET
|
|
* @url /api/user/dimension-scores
|
|
* @returns Promise<DimensionScore[]> 维度评分数组
|
|
*/
|
|
export async function fetchDimensionScores(): Promise<DimensionScore[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/dimension-scores`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_DIMENSION_SCORES);
|
|
}
|
|
|
|
/**
|
|
* 获取求职偏好
|
|
* @method GET
|
|
* @url /api/user/job-preference
|
|
* @returns Promise<JobPreference> 求职偏好
|
|
*/
|
|
export async function fetchJobPreference(): Promise<JobPreference> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/job-preference`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_JOB_PREFERENCE);
|
|
}
|
|
|
|
/**
|
|
* 获取勋章列表
|
|
* @method GET
|
|
* @url /api/user/badges
|
|
* @returns Promise<Badge[]> 勋章数组
|
|
*/
|
|
export async function fetchBadges(): Promise<Badge[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/user/badges`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_BADGES);
|
|
}
|
|
|
|
/**
|
|
* 获取岗位详情(含详情页专属字段;等级由分数在接口层计算)
|
|
* @method GET
|
|
* @url /api/jobs/:id
|
|
* @param id { string } 岗位 id
|
|
* @returns Promise<JobDetail> 岗位详情
|
|
*/
|
|
export async function fetchJobDetail(id: string): Promise<JobDetail> {
|
|
// TODO: 真实请求(后端就绪后替换)
|
|
// const res = await fetch(`${API_BASE_URL}/api/jobs/${id}`, { method: 'GET' });
|
|
// return res.json();
|
|
|
|
const base = MOCK_JOBS.find((job) => job.id === id);
|
|
if (!base) throw new Error(`岗位不存在: ${id}`);
|
|
|
|
// 详情 = 列表基础数据 + 详情页专属字段(无对应覆盖时用兜底值)
|
|
const detail: JobDetail = {
|
|
...base,
|
|
level: scoreToLevel(base.score),
|
|
companySize: '1000-9999人',
|
|
status: '热招中',
|
|
views: 0,
|
|
salaryMonths: 13,
|
|
...MOCK_JOB_DETAILS[id],
|
|
};
|
|
return mockRequest(detail);
|
|
}
|
|
|
|
/**
|
|
* 获取岗位综合评分(含 8 维度雷达图数据)
|
|
* @method GET
|
|
* @url /api/jobs/:id/score
|
|
* @param id { string } 岗位 id
|
|
* @returns Promise<JobOverallScore> 综合评分
|
|
*/
|
|
export async function fetchJobOverallScore(id: string): Promise<JobOverallScore> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/jobs/${id}/score`, { method: 'GET' }); return res.json();
|
|
void id;
|
|
return mockRequest(MOCK_JOB_OVERALL_SCORE);
|
|
}
|
|
|
|
/**
|
|
* 获取岗位真实薪资区间
|
|
* @method GET
|
|
* @url /api/jobs/:id/salary
|
|
* @param id { string } 岗位 id
|
|
* @returns Promise<SalaryData> 薪资数据
|
|
*/
|
|
export async function fetchSalaryData(id: string): Promise<SalaryData> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/jobs/${id}/salary`, { method: 'GET' }); return res.json();
|
|
void id;
|
|
return mockRequest(MOCK_SALARY_DATA);
|
|
}
|
|
|
|
/**
|
|
* 获取员工评价列表
|
|
* @method GET
|
|
* @url /api/jobs/:id/reviews
|
|
* @param id { string } 岗位 id
|
|
* @returns Promise<EmployeeReview[]> 评价数组
|
|
*/
|
|
export async function fetchEmployeeReviews(id: string): Promise<EmployeeReview[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/jobs/${id}/reviews`, { method: 'GET' }); return res.json();
|
|
// mock 阶段:内置评价 + 该岗位下本人已发布的评价(本人发布的最新在前)
|
|
const mine = (await readJobReviews()).filter((r) => r.jobId === id);
|
|
return mockRequest([...mine, ...MOCK_EMPLOYEE_REVIEWS]);
|
|
}
|
|
|
|
/* ===================== 岗位评价发布(mock 阶段持久化到 AsyncStorage) ===================== */
|
|
|
|
const JOB_REVIEWS_KEY = 'job:reviews';
|
|
|
|
/** 本人发布评价的存储结构:在 EmployeeReview 上补充归属岗位 */
|
|
interface StoredJobReview extends EmployeeReview {
|
|
jobId: string;
|
|
}
|
|
|
|
/** 读取本人发布的全部评价 */
|
|
async function readJobReviews(): Promise<StoredJobReview[]> {
|
|
const raw = await storageGet(JOB_REVIEWS_KEY);
|
|
return raw ? (JSON.parse(raw) as StoredJobReview[]) : [];
|
|
}
|
|
|
|
/**
|
|
* 发布岗位评价
|
|
* @method POST
|
|
* @url /api/jobs/:id/reviews
|
|
* @param id 岗位 id
|
|
* @param content 评价内容
|
|
* @returns Promise<EmployeeReview> 新建的评价
|
|
* @remark 真实后端下需做敏感词校验与重复提交限制,此处不做
|
|
*/
|
|
export async function createJobReview(id: string, content: string): Promise<EmployeeReview> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/jobs/${id}/reviews`, { method: 'POST', body: JSON.stringify({ content }) }); return res.json();
|
|
const review: StoredJobReview = {
|
|
id: `my-review-${Date.now()}`,
|
|
jobId: id,
|
|
userName: '我',
|
|
userRole: '我的评价',
|
|
time: '刚刚',
|
|
content,
|
|
tags: [],
|
|
};
|
|
const all = await readJobReviews();
|
|
all.unshift(review);
|
|
await storageSet(JOB_REVIEWS_KEY, JSON.stringify(all));
|
|
return mockRequest(review);
|
|
}
|
|
|
|
/**
|
|
* 获取岗位对比数据(按 jobIds 过滤九大维度评分 + 基本信息 + 福利)
|
|
* @method GET
|
|
* @url /api/jobs/compare
|
|
* @param jobIds { string[] } 参与对比的岗位 id 数组
|
|
* @returns Promise<CompareData> 岗位对比数据
|
|
*/
|
|
export async function fetchCompareData(jobIds: string[]): Promise<CompareData> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/jobs/compare?jobIds=${jobIds.join(',')}`, { method: 'GET' }); return res.json();
|
|
|
|
// 按传入 jobIds 过滤对比数据(无匹配时兜底返回全部,避免空页面)
|
|
const selected = MOCK_COMPARE_DATA.jobs
|
|
.map((job, index) => (jobIds.includes(job.id) ? index : -1))
|
|
.filter((index) => index >= 0);
|
|
const indices =
|
|
selected.length > 0 ? selected : MOCK_COMPARE_DATA.jobs.map((_, index) => index);
|
|
|
|
const data: CompareData = {
|
|
jobs: indices.map((index) => MOCK_COMPARE_DATA.jobs[index]),
|
|
dimensionScores: MOCK_COMPARE_DATA.dimensionScores.map((dim) => ({
|
|
dimension: dim.dimension,
|
|
scores: indices.map((index) => dim.scores[index]),
|
|
})),
|
|
basicInfo: {
|
|
fields: MOCK_COMPARE_DATA.basicInfo.fields,
|
|
values: MOCK_COMPARE_DATA.basicInfo.values.map((row) => indices.map((index) => row[index])),
|
|
},
|
|
benefits: indices.map((index) => MOCK_COMPARE_DATA.benefits[index]),
|
|
};
|
|
|
|
return mockRequest(data);
|
|
}
|
|
|
|
/* ===================== Offer 对比 ===================== */
|
|
|
|
/** Offer 对比 mock 数据(字节跳动 vs 腾讯科技) */
|
|
const MOCK_OFFER_COMPARE_DATA: OfferCompareData = {
|
|
offers: [
|
|
{
|
|
id: 'offer-1',
|
|
company: '字节跳动',
|
|
position: '高级前端',
|
|
salary: '25-40K',
|
|
selected: true,
|
|
color: colors.primary,
|
|
},
|
|
{
|
|
id: 'offer-2',
|
|
company: '腾讯科技',
|
|
position: '产品经理',
|
|
salary: '20-35K',
|
|
selected: true,
|
|
color: colors.compareRed,
|
|
},
|
|
],
|
|
scores: [
|
|
{ score: 92, level: '优秀', stars: 5 },
|
|
{ score: 85, level: '良好', stars: 4 },
|
|
],
|
|
benefitGroups: [
|
|
{
|
|
title: '薪资',
|
|
rows: [
|
|
{ label: '基本工资', values: ['25K', '22K'], highlights: [true, false] },
|
|
{ label: '年终奖', values: ['2-4个月', '3-6个月'] },
|
|
{ label: '股票期权', values: ['有(RSU)', '有(期权)'] },
|
|
{ label: '签字费', values: ['5万', '3万'], highlights: [true, false] },
|
|
{ label: '年度调薪', values: ['有', '有'] },
|
|
],
|
|
},
|
|
{
|
|
title: '福利',
|
|
rows: [
|
|
{ label: '五险一金', values: ['全额', '全额'] },
|
|
{ label: '补充医疗', values: ['有', '有'] },
|
|
{ label: '带薪年假', values: ['15天', '18天'], highlights: [false, true] },
|
|
{ label: '免费三餐', values: ['有', '有'] },
|
|
{ label: '健身房', values: ['有', '有'] },
|
|
{ label: '住房补贴', values: ['有(1500/月)', '无'], highlights: [true, false] },
|
|
],
|
|
},
|
|
{
|
|
title: '发展',
|
|
rows: [
|
|
{ label: '晋升通道', values: ['技术/管理双线', '技术为主'], highlights: [true, false] },
|
|
{ label: '培训体系', values: ['完善', '完善'] },
|
|
{ label: '转岗机会', values: ['多', '一般'], highlights: [true, false] },
|
|
],
|
|
},
|
|
],
|
|
workLifeBalance: [
|
|
{ label: '平均工时', values: ['9.5小时', '10.2小时'], highlights: [true, false] },
|
|
{ label: '出差频率', values: ['经常', '偶尔'] },
|
|
{ label: '周末加班', values: ['大小周', '双休'], highlights: [false, true] },
|
|
],
|
|
aiSuggestion: {
|
|
summary: '综合薪资、福利、发展空间等多维度评估,字节跳动的 Offer 综合评分更高。',
|
|
detail:
|
|
'但腾讯的工作生活平衡更好,建议根据个人职业规划选择。若追求短期收入与快速成长,字节更优;若看重工作节奏与长期稳定,腾讯更合适。',
|
|
reportAction: '查看完整分析报告',
|
|
},
|
|
actions: {
|
|
exportLabel: '导出对比报告',
|
|
acceptLabel: '接受字节Offer',
|
|
acceptOfferId: 'offer-1',
|
|
},
|
|
};
|
|
|
|
/**
|
|
* 获取 Offer 对比数据
|
|
* @method GET
|
|
* @url /api/offers/compare
|
|
* @param offerIds { string[] } 参与对比的 Offer id 数组
|
|
* @returns Promise<OfferCompareData> Offer 对比数据
|
|
*/
|
|
export async function fetchOfferCompareData(offerIds?: string[]): Promise<OfferCompareData> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/offers/compare?offerIds=${offerIds?.join(',') ?? ''}`, { method: 'GET' }); return res.json();
|
|
|
|
if (!offerIds || offerIds.length === 0) {
|
|
return mockRequest(MOCK_OFFER_COMPARE_DATA);
|
|
}
|
|
|
|
const filteredOffers = MOCK_OFFER_COMPARE_DATA.offers.filter((o) => offerIds.includes(o.id));
|
|
const indices = MOCK_OFFER_COMPARE_DATA.offers
|
|
.map((o, index) => (offerIds.includes(o.id) ? index : -1))
|
|
.filter((index) => index >= 0);
|
|
|
|
const data: OfferCompareData = {
|
|
offers: filteredOffers.length > 0 ? filteredOffers : MOCK_OFFER_COMPARE_DATA.offers,
|
|
scores: indices.length > 0 ? indices.map((i) => MOCK_OFFER_COMPARE_DATA.scores[i]) : MOCK_OFFER_COMPARE_DATA.scores,
|
|
benefitGroups: MOCK_OFFER_COMPARE_DATA.benefitGroups.map((group) => ({
|
|
title: group.title,
|
|
rows: group.rows.map((row) => ({
|
|
label: row.label,
|
|
values: indices.length > 0 ? indices.map((i) => row.values[i]) : row.values,
|
|
highlights: row.highlights ? (indices.length > 0 ? indices.map((i) => row.highlights![i]) : row.highlights) : undefined,
|
|
})),
|
|
})),
|
|
workLifeBalance: MOCK_OFFER_COMPARE_DATA.workLifeBalance.map((row) => ({
|
|
label: row.label,
|
|
values: indices.length > 0 ? indices.map((i) => row.values[i]) : row.values,
|
|
highlights: row.highlights ? (indices.length > 0 ? indices.map((i) => row.highlights![i]) : row.highlights) : undefined,
|
|
})),
|
|
aiSuggestion: MOCK_OFFER_COMPARE_DATA.aiSuggestion,
|
|
actions: MOCK_OFFER_COMPARE_DATA.actions,
|
|
};
|
|
|
|
return mockRequest(data);
|
|
}
|
|
|
|
/* ===================== 评分权重设置 ===================== */
|
|
|
|
/** 默认权重维度(9 个,总和 100%) */
|
|
const MOCK_WEIGHT_DIMENSIONS: WeightDimension[] = [
|
|
{ id: 'salary', name: '薪资竞争力', weight: 20 },
|
|
{ id: 'growth', name: '发展空间', weight: 15 },
|
|
{ id: 'atmosphere', name: '工作氛围', weight: 12 },
|
|
{ id: 'benefit', name: '福利待遇', weight: 12 },
|
|
{ id: 'team', name: '团队水平', weight: 10 },
|
|
{ id: 'culture', name: '企业文化', weight: 10 },
|
|
{ id: 'intensity', name: '工作强度', weight: 8, isReversed: true },
|
|
{ id: 'location', name: '地理位置', weight: 8 },
|
|
{ id: 'authenticity', name: '岗位真实性', weight: 5 },
|
|
];
|
|
|
|
/** 预设权重模板(3 个,各维度权重按维度名称匹配) */
|
|
const MOCK_WEIGHT_TEMPLATES: WeightTemplate[] = [
|
|
{
|
|
id: 'salary-first',
|
|
name: '薪资优先',
|
|
description: '薪资30% · 发展15%',
|
|
weights: {
|
|
薪资竞争力: 30,
|
|
发展空间: 15,
|
|
工作氛围: 10,
|
|
福利待遇: 12,
|
|
团队水平: 10,
|
|
企业文化: 8,
|
|
工作强度: 5,
|
|
地理位置: 5,
|
|
岗位真实性: 5,
|
|
},
|
|
},
|
|
{
|
|
id: 'growth-first',
|
|
name: '成长优先',
|
|
description: '发展30% · 团队15%',
|
|
weights: {
|
|
薪资竞争力: 12,
|
|
发展空间: 30,
|
|
工作氛围: 10,
|
|
福利待遇: 10,
|
|
团队水平: 15,
|
|
企业文化: 8,
|
|
工作强度: 5,
|
|
地理位置: 5,
|
|
岗位真实性: 5,
|
|
},
|
|
},
|
|
{
|
|
id: 'stable-first',
|
|
name: '稳定优先',
|
|
description: '福利25%',
|
|
weights: {
|
|
薪资竞争力: 12,
|
|
发展空间: 12,
|
|
工作氛围: 12,
|
|
福利待遇: 25,
|
|
团队水平: 8,
|
|
企业文化: 10,
|
|
工作强度: 6,
|
|
地理位置: 10,
|
|
岗位真实性: 5,
|
|
},
|
|
},
|
|
];
|
|
|
|
/**
|
|
* 获取评分权重设置(含维度 + 预设模板)
|
|
* @method GET
|
|
* @url /api/jobs/weight-settings
|
|
* @returns Promise<WeightSettings> 权重设置对象
|
|
*/
|
|
export async function fetchWeightSettings(): Promise<WeightSettings> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/jobs/weight-settings`, { method: 'GET' }); return res.json();
|
|
return mockRequest({
|
|
dimensions: MOCK_WEIGHT_DIMENSIONS.map((d) => ({ ...d })),
|
|
templates: MOCK_WEIGHT_TEMPLATES,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 保存评分权重设置(各维度权重总和应为 100%)
|
|
* @method POST
|
|
* @url /api/jobs/weight-settings
|
|
* @param dimensions { WeightDimension[] } 完整维度权重列表
|
|
* @returns Promise<WeightDimension[]> 保存后的维度列表
|
|
*/
|
|
export async function saveWeightSettings(
|
|
dimensions: WeightDimension[],
|
|
): Promise<WeightDimension[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/jobs/weight-settings`, {
|
|
// method: 'POST',
|
|
// headers: { 'Content-Type': 'application/json' },
|
|
// body: JSON.stringify({ dimensions }),
|
|
// });
|
|
// return res.json();
|
|
|
|
return mockRequest(dimensions.map((d) => ({ ...d })));
|
|
}
|
|
|
|
/* ===================== 同岗位竞争力排名 ===================== */
|
|
|
|
/**
|
|
* 获取同岗位竞争力排名数据(综合排名 + 六维度百分位 + TOP10 候选人 + 提升建议)
|
|
* @method GET
|
|
* @url /api/jobs/rank
|
|
* @param jobId { string } 岗位 id
|
|
* @returns Promise<RankOverview> 排名页完整数据(含数据态与空状态)
|
|
*/
|
|
export async function fetchRankData(jobId: string): Promise<RankOverview> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/jobs/rank?jobId=${jobId}`, { method: 'GET' }); return res.json();
|
|
// 当前为 mock 阶段,jobId 仅用于标注真实接口入参,避免未使用参数告警
|
|
void jobId;
|
|
return mockRequest({
|
|
hasData: true,
|
|
rankData: {
|
|
rank: 127,
|
|
jobTitle: '高级前端工程师',
|
|
company: '字节跳动',
|
|
totalCandidates: 1286,
|
|
percentile: 90.1,
|
|
overallScore: 88,
|
|
scoreLevel: '优秀',
|
|
rankPosition: '前 9.9%',
|
|
},
|
|
dimensions: [
|
|
{ id: 'skill', name: '专业技能', percentile: '前 15%', score: 92 },
|
|
{ id: 'experience', name: '工作经历', percentile: '前 22%', score: 86 },
|
|
{ id: 'education', name: '教育背景', percentile: '前 30%', score: 80 },
|
|
{ id: 'project', name: '项目经验', percentile: '前 12%', score: 94 },
|
|
{ id: 'match', name: '技能匹配', percentile: '前 18%', score: 89 },
|
|
{ id: 'completeness', name: '简历完整度', percentile: '前 25%', score: 84 },
|
|
],
|
|
candidates: [
|
|
{ id: 'c1', name: '候选人 A', score: 96, tags: ['清华硕士', '5 年大厂'] },
|
|
{ id: 'c2', name: '候选人 B', score: 93, tags: ['浙大本科', '3 年独角兽'] },
|
|
{ id: 'c3', name: '候选人 C', score: 91, tags: ['海归硕士', '4 年外企'] },
|
|
],
|
|
tips: [
|
|
{
|
|
id: 't1',
|
|
title: '补充项目经历可提升 5-8 个百分位',
|
|
description: '完善你的项目经历描述,突出技术选型与业务成果',
|
|
type: 'action',
|
|
},
|
|
{
|
|
id: 't2',
|
|
title: '添加技能标签提升匹配度',
|
|
description: '补充 TypeScript、性能优化等岗位高频技能关键词',
|
|
type: 'tip',
|
|
},
|
|
{
|
|
id: 't3',
|
|
title: '你的教育背景处于中位区间',
|
|
description: '可通过突出工作经历与项目成果弥补学历竞争力',
|
|
type: 'tip',
|
|
},
|
|
],
|
|
emptyState: {
|
|
title: '暂无排名数据',
|
|
subtitle: '完善简历和测评信息后,系统将为你计算竞争力排名',
|
|
actionText: '去完善简历',
|
|
resumeProgress: [
|
|
{ id: 'basic', label: '基本信息', completed: true },
|
|
{ id: 'education', label: '教育经历', completed: true },
|
|
{ id: 'work', label: '工作经历', completed: false, tip: '添加工作经历可提升 20% 排名准确度' },
|
|
{ id: 'project', label: '项目经历', completed: false },
|
|
{ id: 'skill', label: '技能标签', completed: false },
|
|
],
|
|
},
|
|
});
|
|
}
|
|
|
|
/* ===================== 行业技能词云 ===================== */
|
|
|
|
/** 技能分类(全部分类 + 5 个具体分类) */
|
|
const MOCK_SKILL_CATEGORIES: SkillCategory[] = [
|
|
{ id: 'all', label: '全部' },
|
|
{ id: 'frontend', label: '前端' },
|
|
{ id: 'backend', label: '后端' },
|
|
{ id: 'devops', label: '工程工具' },
|
|
{ id: 'database', label: '数据库' },
|
|
{ id: 'design', label: '设计' },
|
|
];
|
|
|
|
/** 词云筛选可选项 */
|
|
const MOCK_SKILL_FILTERS: SkillCloudFilter = {
|
|
industries: ['互联网/IT', '金融', '教育', '医疗', '制造', '房地产'],
|
|
positions: ['高级前端工程师', '前端开发工程师', '全栈工程师', 'Java开发工程师'],
|
|
cities: ['全国', '北京', '上海', '深圳', '杭州'],
|
|
experiences: ['应届生', '1-3年', '3-5年', '5-10年'],
|
|
};
|
|
|
|
/** 各行业技能词(权重 0-100) */
|
|
const MOCK_SKILL_WORDS: Record<string, SkillWord[]> = {
|
|
'互联网/IT': [
|
|
{ id: 's1', text: 'React', weight: 96, group: 'tech' },
|
|
{ id: 's2', text: 'TypeScript', weight: 92, group: 'tech' },
|
|
{ id: 's3', text: 'Node.js', weight: 85, group: 'tech' },
|
|
{ id: 's4', text: 'Vue', weight: 78, group: 'tech' },
|
|
{ id: 's5', text: 'Webpack', weight: 64, group: 'tool' },
|
|
{ id: 's6', text: '数据分析', weight: 88, group: 'domain' },
|
|
{ id: 's7', text: '用户体验', weight: 72, group: 'soft' },
|
|
{ id: 's8', text: '需求拆解', weight: 70, group: 'soft' },
|
|
{ id: 's9', text: 'Git', weight: 90, group: 'tool' },
|
|
{ id: 's10', text: '性能优化', weight: 75, group: 'domain' },
|
|
{ id: 's11', text: '云服务', weight: 68, group: 'tech' },
|
|
{ id: 's12', text: '敏捷协作', weight: 66, group: 'soft' },
|
|
{ id: 's13', text: '前端工程化', weight: 82, group: 'tech' },
|
|
{ id: 's14', text: '跨端开发', weight: 73, group: 'tech' },
|
|
],
|
|
'金融': [
|
|
{ id: 'f1', text: '财务建模', weight: 94, group: 'domain' },
|
|
{ id: 'f2', text: 'Python', weight: 88, group: 'tech' },
|
|
{ id: 'f3', text: '风险管理', weight: 86, group: 'domain' },
|
|
{ id: 'f4', text: 'Excel', weight: 90, group: 'tool' },
|
|
{ id: 'f5', text: '行业研究', weight: 80, group: 'soft' },
|
|
{ id: 'f6', text: '估值', weight: 76, group: 'domain' },
|
|
{ id: 'f7', text: '沟通表达', weight: 72, group: 'soft' },
|
|
],
|
|
'教育': [
|
|
{ id: 'e1', text: '课程设计', weight: 90, group: 'domain' },
|
|
{ id: 'e2', text: '演讲表达', weight: 84, group: 'soft' },
|
|
{ id: 'e3', text: '社群运营', weight: 78, group: 'soft' },
|
|
{ id: 'e4', text: 'Office', weight: 86, group: 'tool' },
|
|
{ id: 'e5', text: '用户洞察', weight: 74, group: 'domain' },
|
|
],
|
|
};
|
|
|
|
/** 热门技能 TOP 10 */
|
|
const MOCK_TOP_SKILLS: TopSkill[] = [
|
|
{ id: 't1', name: 'React', rank: 1, change: 12, demand: 96 },
|
|
{ id: 't2', name: 'TypeScript', rank: 2, change: 18, demand: 92 },
|
|
{ id: 't3', name: 'Node.js', rank: 3, change: 8, demand: 85 },
|
|
{ id: 't4', name: '前端工程化', rank: 4, change: 15, demand: 82 },
|
|
{ id: 't5', name: 'Vue', rank: 5, change: -3, demand: 78 },
|
|
{ id: 't6', name: '性能优化', rank: 6, change: 6, demand: 75 },
|
|
{ id: 't7', name: '跨端开发', rank: 7, change: 22, demand: 73 },
|
|
{ id: 't8', name: 'Webpack', rank: 8, change: -5, demand: 64 },
|
|
{ id: 't9', name: '云服务', rank: 9, change: 10, demand: 68 },
|
|
{ id: 't10', name: '敏捷协作', rank: 10, change: 4, demand: 66 },
|
|
];
|
|
|
|
/** 默认行业(词云列表无匹配时兜底) */
|
|
const DEFAULT_SKILL_INDUSTRY = '互联网/IT';
|
|
|
|
/**
|
|
* 获取行业技能词云
|
|
* @method GET
|
|
* @url /api/tools/skill-cloud
|
|
* @param industry { string } 行业名称
|
|
* @returns Promise<SkillCloudData> 技能词云数据
|
|
*/
|
|
export async function fetchSkillCloud(industry: string): Promise<SkillCloudData> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/tools/skill-cloud?industry=${encodeURIComponent(industry)}`, { method: 'GET' }); return res.json();
|
|
const words = MOCK_SKILL_WORDS[industry] ?? MOCK_SKILL_WORDS[DEFAULT_SKILL_INDUSTRY];
|
|
const resolvedIndustry = MOCK_SKILL_WORDS[industry] ? industry : DEFAULT_SKILL_INDUSTRY;
|
|
return mockRequest({
|
|
industry: resolvedIndustry,
|
|
position: '高级前端工程师',
|
|
city: '全国',
|
|
experience: '3-5年',
|
|
activeCategoryId: 'all',
|
|
filters: MOCK_SKILL_FILTERS,
|
|
categories: MOCK_SKILL_CATEGORIES,
|
|
words,
|
|
topSkills: MOCK_TOP_SKILLS,
|
|
matchPercent: 80,
|
|
updatedAt: '数据更新于 2026-08',
|
|
});
|
|
}
|
|
|
|
/* ===================== 薪资计算器 ===================== */
|
|
|
|
/** 各行业平均月薪参考(计算器对比用) */
|
|
const MOCK_SALARY_BRACKETS: SalaryBracket[] = [
|
|
{ industry: '互联网/IT', avgMonthly: 28, sampleCount: 2846 },
|
|
{ industry: '金融', avgMonthly: 35, sampleCount: 1320 },
|
|
{ industry: '教育', avgMonthly: 14, sampleCount: 980 },
|
|
{ industry: '医疗健康', avgMonthly: 18, sampleCount: 760 },
|
|
{ industry: '制造业', avgMonthly: 22, sampleCount: 1540 },
|
|
];
|
|
|
|
/**
|
|
* 获取各行业平均月薪参考
|
|
* @method GET
|
|
* @url /api/tools/salary-brackets
|
|
* @returns Promise<SalaryBracket[]> 行业薪资参考数组
|
|
*/
|
|
export async function fetchSalaryBrackets(): Promise<SalaryBracket[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/tools/salary-brackets`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_SALARY_BRACKETS);
|
|
}
|
|
|
|
/**
|
|
* 薪资计算(业务规则:五险一金 + 个税估算,接口层纯函数)。
|
|
* 计算器为实时交互,故同步计算,避免 300ms mock 延迟影响输入体验。
|
|
* @param input 薪资计算输入(元/百分比)
|
|
* @returns SalaryCalcResult 计算结果(元)
|
|
*/
|
|
export function computeSalaryResult(input: SalaryCalcInput): SalaryCalcResult {
|
|
const monthlyGross = input.monthlyGross;
|
|
// 年终奖摊入月度,得到月度税前合计
|
|
const monthlyTotal = monthlyGross * (1 + input.bonusMonths / 12);
|
|
|
|
// 按所在城市 clamp 社保基数到该城市上下限(养老/医疗/失业比例全国一致,城市差异在基数上下限)
|
|
const socialProfile = SOCIAL_SECURITY_CITY_PROFILES[input.city] ?? DEFAULT_SOCIAL_PROFILE;
|
|
const socialBase = Math.min(
|
|
socialProfile.socialBaseUpper,
|
|
Math.max(socialProfile.socialBaseLower, input.socialBase),
|
|
);
|
|
|
|
// 社保个人缴纳比例:养老 8% + 医疗 2% + 失业 0.5%
|
|
const pension = socialBase * 0.08;
|
|
const medical = socialBase * 0.02;
|
|
const unemployment = socialBase * 0.005;
|
|
const providentFund = socialBase * input.providentFundRate;
|
|
const monthlySocial = pension + medical + unemployment + providentFund;
|
|
|
|
// 个税:月度应纳税所得额 = 税前 - 五险一金 - 5000 起征点 - 专项附加扣除
|
|
const taxable = Math.max(
|
|
0,
|
|
monthlyTotal - monthlySocial - 5000 - input.specialDeduction,
|
|
);
|
|
const monthlyTax = calcMonthlyTax(taxable);
|
|
const monthlyNet = Math.max(0, monthlyTotal - monthlySocial - monthlyTax);
|
|
const annualGross = monthlyGross * 12 + monthlyGross * input.bonusMonths;
|
|
const annualNet = monthlyNet * 12;
|
|
|
|
const totalYearlyGross = annualGross;
|
|
const totalYearlyNet = annualNet;
|
|
const totalYearlySocial = monthlySocial * 12;
|
|
const totalYearlyTax = monthlyTax * 12;
|
|
|
|
const insuranceItems: SalaryInsuranceItem[] = [
|
|
{ label: '养老保险', rate: 0.08, amount: Math.round(pension) },
|
|
{ label: '医疗保险', rate: 0.02, amount: Math.round(medical) },
|
|
{ label: '失业保险', rate: 0.005, amount: Math.round(unemployment) },
|
|
{ label: '住房公积金', rate: input.providentFundRate, amount: Math.round(providentFund) },
|
|
];
|
|
|
|
const taxItems: SalaryTaxItem[] = [
|
|
{ label: '应纳税所得额', amount: Math.round(taxable) },
|
|
{ label: '适用税率', amount: monthlyTax > 0 ? getTaxRate(taxable) : 0, rate: monthlyTax > 0 ? getTaxRate(taxable) : 0 },
|
|
{ label: '月度个税', amount: Math.round(monthlyTax) },
|
|
{ label: '年度个税', amount: Math.round(monthlyTax * 12) },
|
|
];
|
|
|
|
const structure: SalaryStructureItem[] = [
|
|
{ label: '税前工资', value: Math.round(totalYearlyGross), percent: 100, color: 'primary' },
|
|
{ label: '五险一金', value: Math.round(totalYearlySocial), percent: Math.round((totalYearlySocial / totalYearlyGross) * 100), color: 'info' },
|
|
{ label: '个人所得税', value: Math.round(totalYearlyTax), percent: Math.round((totalYearlyTax / totalYearlyGross) * 100), color: 'action' },
|
|
{ label: '实际到手', value: Math.round(totalYearlyNet), percent: Math.round((totalYearlyNet / totalYearlyGross) * 100), color: 'success' },
|
|
];
|
|
|
|
return {
|
|
input,
|
|
monthlyGross,
|
|
bonusMonths: input.bonusMonths,
|
|
monthlyTotal: Math.round(monthlyTotal),
|
|
monthlySocial: Math.round(monthlySocial),
|
|
monthlyTax: Math.round(monthlyTax),
|
|
monthlyNet: Math.round(monthlyNet),
|
|
annualGross: Math.round(annualGross),
|
|
annualNet: Math.round(annualNet),
|
|
insuranceItems,
|
|
taxItems,
|
|
structure,
|
|
};
|
|
}
|
|
|
|
/** 月度超额累进个税(单位元) */
|
|
function calcMonthlyTax(t: number): number {
|
|
if (t <= 0) return 0;
|
|
if (t <= 3000) return t * 0.03;
|
|
if (t <= 12000) return t * 0.1 - 210;
|
|
if (t <= 25000) return t * 0.2 - 1410;
|
|
if (t <= 35000) return t * 0.25 - 2660;
|
|
if (t <= 55000) return t * 0.3 - 4410;
|
|
if (t <= 80000) return t * 0.35 - 7160;
|
|
return t * 0.45 - 15160;
|
|
}
|
|
|
|
/** 获取当前应纳税所得额适用税率 */
|
|
function getTaxRate(t: number): number {
|
|
if (t <= 0) return 0;
|
|
if (t <= 3000) return 0.03;
|
|
if (t <= 12000) return 0.1;
|
|
if (t <= 25000) return 0.2;
|
|
if (t <= 35000) return 0.25;
|
|
if (t <= 55000) return 0.3;
|
|
if (t <= 80000) return 0.35;
|
|
return 0.45;
|
|
}
|
|
|
|
/* ===================== 求职进度 ===================== */
|
|
|
|
/** 求职进度(完整版:统计 / Offer / 公司流程 / 月度统计) */
|
|
const MOCK_JOB_PROGRESS: JobProgress = {
|
|
deliveryCount: 28,
|
|
interviewCount: 6,
|
|
offerCount: 2,
|
|
stats: {
|
|
total: 28,
|
|
pending: 6,
|
|
interviewing: 2,
|
|
offer: 2,
|
|
ended: 20,
|
|
},
|
|
offers: [
|
|
{
|
|
id: 'offer-1',
|
|
companyName: '字节跳动',
|
|
positionName: '高级前端工程师',
|
|
salary: '25-40K·15薪',
|
|
deadline: '8月31日截止',
|
|
daysLeft: 3,
|
|
},
|
|
{
|
|
id: 'offer-2',
|
|
companyName: '腾讯科技',
|
|
positionName: '产品经理',
|
|
salary: '20-35K·14薪',
|
|
deadline: '9月2日截止',
|
|
daysLeft: 5,
|
|
},
|
|
],
|
|
companies: [
|
|
{
|
|
id: 'c1',
|
|
companyName: '字节跳动',
|
|
positionName: '高级前端工程师',
|
|
salary: '25-40K',
|
|
stage: '面试中',
|
|
stageLabel: '终面',
|
|
nextAction: '明天 14:00 · 视频面试',
|
|
date: '08-28',
|
|
},
|
|
{
|
|
id: 'c2',
|
|
companyName: '腾讯科技',
|
|
positionName: '产品经理',
|
|
salary: '20-35K',
|
|
stage: 'Offer',
|
|
stageLabel: 'Offer 沟通中',
|
|
nextAction: 'HR 等待你的确认',
|
|
date: '08-27',
|
|
},
|
|
{
|
|
id: 'c3',
|
|
companyName: '阿里巴巴',
|
|
positionName: '前端开发工程师',
|
|
salary: '18-30K',
|
|
stage: '待反馈',
|
|
stageLabel: '简历筛选中',
|
|
nextAction: '预计 3 天内反馈',
|
|
date: '08-26',
|
|
},
|
|
{
|
|
id: 'c4',
|
|
companyName: '美团',
|
|
positionName: 'Java 开发工程师',
|
|
salary: '20-30K',
|
|
stage: '已结束',
|
|
stageLabel: '流程已结束',
|
|
nextAction: '岗位已关闭',
|
|
date: '08-20',
|
|
},
|
|
],
|
|
monthlyStats: [
|
|
{ month: '3月', deliveries: 4, interviews: 1, offers: 0 },
|
|
{ month: '4月', deliveries: 6, interviews: 2, offers: 0 },
|
|
{ month: '5月', deliveries: 8, interviews: 2, offers: 1 },
|
|
{ month: '6月', deliveries: 10, interviews: 3, offers: 0 },
|
|
{ month: '7月', deliveries: 12, interviews: 4, offers: 1 },
|
|
{ month: '8月', deliveries: 12, interviews: 4, offers: 1 },
|
|
],
|
|
steps: [
|
|
{ id: 'p1', title: '已投递', desc: '向 28 家企业投递简历', status: 'done', date: '2026-08-01' },
|
|
{ id: 'p2', title: '简历被查看', desc: '12 家企业查看了你的简历', status: 'done', date: '2026-08-05' },
|
|
{ id: 'p3', title: '面试邀约', desc: '收到 6 场面试邀请', status: 'done', date: '2026-08-12' },
|
|
{ id: 'p4', title: '面试进行中', desc: '字节跳动终面 / 腾讯二面待完成', status: 'active' },
|
|
{ id: 'p5', title: 'Offer 阶段', desc: '已收获 2 个 Offer,待决策', status: 'todo' },
|
|
{ id: 'p6', title: '入职准备', desc: '背调 / 合同签订 / 入职清单', status: 'todo' },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取求职进度时间轴
|
|
* @method GET
|
|
* @url /api/tools/job-progress
|
|
* @returns Promise<JobProgress> 求职进度数据
|
|
*/
|
|
export async function fetchJobProgress(): Promise<JobProgress> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/tools/job-progress`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_JOB_PROGRESS);
|
|
}
|
|
|
|
/* ===================== 简历 ===================== */
|
|
|
|
/** 简历 mock 数据 */
|
|
const MOCK_RESUME: Resume = {
|
|
score: {
|
|
score: 72,
|
|
maxScore: 100,
|
|
optimizePoints: [
|
|
{ id: 'opt-1', title: '项目经历描述可量化', score: 8, optimized: false },
|
|
{ id: 'opt-2', title: '缺少技能关键词', score: 5, optimized: false },
|
|
{ id: 'opt-3', title: '自我评价太空泛', score: 3, optimized: false },
|
|
{ id: 'opt-4', title: '工作经历缺少成果数据', score: 4, optimized: false },
|
|
{ id: 'opt-5', title: '求职意向可更明确', score: 2, optimized: false },
|
|
],
|
|
},
|
|
basicInfo: {
|
|
name: '李明',
|
|
avatar: '李',
|
|
phone: '138-8888-8888',
|
|
email: 'liming@email.com',
|
|
location: '北京 · 朝阳区',
|
|
},
|
|
jobIntention: {
|
|
position: '高级前端工程师',
|
|
salary: '25-35K',
|
|
city: '北京',
|
|
status: 'active',
|
|
statusLabel: '积极求职中',
|
|
},
|
|
education: [
|
|
{
|
|
id: 'edu-1',
|
|
school: '北京大学',
|
|
major: '计算机科学与技术',
|
|
degree: '本科',
|
|
startYear: '2018',
|
|
endYear: '2022',
|
|
},
|
|
],
|
|
experience: [
|
|
{
|
|
id: 'exp-1',
|
|
company: '字节跳动',
|
|
position: '前端开发工程师',
|
|
startDate: '2022.07',
|
|
endDate: '至今',
|
|
description: '负责公司核心产品的前端开发工作,参与了多个项目的开发。',
|
|
aiOptimized: false,
|
|
},
|
|
],
|
|
projects: [
|
|
{
|
|
id: 'proj-1',
|
|
name: 'XX电商平台重构',
|
|
role: '前端负责人',
|
|
description: '使用React重构了整个电商平台,将首屏加载时间从3.5s优化至1.2s,页面性能提升65%,日活用户增长40%。',
|
|
aiOptimized: true,
|
|
},
|
|
],
|
|
skills: [
|
|
{ name: 'Vue' },
|
|
{ name: 'React' },
|
|
{ name: 'TypeScript' },
|
|
{ name: 'Webpack' },
|
|
{ name: 'CSS3' },
|
|
{ name: 'Git' },
|
|
],
|
|
selfEvaluation: {
|
|
content: '本人性格开朗,工作认真负责,具有良好的团队合作精神。熟悉前端开发流程,有较强的学习能力和问题解决能力。',
|
|
},
|
|
};
|
|
|
|
/** AI 优化后的简历 */
|
|
const MOCK_OPTIMIZED_RESUME: Resume = {
|
|
...MOCK_RESUME,
|
|
score: {
|
|
score: 92,
|
|
maxScore: 100,
|
|
optimizePoints: MOCK_RESUME.score.optimizePoints.map((p) => ({ ...p, optimized: true })),
|
|
},
|
|
experience: [
|
|
{
|
|
...MOCK_RESUME.experience[0],
|
|
description:
|
|
'负责公司核心产品的前端架构设计与开发,主导 React 18 + TypeScript 技术栈升级;通过代码拆分、懒加载与性能监控,将首屏加载时间从 3.5s 优化至 1.2s,核心页面性能提升 65%;推动组件库建设,沉淀 30+ 可复用业务组件。',
|
|
aiOptimized: true,
|
|
},
|
|
],
|
|
selfEvaluation: {
|
|
content:
|
|
'3 年前端开发经验,具备完整的大型 Web 项目架构与性能优化能力。擅长 React / TypeScript 技术栈,对工程化、组件化、性能监控有深入实践,能够独立推动技术方案落地并持续提升团队开发效率。',
|
|
},
|
|
};
|
|
|
|
/** 默认导出设置 */
|
|
const DEFAULT_EXPORT_SETTINGS: ResumeExportSettings = {
|
|
format: 'pdf',
|
|
templateId: 'classic',
|
|
hideContact: false,
|
|
exportPassword: false,
|
|
paperSize: 'A4',
|
|
orientation: 'portrait',
|
|
fileName: '李明_前端工程师简历',
|
|
includeCover: false,
|
|
includeRecommendation: false,
|
|
};
|
|
|
|
/**
|
|
* 获取我的简历
|
|
* @method GET
|
|
* @url /api/resume
|
|
* @returns Promise<Resume> 简历数据
|
|
*/
|
|
export async function fetchResume(): Promise<Resume> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/resume`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_RESUME);
|
|
}
|
|
|
|
/**
|
|
* AI 一键优化简历
|
|
* @method POST
|
|
* @url /api/resume/optimize
|
|
* @returns Promise<ResumeOptimizeResult> 优化后的简历
|
|
*/
|
|
export async function optimizeResume(): Promise<ResumeOptimizeResult> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/resume/optimize`, { method: 'POST' }); return res.json();
|
|
return mockRequest({ score: MOCK_OPTIMIZED_RESUME.score.score, resume: MOCK_OPTIMIZED_RESUME });
|
|
}
|
|
|
|
/**
|
|
* 获取简历导出设置
|
|
* @method GET
|
|
* @url /api/resume/export-settings
|
|
* @returns Promise<ResumeExportSettings> 导出设置
|
|
*/
|
|
export async function fetchResumeExportSettings(): Promise<ResumeExportSettings> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/resume/export-settings`, { method: 'GET' }); return res.json();
|
|
return mockRequest({ ...DEFAULT_EXPORT_SETTINGS });
|
|
}
|
|
|
|
/**
|
|
* 保存简历导出设置
|
|
* @method PUT
|
|
* @url /api/resume/export-settings
|
|
* @param settings 导出设置
|
|
*/
|
|
export async function saveResumeExportSettings(settings: ResumeExportSettings): Promise<void> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/resume/export-settings`, { method: 'PUT', body: JSON.stringify(settings) }); return res.json();
|
|
Object.assign(DEFAULT_EXPORT_SETTINGS, settings);
|
|
return mockRequest(undefined);
|
|
}
|
|
|
|
/**
|
|
* 导出简历
|
|
* @method POST
|
|
* @url /api/resume/export
|
|
* @param settings 导出设置
|
|
* @returns Promise<{ url: string }> 导出文件地址
|
|
*/
|
|
export async function exportResume(settings: ResumeExportSettings): Promise<{ url: string }> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/resume/export`, { method: 'POST', body: JSON.stringify(settings) }); return res.json();
|
|
const formatMap: Record<ResumeExportFormat, string> = {
|
|
pdf: 'pdf',
|
|
word: 'docx',
|
|
image: 'png',
|
|
};
|
|
return mockRequest({
|
|
url: `https://cdn.example.com/resume/${settings.fileName}.${formatMap[settings.format]}`,
|
|
});
|
|
}
|
|
|
|
/* ===================== 入职准备清单 ===================== */
|
|
|
|
/** 入职准备清单分组(材料 / 手续 / 学习 / 生活 / 每日提醒) */
|
|
const MOCK_CHECKLIST_GROUPS: ChecklistGroup[] = [
|
|
{
|
|
id: 'material',
|
|
title: '材料准备',
|
|
icon: 'folder-outline',
|
|
expanded: true,
|
|
items: [
|
|
{ id: 'm1', title: '身份证原件及复印件', done: true, required: true },
|
|
{ id: 'm2', title: '学历学位证书', done: true, required: true },
|
|
{ id: 'm3', title: '离职证明', done: true, required: true },
|
|
{ id: 'm4', title: '入职体检报告', done: false, required: true },
|
|
{ id: 'm5', title: '银行卡(工资卡)', done: false, required: true },
|
|
{ id: 'm6', title: '一寸/二寸证件照', done: false },
|
|
],
|
|
},
|
|
{
|
|
id: 'procedure',
|
|
title: '手续办理',
|
|
icon: 'document-text-outline',
|
|
expanded: true,
|
|
items: [
|
|
{ id: 'p1', title: '签订劳动合同', done: false, required: true },
|
|
{ id: 'p2', title: '社保公积金转移', done: false, required: true },
|
|
{ id: 'p3', title: '入职信息登记', done: false, required: true },
|
|
{ id: 'p4', title: '领取工牌与办公设备', done: false },
|
|
],
|
|
},
|
|
{
|
|
id: 'study',
|
|
title: '学习准备',
|
|
icon: 'book-outline',
|
|
expanded: false,
|
|
items: [
|
|
{ id: 's1', title: '熟悉公司业务与产品线', done: false },
|
|
{ id: 's2', title: '了解团队成员与协作流程', done: false },
|
|
{ id: 's3', title: '技术栈预习(React / TypeScript)', done: false },
|
|
{ id: 's4', title: '搭建本地开发环境', done: false },
|
|
],
|
|
},
|
|
{
|
|
id: 'life',
|
|
title: '生活准备',
|
|
icon: 'home-outline',
|
|
expanded: false,
|
|
items: [
|
|
{ id: 'l1', title: '租房 / 通勤路线规划', done: false },
|
|
{ id: 'l2', title: '准备办公用品', done: false },
|
|
{ id: 'l3', title: '准备入职着装', done: false },
|
|
{ id: 'l4', title: '调整作息时间', done: false },
|
|
],
|
|
},
|
|
];
|
|
|
|
/** 入职准备清单 */
|
|
const MOCK_ONBOARDING_CHECKLIST: OnboardingChecklist = {
|
|
daysLeft: 7,
|
|
position: '高级前端工程师',
|
|
company: '字节跳动',
|
|
total: 18,
|
|
completed: 3,
|
|
onboardingDate: '2026-09-06',
|
|
groups: MOCK_CHECKLIST_GROUPS,
|
|
dailyReminder: {
|
|
enabled: true,
|
|
time: '09:00',
|
|
},
|
|
tips: [
|
|
{
|
|
id: 'tip-1',
|
|
title: '提前 15 分钟到公司',
|
|
desc: '首日建议提前到达,留出办理入职手续的时间',
|
|
icon: 'time-outline',
|
|
},
|
|
{
|
|
id: 'tip-2',
|
|
title: '主动认识团队成员',
|
|
desc: '主动自我介绍,快速融入团队氛围',
|
|
icon: 'people-outline',
|
|
},
|
|
{
|
|
id: 'tip-3',
|
|
title: '准备自我介绍',
|
|
desc: '准备 1 分钟自我介绍,包含背景与擅长方向',
|
|
icon: 'chatbubble-outline',
|
|
},
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取入职准备清单
|
|
* @method GET
|
|
* @url /api/tools/onboarding-checklist
|
|
* @returns Promise<OnboardingChecklist> 入职准备清单数据
|
|
*/
|
|
export async function fetchOnboardingChecklist(): Promise<OnboardingChecklist> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/tools/onboarding-checklist`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_ONBOARDING_CHECKLIST);
|
|
}
|
|
|
|
/**
|
|
* 切换入职待办完成状态
|
|
* @method PATCH
|
|
* @url /api/tools/onboarding-checklist/items/:id
|
|
* @param itemId { string } 待办 id
|
|
* @param done { boolean } 是否完成
|
|
* @returns Promise<OnboardingChecklist> 更新后的清单
|
|
*/
|
|
export async function toggleChecklistItem(
|
|
itemId: string,
|
|
done: boolean,
|
|
): Promise<OnboardingChecklist> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/tools/onboarding-checklist/items/${itemId}`, { method: 'PATCH', body: JSON.stringify({ done }) }); return res.json();
|
|
const groups = MOCK_CHECKLIST_GROUPS.map((g) => ({
|
|
...g,
|
|
items: g.items.map((item) => (item.id === itemId ? { ...item, done } : item)),
|
|
}));
|
|
const completed = groups.reduce(
|
|
(sum, g) => sum + g.items.filter((i) => i.done).length,
|
|
0,
|
|
);
|
|
const total = groups.reduce((sum, g) => sum + g.items.length, 0);
|
|
return mockRequest({ ...MOCK_ONBOARDING_CHECKLIST, groups, completed, total });
|
|
}
|
|
|
|
/* ===================== AI 模拟面试 ===================== */
|
|
|
|
/** AI 模拟面试配置 */
|
|
const MOCK_AI_INTERVIEW: AIInterviewData = {
|
|
mode: 'full',
|
|
modes: [
|
|
{ id: 'full', label: '全真模拟', desc: '完整流程,高仿真', icon: 'videocam-outline' },
|
|
{ id: 'special', label: '专项突破', desc: '针对弱项强化训练', icon: 'fitness-outline' },
|
|
{ id: 'fast', label: '快速问答', desc: '10分钟快速练习', icon: 'flash-outline' },
|
|
],
|
|
jobInfo: {
|
|
position: '高级前端工程师',
|
|
company: '字节跳动',
|
|
difficulty: '中等',
|
|
duration: 30,
|
|
},
|
|
questionTypes: [
|
|
{ id: 'tech', label: '技术面', color: 'primary', selected: true },
|
|
{ id: 'behavior', label: '行为面', color: 'success', selected: true },
|
|
{ id: 'logic', label: '智力题', color: 'action', selected: false },
|
|
],
|
|
specials: [
|
|
{ id: 'sp1', label: '前端框架原理', selected: false },
|
|
{ id: 'sp2', label: '工程化实践', selected: false },
|
|
{ id: 'sp3', label: '性能优化', selected: false },
|
|
{ id: 'sp4', label: '项目深挖', selected: false },
|
|
{ id: 'sp5', label: '算法与数据结构', selected: false },
|
|
],
|
|
improvements: [
|
|
{ id: 'imp1', label: '技术表达', percent: 10, color: 'primary' },
|
|
{ id: 'imp2', label: '应变能力', percent: 12, color: 'success' },
|
|
],
|
|
recent: [
|
|
{ id: 'r1', date: '3月15日', title: '前端开发模拟面试', result: '良好', resultColor: 'success' },
|
|
{ id: 'r2', date: '3月10日', title: '技术专项突破', result: '优秀', resultColor: 'primary' },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取 AI 模拟面试配置
|
|
* @method GET
|
|
* @url /api/interview/config
|
|
* @returns Promise<AIInterviewData> 模拟面试配置
|
|
*/
|
|
export async function fetchAIInterviewData(): Promise<AIInterviewData> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/interview/config`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_AI_INTERVIEW);
|
|
}
|
|
|
|
/**
|
|
* 开始模拟面试
|
|
* @method POST
|
|
* @url /api/interview/start
|
|
* @returns Promise<InterviewSession> 面试会话
|
|
*/
|
|
export async function startInterview(): Promise<InterviewSession> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/interview/start`, { method: 'POST' }); return res.json();
|
|
return mockRequest({ id: `session-${Date.now()}`, total: 10, currentIndex: 1, remainingSeconds: 1800 });
|
|
}
|
|
|
|
/* ===================== HR 评价 ===================== */
|
|
|
|
/** HR 评价详情 */
|
|
const MOCK_HR_EVALUATION: HREvaluation = {
|
|
overallScore: 88,
|
|
percentile: 78,
|
|
trend: [
|
|
{ month: '3月', score: 82 },
|
|
{ month: '4月', score: 84 },
|
|
{ month: '5月', score: 83 },
|
|
{ month: '6月', score: 85 },
|
|
{ month: '7月', score: 86 },
|
|
{ month: '8月', score: 88 },
|
|
],
|
|
dimensions: [
|
|
{ id: 'd1', name: '技术能力', score: 92, maxScore: 100, level: '优秀', change: 2 },
|
|
{ id: 'd2', name: '项目经验', score: 88, maxScore: 100, level: '优秀', change: 1 },
|
|
{ id: 'd3', name: '沟通表达', score: 85, maxScore: 100, level: '良好', change: 3 },
|
|
{ id: 'd4', name: '团队协作', score: 90, maxScore: 100, level: '优秀', change: 4 },
|
|
{ id: 'd5', name: '学习能力', score: 86, maxScore: 100, level: '良好', change: 2 },
|
|
{ id: 'd6', name: '稳定性', score: 80, maxScore: 100, level: '良好', change: -1 },
|
|
],
|
|
reviews: [
|
|
{
|
|
id: 'rv1',
|
|
company: '字节跳动',
|
|
rating: 5,
|
|
date: '2026-08-20',
|
|
content: '技术基础扎实,对 React 与 TypeScript 理解深入,工程化实践经验丰富。沟通顺畅,能够快速理解业务需求并给出技术方案。',
|
|
tags: ['技术扎实', '沟通顺畅', '工程能力强'],
|
|
status: 'accepted',
|
|
},
|
|
{
|
|
id: 'rv2',
|
|
company: '腾讯科技',
|
|
rating: 4,
|
|
date: '2026-08-12',
|
|
content: '项目经验较为丰富,主导过中大型前端项目。在性能优化方向有一定积累,建议进一步补充跨端开发经验。',
|
|
tags: ['项目丰富', '性能优化', '可补充跨端'],
|
|
status: 'accepted',
|
|
},
|
|
{
|
|
id: 'rv3',
|
|
company: '阿里巴巴',
|
|
rating: 4,
|
|
date: '2026-08-05',
|
|
content: '学习能力较强,能够快速上手新技术栈。团队协作意识好,建议在未来加强系统设计与架构能力的沉淀。',
|
|
tags: ['学习能力强', '协作好', '可加强架构'],
|
|
status: 'accepted',
|
|
},
|
|
],
|
|
rules: [
|
|
'综合评分由六维能力加权计算得出,技术能力权重最高',
|
|
'评价数据来自已完成面试流程的企业 HR,确保真实有效',
|
|
'评分每月更新一次,反映你近期的求职表现',
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取 HR 评价详情
|
|
* @method GET
|
|
* @url /api/evaluation/hr
|
|
* @returns Promise<HREvaluation> HR 评价详情
|
|
*/
|
|
export async function fetchHREvaluation(): Promise<HREvaluation> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/evaluation/hr`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_HR_EVALUATION);
|
|
}
|
|
|
|
/* ===================== 评价申诉 ===================== */
|
|
|
|
/** 申诉维度(六维能力) */
|
|
const MOCK_APPEAL_DIMENSIONS: AppealDimension[] = [
|
|
{ id: 'd1', name: '技术能力', selected: false },
|
|
{ id: 'd2', name: '项目经验', selected: false },
|
|
{ id: 'd3', name: '沟通表达', selected: false },
|
|
{ id: 'd4', name: '团队协作', selected: false },
|
|
{ id: 'd5', name: '学习能力', selected: false },
|
|
{ id: 'd6', name: '稳定性', selected: false },
|
|
];
|
|
|
|
/** 评价申诉表单初始数据 */
|
|
const MOCK_APPEAL_FORM: EvaluationAppealForm = {
|
|
type: 'overall',
|
|
dimensions: MOCK_APPEAL_DIMENSIONS,
|
|
reason: 'untrue',
|
|
reasonText: '',
|
|
evidence: [],
|
|
contactPhone: '',
|
|
contactEmail: '',
|
|
agree: false,
|
|
};
|
|
|
|
/**
|
|
* 获取评价申诉表单数据
|
|
* @method GET
|
|
* @url /api/evaluation/appeal
|
|
* @returns Promise<EvaluationAppealForm> 申诉表单初始数据
|
|
*/
|
|
export async function fetchAppealForm(): Promise<EvaluationAppealForm> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/evaluation/appeal`, { method: 'GET' }); return res.json();
|
|
return mockRequest({ ...MOCK_APPEAL_FORM });
|
|
}
|
|
|
|
/**
|
|
* 提交评价申诉
|
|
* @method POST
|
|
* @url /api/evaluation/appeal
|
|
* @param payload { EvaluationAppealForm } 申诉表单
|
|
* @returns Promise<{ appealNo: string; estimatedDays: number }> 申诉结果
|
|
*/
|
|
export async function submitAppeal(
|
|
payload: EvaluationAppealForm,
|
|
): Promise<{ appealNo: string; estimatedDays: number }> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/evaluation/appeal`, { method: 'POST', body: JSON.stringify(payload) }); return res.json();
|
|
void payload;
|
|
return mockRequest({
|
|
appealNo: `AP${Date.now().toString().slice(-8)}`,
|
|
estimatedDays: 3,
|
|
});
|
|
}
|
|
|
|
/* ===================== 企业数据总览 ===================== */
|
|
|
|
/** 企业总览快捷入口 */
|
|
const MOCK_ENTERPRISE_QUICK_ACTIONS: EnterpriseQuickAction[] = [
|
|
{ id: 'publish', label: '发布岗位', icon: 'add-circle-outline', route: '/job-publish', color: 'primary' },
|
|
{ id: 'jobs', label: '岗位管理', icon: 'business-outline', route: '/job-management', color: 'primary' },
|
|
{ id: 'evaluations', label: '岗位评价管理', icon: 'chatbubbles-outline', route: '/job-evaluations', color: 'success' },
|
|
{ id: 'candidates', label: '人才推荐', icon: 'people-outline', route: '/talent-recommendation', color: 'info' },
|
|
{ id: 'communication', label: '企业沟通', icon: 'chatbubble-outline', route: '/enterprise-chat', color: 'action' },
|
|
{ id: 'interviews', label: '面试评分', icon: 'calendar-outline', route: '/interview-score', color: 'action' },
|
|
];
|
|
|
|
/**
|
|
* 企业端管理后台入口
|
|
* 低频管理操作,单独成区展示,不占用快捷入口的横排位;
|
|
* 同时避免「运营规则配置 / 数据审计」等后台术语出现在求职者端菜单。
|
|
*/
|
|
const MOCK_ENTERPRISE_MANAGE_ACTIONS: EnterpriseQuickAction[] = [
|
|
{ id: 'operation-rules', label: '运营规则配置', icon: 'options-outline', route: '/operation-rules', color: 'primary' },
|
|
{ id: 'audit-report', label: '数据审计报告', icon: 'shield-checkmark-outline', route: '/audit-report', color: 'info' },
|
|
];
|
|
|
|
/** 企业总览统计 */
|
|
const MOCK_ENTERPRISE_STATS: EnterpriseStat[] = [
|
|
{ id: 'jobs', label: '在招岗位', value: 1280, change: 12, icon: 'briefcase-outline', color: 'primary' },
|
|
{ id: 'interviews', label: '本月面试', value: 156, change: 8, icon: 'videocam-outline', color: 'info' },
|
|
{ id: 'reviews', label: '累计评价', value: 28, change: -3, icon: 'chatbubble-outline', color: 'success' },
|
|
{ id: 'appeals', label: '待处理申诉', value: 12, change: 2, icon: 'alert-circle-outline', color: 'action' },
|
|
];
|
|
|
|
/** 岗位评价摘要 */
|
|
const MOCK_JOB_EVALUATION_SUMMARIES: JobEvaluationSummary[] = [
|
|
{ id: 'je1', positionName: '高级前端工程师', department: '技术部', score: 4.9, reviewCount: 42, change: 0.2 },
|
|
{ id: 'je2', positionName: '产品经理', department: '产品部', score: 4.7, reviewCount: 38, change: 0.1 },
|
|
{ id: 'je3', positionName: 'UI设计师', department: '设计部', score: 4.6, reviewCount: 25, change: -0.1 },
|
|
{ id: 'je4', positionName: '后端开发工程师', department: '技术部', score: 4.5, reviewCount: 31, change: 0.3 },
|
|
];
|
|
|
|
/** 企业总览数据 */
|
|
const MOCK_ENTERPRISE_OVERVIEW: EnterpriseOverview = {
|
|
id: 'ent-1',
|
|
name: '北京智联科技有限公司',
|
|
logo: '智',
|
|
industry: '互联网/IT',
|
|
scale: '1000-9999人',
|
|
stage: 'B轮',
|
|
overallScore: 4.8,
|
|
reviewCount: 1280,
|
|
stats: MOCK_ENTERPRISE_STATS,
|
|
jobEvaluations: MOCK_JOB_EVALUATION_SUMMARIES,
|
|
quickActions: MOCK_ENTERPRISE_QUICK_ACTIONS,
|
|
manageActions: MOCK_ENTERPRISE_MANAGE_ACTIONS,
|
|
menuItems: MOCK_ENTERPRISE_MENU_ITEMS,
|
|
};
|
|
|
|
/**
|
|
* 获取企业数据总览
|
|
* @method GET
|
|
* @url /api/enterprise/overview
|
|
* @returns Promise<EnterpriseOverview> 企业总览数据
|
|
*/
|
|
export async function fetchEnterpriseOverview(): Promise<EnterpriseOverview> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/overview`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_ENTERPRISE_OVERVIEW);
|
|
}
|
|
|
|
/* ===================== 发布岗位 ===================== */
|
|
|
|
/** 默认发布岗位表单 */
|
|
const DEFAULT_JOB_PUBLISH_FORM: JobPublishFormData = {
|
|
positionName: '高级前端工程师',
|
|
department: '技术部',
|
|
headcount: 2,
|
|
city: '北京',
|
|
address: '朝阳区望京SOHO T3',
|
|
workType: 'full-time',
|
|
education: '本科',
|
|
experience: '3-5年',
|
|
salaryMin: 25,
|
|
salaryMax: 40,
|
|
salaryMonths: 14,
|
|
benefits: ['五险一金', '年终奖金', '弹性工作', '带薪年假', '股票期权'],
|
|
responsibilities:
|
|
'1. 负责公司核心产品前端架构设计与开发\n2. 参与前端工程化建设,提升研发效率\n3. 与产品、设计、后端紧密配合,保证交付质量',
|
|
requirements:
|
|
'1. 本科及以上学历,3年以上前端开发经验\n2. 精通 React / TypeScript,熟悉现代前端工程化\n3. 具备良好的沟通能力和团队协作精神',
|
|
skills: ['React', 'TypeScript', 'Node.js', 'Webpack'],
|
|
visibility: 'all',
|
|
urgent: true,
|
|
acceptFresh: false,
|
|
deadline: '2026-12-31',
|
|
};
|
|
|
|
/** 岗位质量预估 */
|
|
const MOCK_JOB_QUALITY_ESTIMATE: JobQualityEstimate = {
|
|
score: 92,
|
|
level: '优秀',
|
|
suggestions: [
|
|
'岗位职责描述清晰,建议补充团队规模信息',
|
|
'薪资范围具有竞争力,可突出股权激励',
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取发布岗位表单初始数据
|
|
* @method GET
|
|
* @url /api/enterprise/jobs/publish-form
|
|
* @returns Promise<JobPublishFormData> 发布岗位表单
|
|
*/
|
|
export async function fetchJobPublishForm(): Promise<JobPublishFormData> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/jobs/publish-form`, { method: 'GET' }); return res.json();
|
|
return mockRequest({ ...DEFAULT_JOB_PUBLISH_FORM });
|
|
}
|
|
|
|
/**
|
|
* 预估岗位质量
|
|
* @method POST
|
|
* @url /api/enterprise/jobs/quality-estimate
|
|
* @param form { JobPublishFormData } 岗位表单
|
|
* @returns Promise<JobQualityEstimate> 质量预估结果
|
|
*/
|
|
export async function estimateJobQuality(form: JobPublishFormData): Promise<JobQualityEstimate> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/jobs/quality-estimate`, { method: 'POST', body: JSON.stringify(form) }); return res.json();
|
|
void form;
|
|
return mockRequest({ ...MOCK_JOB_QUALITY_ESTIMATE });
|
|
}
|
|
|
|
/**
|
|
* 发布岗位
|
|
* @method POST
|
|
* @url /api/enterprise/jobs
|
|
* @param form { JobPublishFormData } 岗位表单
|
|
* @returns Promise<{ jobId: string }> 发布结果
|
|
*/
|
|
export async function publishJob(form: JobPublishFormData): Promise<{ jobId: string }> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/jobs`, { method: 'POST', body: JSON.stringify(form) }); return res.json();
|
|
void form;
|
|
return mockRequest({ jobId: `job-${Date.now().toString().slice(-8)}` });
|
|
}
|
|
|
|
/* ===================== 岗位评价管理 ===================== */
|
|
|
|
/** 岗位评价维度 */
|
|
const MOCK_JOB_EVALUATION_DIMENSIONS: JobEvaluationDimension[] = [
|
|
{ id: 'jd1', name: '岗位真实性', score: 4.9, maxScore: 5, change: 0.1 },
|
|
{ id: 'jd2', name: '薪资透明度', score: 4.7, maxScore: 5, change: 0.2 },
|
|
{ id: 'jd3', name: '面试体验', score: 4.6, maxScore: 5, change: -0.1 },
|
|
{ id: 'jd4', name: '发展空间', score: 4.5, maxScore: 5, change: 0.3 },
|
|
{ id: 'jd5', name: '工作强度', score: 4.3, maxScore: 5, change: 0.0 },
|
|
{ id: 'jd6', name: '团队氛围', score: 4.8, maxScore: 5, change: 0.2 },
|
|
];
|
|
|
|
/** 岗位评价列表 */
|
|
const MOCK_JOB_REVIEWS: JobReview[] = [
|
|
{
|
|
id: 'jr1',
|
|
userName: '张**',
|
|
userRole: '在职员工,前端开发',
|
|
positionName: '高级前端工程师',
|
|
rating: 5,
|
|
date: '2026-08-20',
|
|
content: '面试流程规范,HR 反馈及时。岗位描述与实际工作基本一致,团队技术氛围很好。',
|
|
tags: ['面试规范', '反馈及时', '氛围好'],
|
|
replied: true,
|
|
},
|
|
{
|
|
id: 'jr2',
|
|
userName: '李**',
|
|
userRole: '求职者,产品经理',
|
|
positionName: '产品经理',
|
|
rating: 4,
|
|
date: '2026-08-15',
|
|
content: '整体体验不错,面试官专业。但面试等待时间稍长,希望可以优化。',
|
|
tags: ['面试官专业', '等待时间长'],
|
|
replied: false,
|
|
},
|
|
{
|
|
id: 'jr3',
|
|
userName: '王**',
|
|
userRole: '在职员工,UI设计',
|
|
positionName: 'UI设计师',
|
|
rating: 5,
|
|
date: '2026-08-10',
|
|
content: '薪资透明,福利完善,工作节奏合理。非常推荐加入这家公司。',
|
|
tags: ['薪资透明', '福利好'],
|
|
replied: true,
|
|
},
|
|
];
|
|
|
|
/** 岗位评价管理数据 */
|
|
const MOCK_JOB_EVALUATION_MANAGEMENT: JobEvaluationManagement = {
|
|
overallScore: 4.8,
|
|
change: 0.1,
|
|
reviewCount: 128,
|
|
positiveRate: 85,
|
|
replyRate: 72,
|
|
complaintRate: 8,
|
|
trend: [
|
|
{ month: '3月', score: 4.5 },
|
|
{ month: '4月', score: 4.6 },
|
|
{ month: '5月', score: 4.6 },
|
|
{ month: '6月', score: 4.7 },
|
|
{ month: '7月', score: 4.7 },
|
|
{ month: '8月', score: 4.8 },
|
|
],
|
|
dimensions: MOCK_JOB_EVALUATION_DIMENSIONS,
|
|
jobScores: MOCK_JOB_EVALUATION_SUMMARIES,
|
|
reviews: MOCK_JOB_REVIEWS,
|
|
};
|
|
|
|
/**
|
|
* 获取岗位评价管理数据
|
|
* @method GET
|
|
* @url /api/enterprise/evaluations
|
|
* @returns Promise<JobEvaluationManagement> 岗位评价管理数据
|
|
*/
|
|
export async function fetchJobEvaluationManagement(): Promise<JobEvaluationManagement> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/evaluations`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_JOB_EVALUATION_MANAGEMENT);
|
|
}
|
|
|
|
/**
|
|
* 回复岗位评价
|
|
* @method POST
|
|
* @url /api/enterprise/evaluations/:id/reply
|
|
* @param reviewId { string } 评价 id
|
|
* @param reply { string } 回复内容
|
|
* @returns Promise<JobReview> 回复后的评价
|
|
*/
|
|
export async function replyJobReview(
|
|
reviewId: string,
|
|
reply: string,
|
|
): Promise<JobReview> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/evaluations/${reviewId}/reply`, { method: 'POST', body: JSON.stringify({ reply }) }); return res.json();
|
|
const review = MOCK_JOB_REVIEWS.find((r) => r.id === reviewId) ?? MOCK_JOB_REVIEWS[0];
|
|
void reply;
|
|
return mockRequest({ ...review, replied: true });
|
|
}
|
|
|
|
/* ===================== 候选人详情 ===================== */
|
|
|
|
/** 候选人能力维度 */
|
|
const MOCK_CANDIDATE_ABILITIES: CandidateAbility[] = [
|
|
{ id: 'ca1', name: '专业技能', score: 92, maxScore: 100 },
|
|
{ id: 'ca2', name: '项目经验', score: 85, maxScore: 100 },
|
|
{ id: 'ca3', name: '逻辑思维', score: 88, maxScore: 100 },
|
|
{ id: 'ca4', name: '沟通表达', score: 92, maxScore: 100 },
|
|
{ id: 'ca5', name: '团队协作', score: 80, maxScore: 100 },
|
|
{ id: 'ca6', name: '学习能力', score: 95, maxScore: 100 },
|
|
{ id: 'ca7', name: '抗压能力', score: 75, maxScore: 100 },
|
|
{ id: 'ca8', name: '文化匹配', score: 88, maxScore: 100 },
|
|
];
|
|
|
|
/** 候选人评价 */
|
|
const MOCK_CANDIDATE_REVIEWS: CandidateReview[] = [
|
|
{
|
|
id: 'cr1',
|
|
interviewer: '王经理',
|
|
position: '高级前端工程师',
|
|
date: '2026-08-25',
|
|
rating: 5,
|
|
content: '技术基础扎实,对 React 与 TypeScript 理解深入,工程化实践经验丰富。沟通顺畅。',
|
|
tags: ['技术扎实', '沟通顺畅', '工程能力强'],
|
|
},
|
|
{
|
|
id: 'cr2',
|
|
interviewer: '李总监',
|
|
position: '前端负责人',
|
|
date: '2026-08-20',
|
|
rating: 4,
|
|
content: '项目经验丰富,主导过中大型前端项目。在性能优化方向有一定积累。',
|
|
tags: ['项目丰富', '性能优化'],
|
|
},
|
|
];
|
|
|
|
/** 候选人详情 */
|
|
const MOCK_CANDIDATE_DETAIL: CandidateDetail = {
|
|
id: 'candidate-1',
|
|
basicInfo: {
|
|
name: '张明远',
|
|
age: 28,
|
|
gender: '男',
|
|
experience: '5年经验',
|
|
education: '本科',
|
|
city: '北京',
|
|
phone: '138****8888',
|
|
email: 'zhang****@email.com',
|
|
jobStatus: '在职看机会',
|
|
},
|
|
matchPercent: 96,
|
|
overallScore: 88,
|
|
percentile: 78,
|
|
trend: [
|
|
{ month: '3月', score: 82 },
|
|
{ month: '4月', score: 84 },
|
|
{ month: '5月', score: 83 },
|
|
{ month: '6月', score: 85 },
|
|
{ month: '7月', score: 86 },
|
|
{ month: '8月', score: 88 },
|
|
],
|
|
abilities: MOCK_CANDIDATE_ABILITIES,
|
|
reviews: MOCK_CANDIDATE_REVIEWS,
|
|
hireSuggestions: [
|
|
{ type: 'strong', title: '强烈推荐录用', desc: '综合匹配度高,建议尽快发起 offer 流程', selected: true },
|
|
{ type: 'consider', title: '可以考虑', desc: '技术能力达标,建议进一步考察项目管理能力' },
|
|
{ type: 'reject', title: '不推荐', desc: '与岗位匹配度较低,建议再看看其他候选人' },
|
|
],
|
|
salarySuggestion: {
|
|
range: '30-33K',
|
|
level: 'P6',
|
|
note: '建议按 P6 职级定薪,可结合候选人当前薪资适当上浮',
|
|
},
|
|
invited: false,
|
|
};
|
|
|
|
/** 候选人身份覆盖(mock 阶段按 id 回填姓名/年龄/城市等基本信息,避免不同候选人显示同一份资料) */
|
|
const CANDIDATE_IDENTITY_OVERRIDE: Record<
|
|
string,
|
|
{ name: string; age: number; city: string; matchPercent: number; overallScore: number; percentile: number }
|
|
> = {
|
|
'cand-1': { name: '张明远', age: 28, city: '北京', matchPercent: 96, overallScore: 88, percentile: 99 },
|
|
'cand-2': { name: '李雨桐', age: 26, city: '北京', matchPercent: 82, overallScore: 75, percentile: 75 },
|
|
'cand-3': { name: '王建国', age: 33, city: '北京', matchPercent: 91, overallScore: 92, percentile: 92 },
|
|
};
|
|
|
|
/**
|
|
* 获取候选人详情
|
|
* @method GET
|
|
* @url /api/enterprise/candidates/:id
|
|
* @param candidateId { string } 候选人 id
|
|
* @returns Promise<CandidateDetail> 候选人详情
|
|
*/
|
|
export async function fetchCandidateDetail(candidateId: string): Promise<CandidateDetail> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/candidates/${candidateId}`, { method: 'GET' }); return res.json();
|
|
const override =
|
|
CANDIDATE_IDENTITY_OVERRIDE[candidateId] ?? CANDIDATE_IDENTITY_OVERRIDE['cand-1'];
|
|
const detail: CandidateDetail = {
|
|
...MOCK_CANDIDATE_DETAIL,
|
|
id: candidateId,
|
|
matchPercent: override.matchPercent,
|
|
overallScore: override.overallScore,
|
|
percentile: override.percentile,
|
|
basicInfo: {
|
|
...MOCK_CANDIDATE_DETAIL.basicInfo,
|
|
name: override.name,
|
|
age: override.age,
|
|
city: override.city,
|
|
},
|
|
};
|
|
return mockRequest(detail);
|
|
}
|
|
|
|
/**
|
|
* 邀请候选人面试
|
|
* @method POST
|
|
* @url /api/enterprise/candidates/:id/invite
|
|
* @param candidateId { string } 候选人 id
|
|
* @returns Promise<{ success: boolean }> 邀请结果
|
|
*/
|
|
export async function inviteCandidate(candidateId: string): Promise<{ success: boolean }> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/candidates/${candidateId}/invite`, { method: 'POST' }); return res.json();
|
|
void candidateId;
|
|
return mockRequest({ success: true });
|
|
}
|
|
|
|
/* ===================== 面试评分 ===================== */
|
|
|
|
/** 面试评分维度 */
|
|
const MOCK_INTERVIEW_SCORE_DIMENSIONS: InterviewScoreDimension[] = [
|
|
{ id: 'is1', name: '专业技能', score: 90, maxScore: 100 },
|
|
{ id: 'is2', name: '项目经验', score: 85, maxScore: 100 },
|
|
{ id: 'is3', name: '逻辑思维', score: 88, maxScore: 100 },
|
|
{ id: 'is4', name: '沟通表达', score: 92, maxScore: 100 },
|
|
{ id: 'is5', name: '团队协作', score: 80, maxScore: 100 },
|
|
{ id: 'is6', name: '学习能力', score: 95, maxScore: 100 },
|
|
{ id: 'is7', name: '抗压能力', score: 75, maxScore: 100 },
|
|
{ id: 'is8', name: '文化匹配', score: 88, maxScore: 100 },
|
|
];
|
|
|
|
/** 面试评价标签 */
|
|
const MOCK_INTERVIEW_SCORE_TAGS: InterviewScoreTag[] = [
|
|
{ id: 'tag1', label: '技术扎实', selected: true, color: 'primary' },
|
|
{ id: 'tag2', label: '表达清晰', selected: true, color: 'success' },
|
|
{ id: 'tag3', label: '项目经验丰富', selected: false, color: 'info' },
|
|
{ id: 'tag4', label: '应变能力强', selected: false, color: 'action' },
|
|
{ id: 'tag5', label: '需加强架构', selected: false, color: 'danger' },
|
|
];
|
|
|
|
/** 面试评分数据 */
|
|
const MOCK_INTERVIEW_SCORE_DATA: InterviewScoreData = {
|
|
candidateId: 'candidate-1',
|
|
candidateName: '张明远',
|
|
positionName: '高级前端工程师',
|
|
interviewTime: '2026-08-30 14:00',
|
|
overallScore: 87,
|
|
dimensions: MOCK_INTERVIEW_SCORE_DIMENSIONS,
|
|
tags: MOCK_INTERVIEW_SCORE_TAGS,
|
|
comment:
|
|
'候选人整体表现优秀,技术功底扎实,有大型项目经验。沟通表达流畅,学习能力强。对 React 底层原理有深入理解,建议录用。',
|
|
hireSuggestions: [
|
|
{ type: 'strong', title: '强烈推荐录用', desc: '综合素质优秀,建议尽快定薪发 offer', selected: true },
|
|
{ type: 'consider', title: '可以考虑', desc: '基本符合要求,建议二面进一步确认' },
|
|
{ type: 'reject', title: '不推荐', desc: '与岗位要求存在明显差距' },
|
|
],
|
|
salarySuggestion: {
|
|
range: '30-33K',
|
|
level: 'P6',
|
|
note: '建议按 P6 职级定薪,可结合候选人期望薪资协商',
|
|
},
|
|
};
|
|
|
|
/**
|
|
* 获取面试评分数据
|
|
* @method GET
|
|
* @url /api/enterprise/interviews/:id/score
|
|
* @param interviewId { string } 面试 id
|
|
* @returns Promise<InterviewScoreData> 面试评分数据
|
|
*/
|
|
export async function fetchInterviewScore(interviewId: string): Promise<InterviewScoreData> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/interviews/${interviewId}/score`, { method: 'GET' }); return res.json();
|
|
void interviewId;
|
|
return mockRequest({ ...MOCK_INTERVIEW_SCORE_DATA });
|
|
}
|
|
|
|
/**
|
|
* 提交面试评分
|
|
* @method POST
|
|
* @url /api/enterprise/interviews/:id/score
|
|
* @param interviewId { string } 面试 id
|
|
* @param payload { InterviewScoreData } 评分数据
|
|
* @returns Promise<{ success: boolean }> 提交结果
|
|
*/
|
|
export async function submitInterviewScore(
|
|
interviewId: string,
|
|
payload: InterviewScoreData,
|
|
): Promise<{ success: boolean }> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/enterprise/interviews/${interviewId}/score`, { method: 'POST', body: JSON.stringify(payload) }); return res.json();
|
|
void interviewId;
|
|
void payload;
|
|
return mockRequest({ success: true });
|
|
}
|
|
|
|
/* ===================== 运营规则配置 ===================== */
|
|
|
|
/** 匹配模式选项 */
|
|
const MOCK_MATCH_MODE_OPTIONS: MatchModeOption[] = [
|
|
{
|
|
id: 'strict',
|
|
name: '严格模式',
|
|
desc: '仅推荐高度匹配的候选人,降低误配风险,适合对岗位要求精确匹配',
|
|
},
|
|
{
|
|
id: 'recommended',
|
|
name: '推荐模式',
|
|
desc: '综合技能、经验、期望进行智能推荐,推荐质量与数量较平衡',
|
|
recommended: true,
|
|
},
|
|
{
|
|
id: 'broad',
|
|
name: '广泛模式',
|
|
desc: '更多候选人,需要人工筛选较多,适合招聘需求量大或新兴岗位',
|
|
},
|
|
];
|
|
|
|
/** 默认运营规则配置 */
|
|
const MOCK_OPERATION_RULES: OperationRules = {
|
|
matchMode: 'recommended',
|
|
matchModeOptions: MOCK_MATCH_MODE_OPTIONS,
|
|
matchThreshold: 70,
|
|
visibilityThreshold: 65,
|
|
dailyRecommendLimit: 20,
|
|
autoFilters: [
|
|
{ id: 'af-1', name: '学历硬性筛选', description: '过滤不满足学历要求的候选人', enabled: true },
|
|
{ id: 'af-2', name: '经验硬性筛选', description: '过滤不满足经验年限的候选人', enabled: true },
|
|
{ id: 'af-3', name: '薪资期望筛选', description: '过滤薪资期望超出预算上限的候选人', enabled: false },
|
|
{ id: 'af-4', name: '地域匹配筛选', description: '优先推荐期望城市与岗位城市一致的候选人', enabled: true },
|
|
{ id: 'af-5', name: '黑名单过滤', description: '自动过滤存在诚信问题的候选人', enabled: true },
|
|
],
|
|
urgentPriority: true,
|
|
campusPriority: false,
|
|
notifyNewDelivery: true,
|
|
};
|
|
|
|
/**
|
|
* 获取运营规则配置
|
|
* @method GET
|
|
* @url /api/operation/rules
|
|
* @returns Promise<OperationRules> 运营规则配置
|
|
*/
|
|
export async function fetchOperationRules(): Promise<OperationRules> {
|
|
return mockRequest({ ...MOCK_OPERATION_RULES });
|
|
}
|
|
|
|
/**
|
|
* 保存运营规则配置
|
|
* @method POST
|
|
* @url /api/operation/rules
|
|
* @param rules { OperationRules } 运营规则配置
|
|
* @returns Promise<OperationRules> 保存后的配置
|
|
*/
|
|
export async function saveOperationRules(rules: OperationRules): Promise<OperationRules> {
|
|
return mockRequest({ ...rules });
|
|
}
|
|
|
|
/* ===================== 数据审计报告 ===================== */
|
|
|
|
/** 数据审计报告 mock */
|
|
const MOCK_AUDIT_REPORT: AuditReport = {
|
|
period: '2024.09.01 - 2024.09.30',
|
|
grade: 'A',
|
|
gradeDesc: '平台整体风险可控,今日拦截异常 3 次',
|
|
todayBlocked: 3,
|
|
kpis: [
|
|
{ id: 'kpi-1', label: '风险识别数', value: 47, change: 12, unit: '个', icon: 'alert-circle-outline', color: colors.danger },
|
|
{ id: 'kpi-2', label: '审计覆盖记录', value: 128, change: 8, unit: '万', icon: 'document-text-outline', color: colors.primary },
|
|
{ id: 'kpi-3', label: '待处理异常', value: 15, change: -3, unit: '个', icon: 'time-outline', color: colors.compareOrange },
|
|
{ id: 'kpi-4', label: '合规评分', value: 98, change: 2, unit: '分', icon: 'shield-checkmark-outline', color: colors.success },
|
|
],
|
|
threatTrend: [
|
|
{ date: '09/01', riskCount: 4, handledCount: 3 },
|
|
{ date: '09/05', riskCount: 6, handledCount: 5 },
|
|
{ date: '09/09', riskCount: 3, handledCount: 3 },
|
|
{ date: '09/13', riskCount: 8, handledCount: 6 },
|
|
{ date: '09/17', riskCount: 5, handledCount: 5 },
|
|
{ date: '09/21', riskCount: 7, handledCount: 7 },
|
|
{ date: '09/25', riskCount: 4, handledCount: 4 },
|
|
{ date: '09/29', riskCount: 6, handledCount: 6 },
|
|
],
|
|
riskDistribution: [
|
|
{ id: 'rd-1', label: '虚假简历', percent: 35, color: colors.primary },
|
|
{ id: 'rd-2', label: '违规岗位', percent: 25, color: colors.danger },
|
|
{ id: 'rd-3', label: '异常沟通', percent: 20, color: colors.compareOrange },
|
|
{ id: 'rd-4', label: '刷量行为', percent: 15, color: colors.info },
|
|
{ id: 'rd-5', label: '其他', percent: 5, color: colors.textHint },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取数据审计报告
|
|
* @method GET
|
|
* @url /api/operation/audit-report
|
|
* @returns Promise<AuditReport> 数据审计报告
|
|
*/
|
|
export async function fetchAuditReport(): Promise<AuditReport> {
|
|
return mockRequest({ ...MOCK_AUDIT_REPORT });
|
|
}
|
|
|
|
/* ===================== 企业认证 ===================== */
|
|
|
|
/** 企业认证权益 */
|
|
const MOCK_ENTERPRISE_CERTIFICATION_BENEFITS: EnterpriseCertificationBenefit[] = [
|
|
{ id: 'b1', text: '主页展示企业认证标识,提升信任度' },
|
|
{ id: 'b2', text: '发布岗位数量上限提升 50%' },
|
|
{ id: 'b3', text: '优先获得优质候选人推荐' },
|
|
{ id: 'b4', text: '解锁数据分析与风控报告' },
|
|
{ id: 'b5', text: '专属客服 1 对 1 服务' },
|
|
];
|
|
|
|
/** 企业认证默认数据 */
|
|
const MOCK_ENTERPRISE_CERTIFICATION: EnterpriseCertificationData = {
|
|
status: 'unverified',
|
|
statusText: '完成企业认证,享受更多招聘权益',
|
|
benefits: MOCK_ENTERPRISE_CERTIFICATION_BENEFITS,
|
|
form: {
|
|
companyName: '',
|
|
creditCode: '',
|
|
contactName: '',
|
|
contactPhone: '',
|
|
},
|
|
};
|
|
|
|
/**
|
|
* 获取企业认证信息
|
|
* @method GET
|
|
* @url /api/enterprise/certification
|
|
* @returns Promise<EnterpriseCertificationData> 企业认证数据
|
|
*/
|
|
export async function fetchEnterpriseCertification(): Promise<EnterpriseCertificationData> {
|
|
return mockRequest({ ...MOCK_ENTERPRISE_CERTIFICATION });
|
|
}
|
|
|
|
/**
|
|
* 提交企业认证
|
|
* @method POST
|
|
* @url /api/enterprise/certification
|
|
* @param form { EnterpriseCertificationForm } 认证表单
|
|
* @returns Promise<{ success: boolean; status: EnterpriseCertificationStatus }> 提交结果
|
|
*/
|
|
export async function submitEnterpriseCertification(
|
|
form: EnterpriseCertificationForm,
|
|
): Promise<{ success: boolean; status: EnterpriseCertificationStatus }> {
|
|
void form;
|
|
return mockRequest({ success: true, status: 'pending' });
|
|
}
|
|
|
|
/* ===================== 霍兰德测评介绍 ===================== */
|
|
|
|
const MOCK_HOLLAND_INTRO: HollandIntro = {
|
|
title: '霍兰德职业兴趣测评',
|
|
subtitle: '发现你的职业兴趣类型,找到最适合你的职业方向',
|
|
totalParticipants: 124580,
|
|
duration: '约10分钟',
|
|
dimensions: [
|
|
{ type: 'R', name: '现实型', desc: '喜欢动手操作与具体任务' },
|
|
{ type: 'I', name: '研究型', desc: '喜欢分析思考与探索未知' },
|
|
{ type: 'A', name: '艺术型', desc: '喜欢创意表达与自由创作' },
|
|
{ type: 'S', name: '社会型', desc: '喜欢与人交往帮助他人' },
|
|
{ type: 'E', name: '企业型', desc: '喜欢组织领导与影响他人' },
|
|
{ type: 'C', name: '常规型', desc: '喜欢规范有序的事务处理' },
|
|
],
|
|
features: [
|
|
{ id: 'dim', icon: 'apps-outline', title: '6大兴趣维度', desc: '现实型、研究型、艺术型、社会型、企业型、常规型' },
|
|
{ id: 'time', icon: 'time-outline', title: '约10分钟', desc: '60道题目,利用碎片时间即可完成' },
|
|
{ id: 'rec', icon: 'locate-outline', title: '精准推荐', desc: '基于测评结果推荐匹配度高的岗位' },
|
|
],
|
|
advantages: [
|
|
'专业心理学理论支撑',
|
|
'个性化职业推荐报告',
|
|
'不限次数免费重做',
|
|
'结果永久保存',
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取霍兰德职业兴趣测评介绍
|
|
* @method GET
|
|
* @url /api/growth/holland-intro
|
|
* @returns Promise<HollandIntro> 测评介绍
|
|
*/
|
|
export async function fetchHollandIntro(): Promise<HollandIntro> {
|
|
return mockRequest(MOCK_HOLLAND_INTRO);
|
|
}
|
|
|
|
/* ===================== 岗位管理(企业端) ===================== */
|
|
|
|
const MOCK_JOB_MANAGEMENT: JobManagement = {
|
|
companyName: '智联科技',
|
|
hasData: true,
|
|
stats: [
|
|
{ id: 'active', label: '在招岗位', value: 12, change: '+2', icon: 'briefcase-outline', color: 'primary' },
|
|
{ id: 'today', label: '今日新增投递', value: 48, change: '+23%', icon: 'mail-outline', color: 'success' },
|
|
{ id: 'pending', label: '待处理简历', value: 23, change: '-5', icon: 'document-text-outline', color: 'action' },
|
|
{ id: 'interview', label: '即将面试', value: 8, change: '已安排', icon: 'calendar-outline', color: 'info' },
|
|
],
|
|
tabs: [
|
|
{ key: 'all', label: '全部', count: 12 },
|
|
{ key: 'active', label: '招聘中', count: 8 },
|
|
{ key: 'offline', label: '已下线', count: 3 },
|
|
{ key: 'draft', label: '草稿', count: 1 },
|
|
],
|
|
jobs: [
|
|
{
|
|
id: 'job-1',
|
|
title: '高级前端工程师',
|
|
salary: '25-35K',
|
|
salaryMonths: 14,
|
|
city: '北京',
|
|
experience: '3-5年',
|
|
education: '本科',
|
|
tags: ['React', 'TypeScript', 'Node.js'],
|
|
status: 'active',
|
|
department: '技术部',
|
|
headcount: 2,
|
|
overallScore: 88,
|
|
rankText: '同岗排名 Top 3%',
|
|
applicants: 128,
|
|
views: 2300,
|
|
matches: 56,
|
|
},
|
|
{
|
|
id: 'job-2',
|
|
title: '产品经理',
|
|
salary: '20-30K',
|
|
salaryMonths: 13,
|
|
city: '北京',
|
|
experience: '3-5年',
|
|
education: '本科',
|
|
tags: ['B端', '数据产品'],
|
|
status: 'active',
|
|
department: '产品部',
|
|
headcount: 1,
|
|
overallScore: 75,
|
|
rankText: '同岗排名 Top 25%',
|
|
applicants: 86,
|
|
views: 1800,
|
|
matches: 32,
|
|
},
|
|
{
|
|
id: 'job-3',
|
|
title: 'Java后端开发',
|
|
salary: '30-45K',
|
|
salaryMonths: 15,
|
|
city: '北京',
|
|
experience: '5-10年',
|
|
education: '本科',
|
|
tags: ['Java', 'Spring Boot', 'MySQL'],
|
|
status: 'active',
|
|
department: '技术部',
|
|
headcount: 3,
|
|
overallScore: 92,
|
|
rankText: '同岗排名 Top 2%',
|
|
applicants: 156,
|
|
views: 3100,
|
|
matches: 78,
|
|
},
|
|
],
|
|
benefits: [
|
|
'接收候选人主动投递',
|
|
'使用AI智能匹配推荐人才',
|
|
'在线沟通与面试安排',
|
|
'查看岗位竞争力排名',
|
|
'获取招聘数据看板',
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取企业端岗位管理数据
|
|
* @method GET
|
|
* @url /api/enterprise/job-management
|
|
* @returns Promise<JobManagement> 岗位管理数据
|
|
*/
|
|
export async function fetchJobManagement(): Promise<JobManagement> {
|
|
return mockRequest(MOCK_JOB_MANAGEMENT);
|
|
}
|
|
|
|
/* ===================== 人才推荐(企业端) ===================== */
|
|
|
|
const MOCK_TALENT_RECOMMENDATION: TalentRecommendation = {
|
|
companyName: '智联科技',
|
|
hasData: true,
|
|
todayCount: 15,
|
|
tabs: [
|
|
{ key: 'all', label: '全部', count: 12 },
|
|
{ key: 'high', label: '匹配度高', count: 8 },
|
|
{ key: '985211', label: '985/211', count: 5 },
|
|
{ key: '5years', label: '5年以上', count: 5 },
|
|
{ key: 'master', label: '硕士及以上', count: 3 },
|
|
],
|
|
candidates: [
|
|
{
|
|
id: 'cand-1',
|
|
name: '张明远',
|
|
avatar: '张',
|
|
title: '高级前端工程师 · 5年经验',
|
|
experience: '5年经验',
|
|
age: 28,
|
|
school: '北京大学',
|
|
city: '北京',
|
|
expectedSalary: '期望薪资 30-35K',
|
|
matchPercent: 96,
|
|
overallScore: 88,
|
|
percentile: 99,
|
|
tags: ['React', 'TypeScript', 'Node.js'],
|
|
},
|
|
{
|
|
id: 'cand-2',
|
|
name: '李雨桐',
|
|
avatar: '李',
|
|
title: '产品经理 · 3年经验',
|
|
experience: '3年经验',
|
|
age: 26,
|
|
school: '上海交通大学',
|
|
city: '北京',
|
|
expectedSalary: '期望薪资 25-30K',
|
|
matchPercent: 82,
|
|
overallScore: 75,
|
|
percentile: 75,
|
|
tags: ['大厂背景', '项目经验', '沟通能力强'],
|
|
},
|
|
{
|
|
id: 'cand-3',
|
|
name: '王建国',
|
|
avatar: '王',
|
|
title: 'Java后端开发 · 7年经验',
|
|
experience: '7年经验',
|
|
age: 33,
|
|
school: '浙江大学',
|
|
city: '北京',
|
|
expectedSalary: '期望薪资 35-45K',
|
|
matchPercent: 91,
|
|
overallScore: 92,
|
|
percentile: 92,
|
|
tags: ['技术扎实', '经验丰富', '高绩效'],
|
|
},
|
|
],
|
|
poolStats: [
|
|
{ id: 'active', value: '12,580', label: '活跃候选人' },
|
|
{ id: 'new', value: '326', label: '今日新增' },
|
|
{ id: 'matched', value: '4,820', label: '匹配人才' },
|
|
],
|
|
recommendedActions: [
|
|
'降低学历要求,候选人数 +35%',
|
|
'放宽经验要求,候选人数 +50%',
|
|
'开启广泛模式,获取更多推荐',
|
|
'发布更多岗位信息,吸引主动投递',
|
|
],
|
|
dataReference: [
|
|
{ label: '主动联系简历数比被动接收高', value: '60%' },
|
|
{ label: '24小时内回复的面试转化率提升', value: '2.3倍' },
|
|
{ label: '完善企业资料可提升候选人信任度', value: '45%' },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取企业端人才推荐数据
|
|
* @method GET
|
|
* @url /api/enterprise/talent-recommendation
|
|
* @returns Promise<TalentRecommendation> 人才推荐数据
|
|
*/
|
|
export async function fetchTalentRecommendation(): Promise<TalentRecommendation> {
|
|
return mockRequest(MOCK_TALENT_RECOMMENDATION);
|
|
}
|
|
|
|
/* ===================== 企业沟通 ===================== */
|
|
|
|
const MOCK_ENTERPRISE_COMMUNICATION: EnterpriseCommunication = {
|
|
companyName: '智联科技',
|
|
hasData: true,
|
|
pendingTasks: [
|
|
{ id: 'reply', label: '待回复', count: 3, hint: '24小时内未回复', icon: 'chatbubble-ellipses-outline', color: 'action' },
|
|
{ id: 'score', label: '待评分', count: 2, hint: '面试结束待及时评分', icon: 'star-outline', color: 'compareOrange' },
|
|
],
|
|
tabs: [
|
|
{ key: 'all', label: '全部', count: 12 },
|
|
{ key: 'pending', label: '未回复', count: 3 },
|
|
{ key: 'interview', label: '面试邀请', count: 2 },
|
|
{ key: 'offer', label: 'Offer沟通', count: 1 },
|
|
{ key: 'closed', label: '已归档', count: 4 },
|
|
],
|
|
chats: [
|
|
{
|
|
id: 'chat-1',
|
|
userId: 'u-1',
|
|
name: '张明远',
|
|
avatar: '张',
|
|
title: '高级前端工程师 · 最近活跃',
|
|
lastMessage: '你的简历很符合我们的要求,方便下周面试吗?',
|
|
time: '10分钟前',
|
|
unreadCount: 1,
|
|
labels: [],
|
|
},
|
|
{
|
|
id: 'chat-2',
|
|
userId: 'u-2',
|
|
name: '李雨桐',
|
|
avatar: '李',
|
|
title: '产品经理',
|
|
lastMessage: '你好,我看贵司的产品经理岗位很匹配,想进一步沟通。',
|
|
time: '30分钟前',
|
|
unreadCount: 0,
|
|
labels: [],
|
|
},
|
|
{
|
|
id: 'chat-3',
|
|
userId: 'u-3',
|
|
name: '王建国',
|
|
avatar: '王',
|
|
title: 'Java后端开发',
|
|
lastMessage: '已收到面试邀请,确认参加。',
|
|
time: '2小时前',
|
|
unreadCount: 0,
|
|
labels: ['面试待评分'],
|
|
},
|
|
],
|
|
dataReference: [
|
|
{ label: '主动联系简历数比被动接收高', value: '60%' },
|
|
{ label: '24小时内回复的面试转化率提升', value: '2.3倍' },
|
|
{ label: '完善企业资料可提升候选人信任度', value: '45%' },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取企业端沟通数据
|
|
* @method GET
|
|
* @url /api/enterprise/communication
|
|
* @returns Promise<EnterpriseCommunication> 沟通数据
|
|
*/
|
|
export async function fetchEnterpriseCommunication(): Promise<EnterpriseCommunication> {
|
|
return mockRequest(MOCK_ENTERPRISE_COMMUNICATION);
|
|
}
|
|
|
|
/* ===================== 岗位评价管理空状态 ===================== */
|
|
|
|
const MOCK_JOB_EVALUATION_EMPTY: JobEvaluationEmptyState = {
|
|
hasData: true,
|
|
tabs: [
|
|
{ key: 'all', label: '全部', count: 0 },
|
|
{ key: 'positive', label: '好评', count: 0 },
|
|
{ key: 'neutral', label: '中评', count: 0 },
|
|
{ key: 'negative', label: '差评', count: 0 },
|
|
{ key: 'pending', label: '待回复', count: 0 },
|
|
],
|
|
emptyTitle: '还没有收到评价',
|
|
emptySubtitle: '候选人面试后可以对岗位进行评价,评价越多,企业可信度越高',
|
|
tips: [
|
|
'及时回复候选人消息',
|
|
'面试安排专业有序',
|
|
'薪资福利透明公开',
|
|
'真诚对待每一位候选人',
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取岗位评价管理空状态数据
|
|
* @method GET
|
|
* @url /api/enterprise/job-evaluation-empty
|
|
* @returns Promise<JobEvaluationEmptyState> 空状态数据
|
|
*/
|
|
export async function fetchJobEvaluationEmptyState(): Promise<JobEvaluationEmptyState> {
|
|
return mockRequest(MOCK_JOB_EVALUATION_EMPTY);
|
|
}
|
|
|
|
/* ===================== 选择身份 ===================== */
|
|
|
|
/** 身份选项(C 端求职者 / 企业端 HR) */
|
|
const MOCK_IDENTITY_OPTIONS: IdentityOption[] = [
|
|
{
|
|
id: 'candidate',
|
|
title: '我是求职者',
|
|
desc: '浏览岗位、查看公司评分、对比 Offer、提升竞争力',
|
|
icon: 'school-outline',
|
|
role: 'candidate',
|
|
},
|
|
{
|
|
id: 'hr',
|
|
title: '我是企业 / HR',
|
|
desc: '发布岗位、筛选人才、管理评价、展示企业形象',
|
|
icon: 'briefcase-outline',
|
|
role: 'enterprise',
|
|
},
|
|
];
|
|
|
|
/**
|
|
* 获取身份选项(首次启动 / 身份切换)
|
|
* @method GET
|
|
* @url /api/tools/identity-options
|
|
* @returns Promise<IdentityOption[]> 身份选项数组
|
|
*/
|
|
export async function fetchIdentityOptions(): Promise<IdentityOption[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/tools/identity-options`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_IDENTITY_OPTIONS);
|
|
}
|
|
|
|
/* ===================== 通关小游戏 ===================== */
|
|
|
|
/** 通关小游戏 7 关配置 */
|
|
const MOCK_GAME_LEVELS: GameLevel[] = [
|
|
{
|
|
id: 'self',
|
|
order: 1,
|
|
title: '认识自己',
|
|
subtitle: '性格与基本信息',
|
|
icon: 'person-outline',
|
|
description: '收集性格、年龄、学历、专业、学校、住址、兴趣爱好等基本信息,建立求职画像。',
|
|
nextLevel: 'ability',
|
|
questions: [
|
|
{
|
|
id: 'self-1',
|
|
title: '你目前最高学历是?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '高中/中专及以下', isCorrect: false },
|
|
{ id: 'b', text: '大专', isCorrect: false },
|
|
{ id: 'c', text: '本科', isCorrect: true },
|
|
{ id: 'd', text: '硕士及以上', isCorrect: true },
|
|
],
|
|
explanation: '学历是岗位筛选的重要门槛,准确填写有助于平台匹配对应层级的岗位。',
|
|
tags: ['学历'],
|
|
},
|
|
{
|
|
id: 'self-2',
|
|
title: '你希望未来 3 年在哪个城市工作?',
|
|
type: 'input',
|
|
placeholder: '例如:北京、上海、杭州',
|
|
explanation: '城市选择直接影响薪资预期与生活成本,建议结合行业聚集地进行选择。',
|
|
tags: ['城市'],
|
|
},
|
|
{
|
|
id: 'self-3',
|
|
title: '你最擅长的 3 项技能或优势是什么?',
|
|
type: 'input',
|
|
placeholder: '如:文案写作、数据分析、沟通协调',
|
|
explanation: '清晰描述自身优势,是定位岗位与撰写简历的第一步,建议结合具体事例。',
|
|
tags: ['自我认知'],
|
|
},
|
|
{
|
|
id: 'self-4',
|
|
title: '以下哪些做法有助于明确职业方向?(多选)',
|
|
type: 'multiple',
|
|
options: [
|
|
{ id: 'a', text: '做职业测评并结合兴趣', isCorrect: true },
|
|
{ id: 'b', text: '通过实习/兼职真实体验', isCorrect: true },
|
|
{ id: 'c', text: '盲目跟风最热门的岗位', isCorrect: false },
|
|
{ id: 'd', text: '完全由他人替你决定', isCorrect: false },
|
|
],
|
|
explanation: '测评、实践与自我反思结合,才能找到真正匹配的方向;盲从他人容易走偏。',
|
|
tags: ['职业方向'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'ability',
|
|
order: 2,
|
|
title: '认识能力',
|
|
subtitle: '技能与成果',
|
|
icon: 'barbell-outline',
|
|
description: '明确自身具备的技能与可提升空间,包括项目开发、竞赛比赛、奖项、技能等维度。',
|
|
nextLevel: 'job',
|
|
questions: [
|
|
{
|
|
id: 'ability-1',
|
|
title: '以下哪些属于可写入简历的硬技能证明?(多选)',
|
|
type: 'multiple',
|
|
options: [
|
|
{ id: 'a', text: 'GitHub 开源项目', isCorrect: true },
|
|
{ id: 'b', text: '学科竞赛获奖', isCorrect: true },
|
|
{ id: 'c', text: '微信朋友圈活跃', isCorrect: false },
|
|
{ id: 'd', text: '专业认证/资格证书', isCorrect: true },
|
|
],
|
|
explanation: '开源项目、竞赛获奖、专业认证都是简历中极具说服力的硬技能证明。',
|
|
tags: ['技能证明'],
|
|
},
|
|
{
|
|
id: 'ability-2',
|
|
title: '以下哪些方式能有效提升简历的含金量?(多选)',
|
|
type: 'multiple',
|
|
options: [
|
|
{ id: 'a', text: '参与真实项目并沉淀成果', isCorrect: true },
|
|
{ id: 'b', text: '考取行业权威证书', isCorrect: true },
|
|
{ id: 'c', text: '撰写技术博客 / 作品集', isCorrect: true },
|
|
{ id: 'd', text: '花钱买来路不明的"水证"', isCorrect: false },
|
|
],
|
|
explanation: '真实项目、权威证书与可展示的作品,是简历含金量最硬的支撑。',
|
|
tags: ['能力提升'],
|
|
},
|
|
{
|
|
id: 'ability-3',
|
|
title: '描述一段经历时,最能体现能力的写法是?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '只写"负责相关工作"', isCorrect: false },
|
|
{ id: 'b', text: '用 STAR 法则并量化结果', isCorrect: true },
|
|
{ id: 'c', text: '堆砌"吃苦耐劳"等形容词', isCorrect: false },
|
|
{ id: 'd', text: '直接复制他人经历', isCorrect: false },
|
|
],
|
|
explanation: '情境-任务-行动-结果 + 数据,让能力可被验证。',
|
|
tags: ['能力表达'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'job',
|
|
order: 3,
|
|
title: '认识岗位',
|
|
subtitle: '职责与发展',
|
|
icon: 'briefcase-outline',
|
|
description: '了解不同岗位的职责要求、发展路径和薪资水平,明确目标岗位。',
|
|
nextLevel: 'company',
|
|
questions: [
|
|
{
|
|
id: 'job-1',
|
|
title: '在筛选岗位时,最应优先关注的是?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '公司离地铁站的距离', isCorrect: false },
|
|
{ id: 'b', text: '岗位 JD 中的核心技能要求', isCorrect: true },
|
|
{ id: 'c', text: '办公室装修风格', isCorrect: false },
|
|
{ id: 'd', text: '招聘人员的头像', isCorrect: false },
|
|
],
|
|
explanation: '岗位 JD 中的核心技能要求决定了你是否能胜任,是筛选岗位的首要依据。',
|
|
tags: ['岗位理解'],
|
|
},
|
|
{
|
|
id: 'job-2',
|
|
title: '阅读 JD(岗位描述)时,应重点提取哪些信息?(多选)',
|
|
type: 'multiple',
|
|
options: [
|
|
{ id: 'a', text: '核心职责与任职要求', isCorrect: true },
|
|
{ id: 'b', text: '薪资范围与结构', isCorrect: true },
|
|
{ id: 'c', text: '发展空间与汇报线', isCorrect: true },
|
|
{ id: 'd', text: '公司团建照片', isCorrect: false },
|
|
],
|
|
explanation: '职责、要求、薪资、发展是判断"该不该投"的关键。',
|
|
tags: ['岗位理解'],
|
|
},
|
|
{
|
|
id: 'job-3',
|
|
title: '你最想从事的岗位名称是?',
|
|
type: 'input',
|
|
placeholder: '如:前端工程师 / 产品经理',
|
|
explanation: '明确目标岗位名,才能精准匹配 JD 并针对性准备面试。',
|
|
tags: ['目标岗位'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'company',
|
|
order: 4,
|
|
title: '认识企业',
|
|
subtitle: '文化与前景',
|
|
icon: 'business-outline',
|
|
description: '了解目标企业的背景、文化、发展前景,学会通过公开信息判断企业是否靠谱。',
|
|
nextLevel: 'resume',
|
|
questions: [
|
|
{
|
|
id: 'company-1',
|
|
title: '判断一家企业是否靠谱,最不值得参考的是?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '工商注册信息与经营年限', isCorrect: false },
|
|
{ id: 'b', text: '员工真实评价与离职率', isCorrect: false },
|
|
{ id: 'c', text: '公司官网口号与宣传视频', isCorrect: true },
|
|
{ id: 'd', text: '融资阶段与核心业务', isCorrect: false },
|
|
],
|
|
explanation: '官网口号往往经过包装,工商信息、员工评价、业务健康度才是判断企业的硬指标。',
|
|
tags: ['企业判断'],
|
|
},
|
|
{
|
|
id: 'company-2',
|
|
title: '以下哪些是判断企业风险的信号?(多选)',
|
|
type: 'multiple',
|
|
options: [
|
|
{ id: 'a', text: '频繁大规模裁员', isCorrect: true },
|
|
{ id: 'b', text: '社保公积金按最低基数缴纳', isCorrect: true },
|
|
{ id: 'c', text: '劳动纠纷频发', isCorrect: true },
|
|
{ id: 'd', text: '公司前台装修豪华', isCorrect: false },
|
|
],
|
|
explanation: '裁员、社保、纠纷是真实风险信号,装修不能代表经营健康。',
|
|
tags: ['风险识别'],
|
|
},
|
|
{
|
|
id: 'company-3',
|
|
title: '了解一家公司真实口碑,最靠谱的渠道是?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '公司官网与招聘海报', isCorrect: false },
|
|
{ id: 'b', text: '在职/离职员工匿名评价 + 脉脉/看准等平台', isCorrect: true },
|
|
{ id: 'c', text: '公司年会视频', isCorrect: false },
|
|
{ id: 'd', text: '老板个人朋友圈', isCorrect: false },
|
|
],
|
|
explanation: '一线员工的真实评价,比官方宣传更接近事实。',
|
|
tags: ['口碑调研'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'resume',
|
|
order: 5,
|
|
title: '认识简历',
|
|
subtitle: '制作与优化',
|
|
icon: 'document-text-outline',
|
|
description: '借助 AI 助手了解简历制作的过程及要求,学习如何写出高通过率的简历。',
|
|
nextLevel: 'interview',
|
|
questions: [
|
|
{
|
|
id: 'resume-1',
|
|
title: '简历中描述项目经历的最佳写法是?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '负责前端开发', isCorrect: false },
|
|
{ id: 'b', text: '使用 STAR 法则,量化结果', isCorrect: true },
|
|
{ id: 'c', text: '罗列所有课程名称', isCorrect: false },
|
|
{ id: 'd', text: '写满三页纸', isCorrect: false },
|
|
],
|
|
explanation: 'STAR 法则(情境-任务-行动-结果)加量化数据,是项目经历最说服人的写法。',
|
|
tags: ['简历'],
|
|
},
|
|
{
|
|
id: 'resume-2',
|
|
title: '简历中应避免哪些问题?(多选)',
|
|
type: 'multiple',
|
|
options: [
|
|
{ id: 'a', text: '错别字与排版混乱', isCorrect: true },
|
|
{ id: 'b', text: '虚构或夸大经历', isCorrect: true },
|
|
{ id: 'c', text: '与岗位无关内容过多', isCorrect: true },
|
|
{ id: 'd', text: '用数据量化成果', isCorrect: false },
|
|
],
|
|
explanation: '错字、造假、跑题会直接减分;量化成果才是加分项。',
|
|
tags: ['简历避坑'],
|
|
},
|
|
{
|
|
id: 'resume-3',
|
|
title: '针对不同岗位投递,简历应?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '一份简历投遍所有岗位', isCorrect: false },
|
|
{ id: 'b', text: '按 JD 关键词针对性修改', isCorrect: true },
|
|
{ id: 'c', text: '大量使用花哨模板', isCorrect: false },
|
|
{ id: 'd', text: '隐瞒关键经历', isCorrect: false },
|
|
],
|
|
explanation: '针对性修改能显著提升匹配度与通过率。',
|
|
tags: ['简历优化'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'interview',
|
|
order: 6,
|
|
title: '认识面试',
|
|
subtitle: '模拟与技巧',
|
|
icon: 'chatbubbles-outline',
|
|
description: '借助 AI 助手开展模拟面试,提前熟悉真实面试场景和常见问题。',
|
|
nextLevel: 'offer',
|
|
questions: [
|
|
{
|
|
id: 'interview-1',
|
|
title: '面试中被问到「你最大的缺点是什么」,合适的回答是?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '我没有缺点', isCorrect: false },
|
|
{ id: 'b', text: '选择一个真实但无关岗位核心能力的缺点,并说明改进措施', isCorrect: true },
|
|
{ id: 'c', text: '抱怨上一家公司', isCorrect: false },
|
|
{ id: 'd', text: '沉默不答', isCorrect: false },
|
|
],
|
|
explanation: '坦诚展示自我认知与改进计划,能体现你的成长型思维。',
|
|
tags: ['面试'],
|
|
},
|
|
{
|
|
id: 'interview-2',
|
|
title: '面试前应如何准备?(多选)',
|
|
type: 'multiple',
|
|
options: [
|
|
{ id: 'a', text: '研究公司与岗位背景', isCorrect: true },
|
|
{ id: 'b', text: '准备 STAR 案例', isCorrect: true },
|
|
{ id: 'c', text: '模拟常见题型', isCorrect: true },
|
|
{ id: 'd', text: '完全不准备靠临场发挥', isCorrect: false },
|
|
],
|
|
explanation: '研究、案例、模拟三者结合,临场更稳。',
|
|
tags: ['面试准备'],
|
|
},
|
|
{
|
|
id: 'interview-3',
|
|
title: '被问期望薪资时,合适的做法是?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '完全不敢谈,随便应允', isCorrect: false },
|
|
{ id: 'b', text: '提前调研行业薪资区间后给范围', isCorrect: true },
|
|
{ id: 'c', text: '直接报一个离谱高价', isCorrect: false },
|
|
{ id: 'd', text: '说"你定就行"', isCorrect: false },
|
|
],
|
|
explanation: '基于调研给区间,既体现专业也保护自身权益。',
|
|
tags: ['薪资谈判'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'offer',
|
|
order: 7,
|
|
title: '认识签约',
|
|
subtitle: 'Offer 与入职',
|
|
icon: 'file-tray-full-outline',
|
|
description: '学习 Offer 选择、入职准备、试用期注意事项、合同签订等关键环节。',
|
|
questions: [
|
|
{
|
|
id: 'offer-1',
|
|
title: '签订劳动合同前,必须确认的关键条款不包括?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '薪资结构与发放时间', isCorrect: false },
|
|
{ id: 'b', text: '试用期时长与转正条件', isCorrect: false },
|
|
{ id: 'c', text: '公司团建频率', isCorrect: true },
|
|
{ id: 'd', text: '工作岗位与工作地点', isCorrect: false },
|
|
],
|
|
explanation: '团建频率不属于劳动合同必备条款,薪资、试用期、岗位地点才是核心。',
|
|
tags: ['签约'],
|
|
},
|
|
{
|
|
id: 'offer-2',
|
|
title: '签订劳动合同时应关注的合法权益包括?(多选)',
|
|
type: 'multiple',
|
|
options: [
|
|
{ id: 'a', text: '五险一金缴纳', isCorrect: true },
|
|
{ id: 'b', text: '试用期时长合规', isCorrect: true },
|
|
{ id: 'c', text: '加班与休假规定', isCorrect: true },
|
|
{ id: 'd', text: '公司团建频率', isCorrect: false },
|
|
],
|
|
explanation: '社保、试用期、工时休假是法定权益核心。',
|
|
tags: ['签约权益'],
|
|
},
|
|
{
|
|
id: 'offer-3',
|
|
title: '关于试用期,下列说法正确的是?',
|
|
type: 'single',
|
|
options: [
|
|
{ id: 'a', text: '试用期可随意辞退且不补偿', isCorrect: false },
|
|
{ id: 'b', text: '试用期最长不超 6 个月且须缴社保', isCorrect: true },
|
|
{ id: 'c', text: '试用期不缴社保是行业惯例合法', isCorrect: false },
|
|
{ id: 'd', text: '试用期长短由公司随便定', isCorrect: false },
|
|
],
|
|
explanation: '法律对试用期上限与社保有明确规定,不能随意。',
|
|
tags: ['试用期'],
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
/** 默认游戏进度:第一关可玩 */
|
|
const DEFAULT_GAME_PROGRESS: GameProgress = {
|
|
currentLevel: 'self',
|
|
unlockedLevels: ['self'],
|
|
results: [],
|
|
totalPercent: 0,
|
|
};
|
|
|
|
/** 内存中的游戏进度(mock 阶段用,刷新后重置) */
|
|
let MOCK_GAME_PROGRESS: GameProgress = { ...DEFAULT_GAME_PROGRESS };
|
|
|
|
/**
|
|
* 获取所有关卡配置
|
|
* @method GET
|
|
* @url /api/game/levels
|
|
* @returns Promise<GameLevel[]> 关卡数组
|
|
*/
|
|
export async function fetchGameLevels(): Promise<GameLevel[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/game/levels`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_GAME_LEVELS);
|
|
}
|
|
|
|
/**
|
|
* 获取当前游戏进度
|
|
* @method GET
|
|
* @url /api/game/progress
|
|
* @returns Promise<GameProgress> 游戏进度
|
|
*/
|
|
export async function fetchGameProgress(): Promise<GameProgress> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/game/progress`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_GAME_PROGRESS);
|
|
}
|
|
|
|
/**
|
|
* 提交关卡答案并计算结果
|
|
* @method POST
|
|
* @url /api/game/levels/:levelId/submit
|
|
* @param payload {SubmitGameLevelPayload} 关卡 id 与作答
|
|
* @returns Promise<GameProgress> 更新后的游戏进度
|
|
*/
|
|
export async function submitGameLevel(
|
|
payload: SubmitGameLevelPayload,
|
|
): Promise<GameProgress> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/game/levels/${payload.levelId}/submit`, { method: 'POST', body: JSON.stringify(payload) }); return res.json();
|
|
const level = MOCK_GAME_LEVELS.find((l) => l.id === payload.levelId);
|
|
if (!level) {
|
|
return mockRequest(MOCK_GAME_PROGRESS);
|
|
}
|
|
|
|
let correctCount = 0;
|
|
payload.answers.forEach((answer) => {
|
|
const question = level.questions.find((q) => q.id === answer.questionId);
|
|
if (!question) return;
|
|
if (question.type === 'input') {
|
|
// 输入题只要有内容即算完成(不判定对错)
|
|
if (answer.inputValue && answer.inputValue.trim().length > 0) correctCount += 1;
|
|
} else {
|
|
const correctIds =
|
|
question.options?.filter((o) => o.isCorrect).map((o) => o.id) ?? [];
|
|
const isCorrect =
|
|
correctIds.length > 0 &&
|
|
correctIds.length === answer.selectedIds.length &&
|
|
correctIds.every((id) => answer.selectedIds.includes(id));
|
|
if (isCorrect) correctCount += 1;
|
|
answer.isCorrect = isCorrect;
|
|
}
|
|
});
|
|
|
|
const score = level.questions.length > 0 ? Math.round((correctCount / level.questions.length) * 100) : 100;
|
|
const completed = score >= 60;
|
|
|
|
const existingIndex = MOCK_GAME_PROGRESS.results.findIndex((r) => r.levelId === payload.levelId);
|
|
const result: GameLevelResult = {
|
|
levelId: payload.levelId,
|
|
completed,
|
|
score,
|
|
answers: payload.answers,
|
|
completedAt: completed ? new Date().toISOString() : undefined,
|
|
};
|
|
|
|
if (existingIndex >= 0) {
|
|
MOCK_GAME_PROGRESS.results[existingIndex] = result;
|
|
} else {
|
|
MOCK_GAME_PROGRESS.results.push(result);
|
|
}
|
|
|
|
if (completed && level.nextLevel && !MOCK_GAME_PROGRESS.unlockedLevels.includes(level.nextLevel)) {
|
|
MOCK_GAME_PROGRESS.unlockedLevels.push(level.nextLevel);
|
|
MOCK_GAME_PROGRESS.currentLevel = level.nextLevel;
|
|
}
|
|
|
|
const completedCount = MOCK_GAME_PROGRESS.results.filter((r) => r.completed).length;
|
|
MOCK_GAME_PROGRESS.totalPercent = Math.round((completedCount / MOCK_GAME_LEVELS.length) * 100);
|
|
|
|
return mockRequest(MOCK_GAME_PROGRESS);
|
|
}
|
|
|
|
/**
|
|
* 重置游戏进度(仅 mock 阶段方便演示)
|
|
* @method POST
|
|
* @url /api/game/reset
|
|
* @returns Promise<GameProgress> 重置后的进度
|
|
*/
|
|
export async function resetGameProgress(): Promise<GameProgress> {
|
|
MOCK_GAME_PROGRESS = { ...DEFAULT_GAME_PROGRESS };
|
|
return mockRequest(MOCK_GAME_PROGRESS);
|
|
}
|
|
|
|
/* ===================== 阿布阿诺 AI 法律服务助手 ===================== */
|
|
|
|
/** 阿布阿诺品牌信息 */
|
|
const MOCK_LEGAL_BRAND: LegalBrand = {
|
|
name: '阿布阿诺',
|
|
company: '阿布阿诺人工智能科技有限公司',
|
|
slogan: '求职路上,法律护航',
|
|
logo: '🛡️',
|
|
};
|
|
|
|
/** 法律服务入口列表 */
|
|
const MOCK_LEGAL_SERVICES: LegalService[] = [
|
|
{
|
|
id: 'free-consult',
|
|
title: '免费法律咨询',
|
|
description: '劳动权益、劳动合同、求职陷阱等基础法律问题快速解答。',
|
|
icon: 'chatbubble-ellipses-outline',
|
|
type: 'free',
|
|
action: 'chat',
|
|
},
|
|
{
|
|
id: 'free-templates',
|
|
title: '文书模板下载',
|
|
description: '劳动合同审查清单、离职申请书、劳动仲裁申请书等常用模板。',
|
|
icon: 'document-attach-outline',
|
|
type: 'free',
|
|
action: 'templates',
|
|
},
|
|
{
|
|
id: 'free-faq',
|
|
title: '常见问题速查',
|
|
description: '试用期被辞退、拖欠工资、不签劳动合同等高频问题。',
|
|
icon: 'help-circle-outline',
|
|
type: 'free',
|
|
action: 'faq',
|
|
},
|
|
{
|
|
id: 'member-lawyer',
|
|
title: '一对一律师咨询',
|
|
description: '专业劳动法律师在线答疑,定制解决方案。',
|
|
icon: 'people-outline',
|
|
type: 'member',
|
|
action: 'upgrade',
|
|
requestHint: '请简要描述你遇到的劳动法律问题,例如试用期被辞退、拖欠工资、未签劳动合同等,律师将在 24 小时内响应。',
|
|
},
|
|
{
|
|
id: 'member-review',
|
|
title: '合同/文书审核',
|
|
description: '劳动合同、竞业协议、保密协议等深度审核与风险提示。',
|
|
icon: 'reader-outline',
|
|
type: 'member',
|
|
action: 'upgrade',
|
|
requestHint: '请说明需要审核的文书类型与你重点关注的风险点,例如劳动合同中的违约金条款、竞业限制范围等。',
|
|
},
|
|
{
|
|
id: 'member-advisor',
|
|
title: '专属法律顾问',
|
|
description: '全年专属法律顾问,随时响应求职与职场法律问题。',
|
|
icon: 'shield-checkmark-outline',
|
|
type: 'member',
|
|
action: 'upgrade',
|
|
requestHint: '请描述你希望顾问长期跟进的事项,例如在职期间的权益保障、跳槽风险把控等。',
|
|
},
|
|
];
|
|
|
|
/** 法律常见问题 */
|
|
const MOCK_LEGAL_FAQS: LegalFAQ[] = [
|
|
{
|
|
id: 'faq-1',
|
|
question: '试用期被辞退,公司需要赔偿吗?',
|
|
answer: '如果公司无法证明你「不符合录用条件」,属于违法解除,可要求赔偿金(2N)或继续履行合同。',
|
|
category: '试用期',
|
|
},
|
|
{
|
|
id: 'faq-2',
|
|
question: '入职后公司一直不签劳动合同怎么办?',
|
|
answer: '自用工之日起超过一个月不满一年未签合同的,劳动者可主张每月二倍工资差额。',
|
|
category: '劳动合同',
|
|
},
|
|
{
|
|
id: 'faq-3',
|
|
question: '公司拖欠工资,我可以直接离职吗?',
|
|
answer: '用人单位未及时足额支付劳动报酬的,劳动者可以解除劳动合同并要求经济补偿金(N)。',
|
|
category: '工资',
|
|
},
|
|
{
|
|
id: 'faq-4',
|
|
question: 'Offer 上写的薪资和合同不一致,以哪个为准?',
|
|
answer: '一般以劳动合同为准,但 Offer、聊天记录、邮件等也可作为佐证。建议入职前要求书面确认薪资结构。',
|
|
category: 'Offer',
|
|
},
|
|
];
|
|
|
|
/** 法律文书模板 */
|
|
const MOCK_LEGAL_TEMPLATES: LegalTemplate[] = [
|
|
{
|
|
id: 'tmpl-1',
|
|
name: '劳动合同审查清单',
|
|
description: '入职前必查 12 项关键条款',
|
|
icon: 'checkbox-outline',
|
|
url: 'https://dev.api.com/legal/templates/contract-checklist',
|
|
content: `劳动合同审查清单(入职前必查 12 项)
|
|
|
|
1. 用人单位名称、法定代表人、注册地址是否与营业执照一致
|
|
2. 合同期限(固定/无固定/以完成一定工作任务为期限)与试用期约定是否合法(试用期不超合同期 1/12,且最长 6 个月)
|
|
3. 工作地点、岗位名称、工作内容是否明确,有无「公司可随意调岗」等霸王条款
|
|
4. 工时制度(标准/综合计算/不定时)及加班工资计算基数与倍数
|
|
5. 劳动报酬:基本工资、绩效、补贴构成,发放时间与方式
|
|
6. 社会保险与住房公积金的缴纳基数、比例、起始时间
|
|
7. 试用期工资是否不低于转正工资 80% 且不低于当地最低工资
|
|
8. 竞业限制范围、期限(不超 2 年)、补偿标准是否明确
|
|
9. 培训服务期约定及违约金上限(不超培训费用)
|
|
10. 解除劳动合同的条件与经济补偿(N / N+1 / 2N)是否合法
|
|
11. 违约金条款是否超出法定范围(仅培训、竞业可约定违约金)
|
|
12. 争议解决方式(仲裁/诉讼)与管辖地是否合理`,
|
|
},
|
|
{
|
|
id: 'tmpl-2',
|
|
name: '离职申请书',
|
|
description: '规范离职通知,保留证据',
|
|
icon: 'exit-outline',
|
|
url: 'https://dev.api.com/legal/templates/resignation-letter',
|
|
content: `离职申请书
|
|
|
|
致:____(公司名称)____ 人力资源部
|
|
|
|
本人 ____(姓名)____,身份证号 ____,于 ____(入职日期)____ 入职,现任 ____(部门 / 岗位)____。
|
|
|
|
现因 ____(离职原因,如:个人职业发展)____,根据《劳动合同法》第三十七条,本人提前三十日以书面形式通知公司,拟于 ____(最后工作日期)____ 正式解除劳动关系。
|
|
|
|
请公司按约定办理工作交接、出具离职证明,并结清工资、加班费及未休年假工资;社会保险与住房公积金请缴存至 ____(年 / 月)____。
|
|
|
|
此致
|
|
敬礼
|
|
|
|
申请人:____(签名)____
|
|
联系电话:____
|
|
日期:____ 年 ____ 月 ____ 日`,
|
|
},
|
|
{
|
|
id: 'tmpl-3',
|
|
name: '劳动仲裁申请书',
|
|
description: '劳动争议维权起步文书',
|
|
icon: 'gavel-outline',
|
|
url: 'https://dev.api.com/legal/templates/arbitration-application',
|
|
content: `劳动仲裁申请书
|
|
|
|
申请人:____(姓名)____,性别 ____,身份证号 ____,联系电话 ____,住址 ____
|
|
|
|
被申请人:____(公司全称)____,统一社会信用代码 ____,住所地 ____,法定代表人 ____
|
|
|
|
仲裁请求:
|
|
1. 请求裁决被申请人支付违法解除劳动合同赔偿金 ____ 元;
|
|
2. 请求裁决被申请人支付拖欠工资 ____ 元及加班费 ____ 元;
|
|
3. 请求裁决被申请人补缴 ____ 年 ____ 月至 ____ 年 ____ 月的社会保险与住房公积金。
|
|
|
|
事实与理由:
|
|
____(简述入职时间、岗位、工资标准、争议发生经过及证据,如劳动合同、工资条、考勤记录、聊天记录等)____
|
|
|
|
此致
|
|
____(用人单位所在地或合同履行地)____ 劳动人事争议仲裁委员会
|
|
|
|
申请人:____(签名)____
|
|
日期:____ 年 ____ 月 ____ 日
|
|
|
|
附:1. 申请人身份证复印件;2. 证据清单及材料 ____ 份`,
|
|
},
|
|
];
|
|
|
|
/** 会员套餐 */
|
|
const MOCK_LEGAL_PLANS: LegalPlan[] = [
|
|
{
|
|
id: 'plan-month',
|
|
name: '月度会员',
|
|
price: 29,
|
|
period: '/月',
|
|
benefits: ['3 次一对一律师咨询', '1 份文书审核', '常见问题不限次答疑'],
|
|
},
|
|
{
|
|
id: 'plan-year',
|
|
name: '年度会员',
|
|
price: 199,
|
|
period: '/年',
|
|
recommended: true,
|
|
benefits: ['不限次一对一律师咨询', '不限次文书审核', '专属法律顾问', '优先响应'],
|
|
},
|
|
];
|
|
|
|
/** 法律咨询默认欢迎会话 */
|
|
const MOCK_LEGAL_CONVERSATION: LegalConversation = {
|
|
id: 'default',
|
|
title: '阿布阿诺法律咨询',
|
|
messages: [
|
|
{
|
|
id: 'm1',
|
|
role: 'assistant',
|
|
content: '你好,我是阿布阿诺 AI 法律助手。我可以帮你解答求职过程中的劳动权益、劳动合同、试用期辞退等法律问题。请问你遇到什么问题了?',
|
|
timestamp: new Date().toISOString(),
|
|
},
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取阿布阿诺品牌信息
|
|
* @method GET
|
|
* @url /api/legal/brand
|
|
* @returns Promise<LegalBrand> 品牌信息
|
|
*/
|
|
export async function fetchLegalBrand(): Promise<LegalBrand> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/brand`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_LEGAL_BRAND);
|
|
}
|
|
|
|
/**
|
|
* 获取法律服务入口列表
|
|
* @method GET
|
|
* @url /api/legal/services
|
|
* @returns Promise<LegalService[]> 服务列表
|
|
*/
|
|
export async function fetchLegalServices(): Promise<LegalService[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/services`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_LEGAL_SERVICES);
|
|
}
|
|
|
|
/**
|
|
* 获取法律常见问题
|
|
* @method GET
|
|
* @url /api/legal/faqs
|
|
* @returns Promise<LegalFAQ[]> FAQ 列表
|
|
*/
|
|
export async function fetchLegalFAQs(): Promise<LegalFAQ[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/faqs`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_LEGAL_FAQS);
|
|
}
|
|
|
|
/**
|
|
* 获取法律文书模板
|
|
* @method GET
|
|
* @url /api/legal/templates
|
|
* @returns Promise<LegalTemplate[]> 模板列表
|
|
*/
|
|
export async function fetchLegalTemplates(): Promise<LegalTemplate[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/templates`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_LEGAL_TEMPLATES);
|
|
}
|
|
|
|
/**
|
|
* 获取会员套餐
|
|
* @method GET
|
|
* @url /api/legal/plans
|
|
* @returns Promise<LegalPlan[]> 套餐列表
|
|
*/
|
|
export async function fetchLegalPlans(): Promise<LegalPlan[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/plans`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_LEGAL_PLANS);
|
|
}
|
|
|
|
/* ===================== 付费 / 订单 / 会员态(mock 阶段持久化到 AsyncStorage) ===================== */
|
|
|
|
const LEGAL_MEMBERSHIP_KEY = 'legal:membership';
|
|
const LEGAL_ORDERS_KEY = 'legal:orders';
|
|
|
|
/** 读取会员态(storage 为唯一真相源,避免内存态过期) */
|
|
async function readLegalMembership(): Promise<LegalMembership | null> {
|
|
const raw = await storageGet(LEGAL_MEMBERSHIP_KEY);
|
|
return raw ? (JSON.parse(raw) as LegalMembership) : null;
|
|
}
|
|
|
|
/** 写入会员态(null 表示清除) */
|
|
async function writeLegalMembership(m: LegalMembership | null): Promise<void> {
|
|
if (m) {
|
|
await storageSet(LEGAL_MEMBERSHIP_KEY, JSON.stringify(m));
|
|
} else {
|
|
await storageRemove(LEGAL_MEMBERSHIP_KEY);
|
|
}
|
|
}
|
|
|
|
/** 读取订单列表 */
|
|
async function readLegalOrders(): Promise<LegalOrder[]> {
|
|
const raw = await storageGet(LEGAL_ORDERS_KEY);
|
|
return raw ? (JSON.parse(raw) as LegalOrder[]) : [];
|
|
}
|
|
|
|
/** 写入订单列表 */
|
|
async function writeLegalOrders(orders: LegalOrder[]): Promise<void> {
|
|
await storageSet(LEGAL_ORDERS_KEY, JSON.stringify(orders));
|
|
}
|
|
|
|
/**
|
|
* 获取当前会员态
|
|
* @method GET
|
|
* @url /api/legal/membership
|
|
* @returns Promise<LegalMembership | null> 未开通则返回 null
|
|
*/
|
|
export async function fetchLegalMembership(): Promise<LegalMembership | null> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/membership`, { method: 'GET' }); return res.json();
|
|
return mockRequest(await readLegalMembership());
|
|
}
|
|
|
|
/**
|
|
* 获取付费订单列表(按创建时间倒序)
|
|
* @method GET
|
|
* @url /api/legal/orders
|
|
* @returns Promise<LegalOrder[]> 订单列表
|
|
*/
|
|
export async function fetchLegalOrders(): Promise<LegalOrder[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/orders`, { method: 'GET' }); return res.json();
|
|
return mockRequest(await readLegalOrders());
|
|
}
|
|
|
|
/**
|
|
* 创建会员订单(mock 阶段仅本地落库,不接真实支付)
|
|
* @method POST
|
|
* @url /api/legal/orders
|
|
* @param planId 套餐 id
|
|
* @param channel 支付方式
|
|
* @returns Promise<LegalOrder> 新建的待支付订单
|
|
*/
|
|
export async function createLegalOrder(
|
|
planId: string,
|
|
channel: LegalPaymentChannel,
|
|
): Promise<LegalOrder> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/orders`, { method: 'POST', body: JSON.stringify({ planId, channel }) }); return res.json();
|
|
const plans = await fetchLegalPlans();
|
|
const plan = plans.find((p) => p.id === planId);
|
|
if (!plan) {
|
|
throw new Error('套餐不存在');
|
|
}
|
|
const order: LegalOrder = {
|
|
id: `order-${Date.now()}`,
|
|
planId: plan.id,
|
|
planName: plan.name,
|
|
price: plan.price,
|
|
channel,
|
|
status: 'pending',
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
const orders = await readLegalOrders();
|
|
orders.unshift(order);
|
|
await writeLegalOrders(orders);
|
|
return mockRequest(order);
|
|
}
|
|
|
|
/**
|
|
* 支付订单(mock 阶段模拟支付处理并激活会员态)
|
|
* @method POST
|
|
* @url /api/legal/orders/:id/pay
|
|
* @param orderId 订单 id
|
|
* @returns Promise<LegalOrder> 支付后的订单(status=paid)
|
|
* @remark 真实接入时,应由后端支付回调确认支付结果后再激活会员态,前端不自行判定成功
|
|
*/
|
|
export async function payLegalOrder(orderId: string): Promise<LegalOrder> {
|
|
// TODO: 调用真实支付 SDK → 后端回调确认 → 返回订单
|
|
const orders = await readLegalOrders();
|
|
const idx = orders.findIndex((o) => o.id === orderId);
|
|
if (idx === -1) {
|
|
throw new Error('订单不存在');
|
|
}
|
|
const paid: LegalOrder = {
|
|
...orders[idx],
|
|
status: 'paid',
|
|
paidAt: new Date().toISOString(),
|
|
};
|
|
orders[idx] = paid;
|
|
await writeLegalOrders(orders);
|
|
|
|
// 根据套餐周期计算会员有效期并激活会员态
|
|
const plans = await fetchLegalPlans();
|
|
const plan = plans.find((p) => p.id === paid.planId);
|
|
if (plan) {
|
|
const now = new Date();
|
|
const expires = new Date(now.getTime());
|
|
// period: '/年' 或 '/月'
|
|
if (plan.period.includes('年')) {
|
|
expires.setFullYear(now.getFullYear() + 1);
|
|
} else {
|
|
expires.setMonth(now.getMonth() + 1);
|
|
}
|
|
await writeLegalMembership({
|
|
planId: plan.id,
|
|
planName: plan.name,
|
|
startedAt: now.toISOString(),
|
|
expiresAt: expires.toISOString(),
|
|
});
|
|
}
|
|
|
|
// 模拟支付处理延时
|
|
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
return mockRequest(paid);
|
|
}
|
|
|
|
/* ===================== 会员服务使用申请(付费后权益兑现) ===================== */
|
|
|
|
const LEGAL_REQUESTS_KEY = 'legal:requests';
|
|
|
|
/** 读取服务申请列表 */
|
|
async function readLegalRequests(): Promise<LegalServiceRequest[]> {
|
|
const raw = await storageGet(LEGAL_REQUESTS_KEY);
|
|
return raw ? (JSON.parse(raw) as LegalServiceRequest[]) : [];
|
|
}
|
|
|
|
/** 写入服务申请列表 */
|
|
async function writeLegalRequests(requests: LegalServiceRequest[]): Promise<void> {
|
|
await storageSet(LEGAL_REQUESTS_KEY, JSON.stringify(requests));
|
|
}
|
|
|
|
/**
|
|
* 获取会员服务使用申请列表(按提交时间倒序)
|
|
* @method GET
|
|
* @url /api/legal/service-requests
|
|
* @returns Promise<LegalServiceRequest[]> 申请列表
|
|
*/
|
|
export async function fetchLegalServiceRequests(): Promise<LegalServiceRequest[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/service-requests`, { method: 'GET' }); return res.json();
|
|
return mockRequest(await readLegalRequests());
|
|
}
|
|
|
|
/**
|
|
* 提交会员服务使用申请(仅已开通会员可提交,权限由后端校验)
|
|
* @method POST
|
|
* @url /api/legal/service-requests
|
|
* @param serviceId 服务 id
|
|
* @param content 用户填写的需求描述
|
|
* @returns Promise<LegalServiceRequest> 新建的申请
|
|
*/
|
|
export async function createLegalServiceRequest(
|
|
serviceId: string,
|
|
content: string,
|
|
): Promise<LegalServiceRequest> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/service-requests`, { method: 'POST', body: JSON.stringify({ serviceId, content }) }); return res.json();
|
|
const services = await fetchLegalServices();
|
|
const service = services.find((s) => s.id === serviceId);
|
|
if (!service) {
|
|
throw new Error('服务不存在');
|
|
}
|
|
const request: LegalServiceRequest = {
|
|
id: `request-${Date.now()}`,
|
|
serviceId: service.id,
|
|
serviceName: service.title,
|
|
content,
|
|
status: 'submitted',
|
|
createdAt: new Date().toISOString(),
|
|
};
|
|
const requests = await readLegalRequests();
|
|
requests.unshift(request);
|
|
await writeLegalRequests(requests);
|
|
return mockRequest(request);
|
|
}
|
|
|
|
/**
|
|
* 获取默认法律咨询服务会话
|
|
* @method GET
|
|
* @url /api/legal/conversations/default
|
|
* @returns Promise<LegalConversation> 咨询会话
|
|
*/
|
|
export async function fetchLegalConversation(): Promise<LegalConversation> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/conversations/default`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_LEGAL_CONVERSATION);
|
|
}
|
|
|
|
/**
|
|
* 发送法律咨询消息(mock 阶段模拟 AI 回复)
|
|
* @method POST
|
|
* @url /api/legal/conversations/default/messages
|
|
* @param content {string} 用户输入
|
|
* @returns Promise<LegalMessage[]> 最新两条消息(用户 + 助手)
|
|
*/
|
|
export async function sendLegalMessage(content: string): Promise<LegalMessage[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/legal/conversations/default/messages`, { method: 'POST', body: JSON.stringify({ content }) }); return res.json();
|
|
const now = new Date().toISOString();
|
|
const userMessage: LegalMessage = {
|
|
id: `u-${Date.now()}`,
|
|
role: 'user',
|
|
content,
|
|
timestamp: now,
|
|
};
|
|
const assistantMessage: LegalMessage = {
|
|
id: `a-${Date.now()}`,
|
|
role: 'assistant',
|
|
content:
|
|
'收到你的问题。作为阿布阿诺 AI 法律助手,我建议你先保留相关证据(聊天记录、合同、Offer、工资流水等)。如情况复杂,可升级到一对一律师咨询获取专业方案。',
|
|
timestamp: now,
|
|
};
|
|
return mockRequest([userMessage, assistantMessage]);
|
|
}
|
|
|
|
/* ===================== 设置:隐私设置 ===================== */
|
|
|
|
/** 隐私开关(内存态,mock 阶段刷新后重置) */
|
|
const MOCK_PRIVACY_SETTINGS: ToggleSetting[] = [
|
|
{
|
|
id: 'resume-visible',
|
|
title: '简历对 HR 可见',
|
|
description: '关闭后企业将无法搜索到你的简历',
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 'anonymous-browse',
|
|
title: '匿名浏览企业',
|
|
description: '浏览公司主页时不留下访问记录',
|
|
enabled: false,
|
|
},
|
|
{
|
|
id: 'show-job-status',
|
|
title: '展示求职状态',
|
|
description: '在个人主页公开「在职-考虑机会」等状态',
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 'show-salary',
|
|
title: '展示薪资期望',
|
|
description: '允许企业在推荐列表中看到你的期望薪资',
|
|
enabled: false,
|
|
},
|
|
{
|
|
id: 'allow-bgcheck',
|
|
title: '允许背景调查',
|
|
description: '入职前授权企业进行雇佣背景核实',
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 'personalized',
|
|
title: '个性化岗位推荐',
|
|
description: '基于你的浏览与投递行为推荐匹配岗位',
|
|
enabled: true,
|
|
},
|
|
];
|
|
|
|
/**
|
|
* 获取隐私设置开关列表
|
|
* @method GET
|
|
* @url /api/settings/privacy
|
|
* @returns Promise<ToggleSetting[]> 隐私开关列表
|
|
*/
|
|
export async function fetchPrivacySettings(): Promise<ToggleSetting[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/settings/privacy`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_PRIVACY_SETTINGS);
|
|
}
|
|
|
|
/**
|
|
* 更新单个隐私开关
|
|
* @method PATCH
|
|
* @url /api/settings/privacy/:id
|
|
* @param payload {UpdateTogglePayload} 开关 id 与目标状态
|
|
* @returns Promise<ToggleSetting[]> 更新后的开关列表
|
|
*/
|
|
export async function updatePrivacySetting(
|
|
payload: UpdateTogglePayload,
|
|
): Promise<ToggleSetting[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/settings/privacy/${payload.id}`, { method: 'PATCH', body: JSON.stringify(payload) }); return res.json();
|
|
const target = MOCK_PRIVACY_SETTINGS.find((item) => item.id === payload.id);
|
|
if (target) {
|
|
target.enabled = payload.enabled;
|
|
}
|
|
return mockRequest(MOCK_PRIVACY_SETTINGS);
|
|
}
|
|
|
|
/* ===================== 隐私中心(截图版) ===================== */
|
|
|
|
const MOCK_PRIVACY_CENTER: PrivacyCenter = {
|
|
privacyLevel: {
|
|
level: 'high',
|
|
label: '较高',
|
|
description: '你的数据保护状态良好,但仍有可调整空间。进一步提升隐私',
|
|
},
|
|
sections: [
|
|
{
|
|
id: 'account',
|
|
title: '账号隐私',
|
|
items: [
|
|
{
|
|
id: 'real-name-visible',
|
|
title: '真实姓名可见性',
|
|
type: 'switch',
|
|
enabled: true,
|
|
route: '/settings/privacy/real-name',
|
|
},
|
|
{
|
|
id: 'contact-visible',
|
|
title: '联系方式可见性',
|
|
type: 'switch',
|
|
enabled: true,
|
|
route: '/settings/privacy/contact',
|
|
},
|
|
{ id: 'avatar-public', title: '头像公开', type: 'switch', enabled: true },
|
|
{ id: 'searchable', title: '允许被搜索', type: 'switch', enabled: true },
|
|
{ id: 'resume-view-notify', title: '简历被浏览通知', type: 'switch', enabled: true },
|
|
],
|
|
},
|
|
{
|
|
id: 'authorization',
|
|
title: '数据使用授权',
|
|
items: [
|
|
{ id: 'personalized-recommend', title: '个性化推荐', type: 'switch', enabled: true },
|
|
{
|
|
id: 'algo-training',
|
|
title: '数据用于算法训练',
|
|
description: '匿名数据用于提升匹配效果',
|
|
type: 'switch',
|
|
enabled: false,
|
|
route: '/settings/privacy/algo',
|
|
},
|
|
{
|
|
id: 'third-party-analysis',
|
|
title: '允许第三方数据分析',
|
|
type: 'switch',
|
|
enabled: false,
|
|
route: '/settings/privacy/third-party',
|
|
},
|
|
{
|
|
id: 'location-auth',
|
|
title: '位置信息授权',
|
|
description: '仅用于岗位推荐',
|
|
type: 'switch',
|
|
enabled: true,
|
|
route: '/settings/privacy/location',
|
|
},
|
|
{
|
|
id: 'contacts-auth',
|
|
title: '通讯录权限',
|
|
type: 'switch',
|
|
enabled: false,
|
|
route: '/settings/privacy/contacts',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'anti-harassment',
|
|
title: '求职防骚扰',
|
|
items: [
|
|
{
|
|
id: 'block-unmatched',
|
|
title: '屏蔽未匹配企业联系',
|
|
type: 'switch',
|
|
enabled: true,
|
|
},
|
|
{ id: 'block-headhunter', title: '屏蔽猎头/中介', type: 'switch', enabled: false },
|
|
{
|
|
id: 'dnd-mode',
|
|
title: '勿扰模式',
|
|
description: '已屏蔽 3 个关键词',
|
|
type: 'switch',
|
|
enabled: true,
|
|
route: '/settings/privacy/dnd',
|
|
},
|
|
{
|
|
id: 'keyword-block',
|
|
title: '关键词屏蔽',
|
|
description: '已屏蔽 3 个关键词',
|
|
type: 'value',
|
|
value: '3个',
|
|
route: '/settings/privacy/keywords',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'data-management',
|
|
title: '数据管理',
|
|
items: [
|
|
{
|
|
id: 'personal-data-export',
|
|
title: '个人数据导出',
|
|
type: 'link',
|
|
route: '/personal-data-export',
|
|
},
|
|
{ id: 'account-cancel', title: '账号注销', type: 'link', route: '/settings/account-cancel' },
|
|
{
|
|
id: 'delete-all-data',
|
|
title: '删除全部数据',
|
|
type: 'danger-link',
|
|
route: '/settings/delete-data',
|
|
},
|
|
{ id: 'privacy-policy', title: '隐私政策', type: 'link', route: '/settings/privacy-policy' },
|
|
{
|
|
id: 'user-agreement',
|
|
title: '用户协议',
|
|
type: 'link',
|
|
route: '/settings/user-agreement',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
explanation:
|
|
'我们承诺严格保护你的个人隐私,所有数据使用均符合《个人信息保护法》。你可以随时调整隐私设置,我们不会向第三方出售或泄露你的个人信息。',
|
|
};
|
|
|
|
/**
|
|
* 获取隐私中心聚合数据
|
|
* @method GET
|
|
* @url /api/settings/privacy-center
|
|
* @returns Promise<PrivacyCenter> 隐私中心数据
|
|
*/
|
|
export async function fetchPrivacyCenter(): Promise<PrivacyCenter> {
|
|
return mockRequest(MOCK_PRIVACY_CENTER);
|
|
}
|
|
|
|
/**
|
|
* 更新隐私中心开关项
|
|
* @method PATCH
|
|
* @url /api/settings/privacy-center/:id
|
|
* @param payload {UpdateTogglePayload} 开关 id 与目标状态
|
|
* @returns Promise<PrivacyCenter> 更新后的隐私中心数据
|
|
*/
|
|
export async function updatePrivacyCenterSetting(
|
|
payload: UpdateTogglePayload,
|
|
): Promise<PrivacyCenter> {
|
|
for (const section of MOCK_PRIVACY_CENTER.sections) {
|
|
const target = section.items.find((item) => item.id === payload.id);
|
|
if (target && target.type === 'switch') {
|
|
target.enabled = payload.enabled;
|
|
}
|
|
}
|
|
return mockRequest(MOCK_PRIVACY_CENTER);
|
|
}
|
|
|
|
/* ===================== 隐私二级设置页 ===================== */
|
|
|
|
/**
|
|
* 6 个开关类二级页的数据,键为页面 id(对应路由 /settings/privacy/[id])
|
|
* 【待确认】各页细选项的文案与默认开关状态,需产品与法务复核
|
|
*/
|
|
const MOCK_PRIVACY_DETAILS: Record<string, PrivacyDetailSetting> = {
|
|
'real-name': {
|
|
id: 'real-name',
|
|
title: '真实姓名可见性',
|
|
intro: '控制企业查看你的简历时能否看到真实姓名。关闭后将展示为「张先生」「李女士」等脱敏形式。',
|
|
master: { id: 'real-name-master', title: '向企业展示真实姓名', enabled: true },
|
|
options: [
|
|
{ id: 'rn-delivered', title: '已投递的企业可见', enabled: true },
|
|
{ id: 'rn-verified', title: '已完成企业认证的公司可见', enabled: true },
|
|
{ id: 'rn-masked', title: '未投递企业展示脱敏姓名', description: '如「张先生」', enabled: true },
|
|
],
|
|
note: '无论是否展示真实姓名,企业在发出 offer 前均需经过你的明示同意才能获取完整身份信息。',
|
|
},
|
|
contact: {
|
|
id: 'contact',
|
|
title: '联系方式可见性',
|
|
intro: '控制手机号、邮箱等联系方式的可见范围。建议进入面试环节后再向企业开放。',
|
|
master: { id: 'contact-master', title: '向企业展示联系方式', enabled: true },
|
|
options: [
|
|
{ id: 'ct-phone', title: '手机号', enabled: true },
|
|
{ id: 'ct-email', title: '邮箱', enabled: true },
|
|
{ id: 'ct-wechat', title: '微信号', enabled: false },
|
|
{ id: 'ct-after-interview', title: '仅进入面试后自动开放', enabled: true },
|
|
],
|
|
note: '开启「仅进入面试后自动开放」后,企业需将你推进至面试环节才能查看完整联系方式。',
|
|
},
|
|
algo: {
|
|
id: 'algo',
|
|
title: '数据用于算法训练',
|
|
intro: '开启后,你的匿名化行为数据将用于优化岗位匹配与推荐算法。训练数据经过去标识化处理,不含可识别身份的信息。',
|
|
master: { id: 'algo-master', title: '数据用于算法训练', description: '匿名数据用于提升匹配效果', enabled: false },
|
|
options: [
|
|
{ id: 'ag-browse', title: '使用浏览行为', enabled: false },
|
|
{ id: 'ag-delivery', title: '使用投递记录', enabled: false },
|
|
{ id: 'ag-chat', title: '使用沟通记录', description: '不含聊天正文,仅使用结构化结果', enabled: false },
|
|
],
|
|
note: '关闭后你仍可正常使用全部功能,但推荐精准度可能下降。你可随时重新开启。',
|
|
},
|
|
'third-party': {
|
|
id: 'third-party',
|
|
title: '允许第三方数据分析',
|
|
intro: '第三方数据分析用于生成行业薪酬报告与人才流动趋势。分析结果均为聚合统计,不含个人身份信息,且不会用于向你推送广告。',
|
|
master: { id: 'tp-master', title: '允许第三方数据分析', enabled: false },
|
|
options: [
|
|
{ id: 'tp-salary', title: '薪酬水平分析', enabled: false },
|
|
{ id: 'tp-flow', title: '人才流动趋势', enabled: false },
|
|
{ id: 'tp-skill', title: '技能需求统计', enabled: false },
|
|
],
|
|
note: '我们仅向具备数据安全资质的合作方提供聚合结果,并签署数据处理协议。',
|
|
},
|
|
location: {
|
|
id: 'location',
|
|
title: '位置信息授权',
|
|
intro: '位置信息仅用于为你推荐附近岗位与计算通勤时间。关闭后,推荐结果将按你填写的期望城市计算。',
|
|
master: { id: 'loc-master', title: '位置信息授权', description: '仅用于岗位推荐', enabled: true },
|
|
options: [
|
|
{ id: 'loc-precise', title: '使用精确定位', enabled: true },
|
|
{ id: 'loc-background', title: '允许后台获取位置', description: '关闭后仅在使用应用时获取', enabled: false },
|
|
{ id: 'loc-commute', title: '用于通勤时间计算', enabled: true },
|
|
],
|
|
note: '我们不会持续上传你的位置轨迹,位置信息仅在本地完成距离计算后即被丢弃。',
|
|
},
|
|
contacts: {
|
|
id: 'contacts',
|
|
title: '通讯录权限',
|
|
intro: '授权通讯录后,你可以快速发现已在平台的好友,也能邀请同事加入。我们仅在本地比对通讯录,不会上传或存储你的联系人信息。',
|
|
master: { id: 'cnt-master', title: '通讯录权限', enabled: false },
|
|
options: [
|
|
{ id: 'cnt-discover', title: '发现通讯录好友', enabled: false },
|
|
{ id: 'cnt-invite', title: '向好友推荐岗位', enabled: false },
|
|
{ id: 'cnt-network', title: '同步人脉关系', description: '用于展示可能认识的同行', enabled: false },
|
|
],
|
|
note: '通讯录比对通过本地哈希完成,服务端无法还原你的联系人姓名与号码。',
|
|
},
|
|
};
|
|
|
|
/**
|
|
* 获取隐私二级设置页数据(开关类)
|
|
* @method GET
|
|
* @url /api/settings/privacy/:id
|
|
* @param id { string } 页面 id,如 real-name / contact / algo
|
|
* @returns Promise<PrivacyDetailSetting | null> 找不到时返回 null
|
|
*/
|
|
export async function fetchPrivacyDetailSetting(
|
|
id: string,
|
|
): Promise<PrivacyDetailSetting | null> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/settings/privacy/${id}`, { method: 'GET' }); return res.json();
|
|
const detail = MOCK_PRIVACY_DETAILS[id];
|
|
return mockRequest(detail ? { ...detail } : null);
|
|
}
|
|
|
|
/**
|
|
* 更新隐私二级设置页的开关(主开关或细选项)
|
|
* @method PATCH
|
|
* @url /api/settings/privacy/:id
|
|
* @param id { string } 页面 id
|
|
* @param optionId { string } 主开关或细选项 id
|
|
* @param enabled { boolean } 目标状态
|
|
* @returns Promise<PrivacyDetailSetting | null>
|
|
*/
|
|
export async function updatePrivacyDetailSetting(
|
|
id: string,
|
|
optionId: string,
|
|
enabled: boolean,
|
|
): Promise<PrivacyDetailSetting | null> {
|
|
// TODO: await fetch(`${API_BASE_URL}/api/settings/privacy/${id}`, {
|
|
// method: 'PATCH', headers: { 'Content-Type': 'application/json' },
|
|
// body: JSON.stringify({ optionId, enabled }),
|
|
// });
|
|
const detail = MOCK_PRIVACY_DETAILS[id];
|
|
if (!detail) return mockRequest(null);
|
|
if (detail.master.id === optionId) {
|
|
detail.master.enabled = enabled;
|
|
} else {
|
|
const option = detail.options.find((item) => item.id === optionId);
|
|
if (option) option.enabled = enabled;
|
|
}
|
|
return mockRequest({ ...detail });
|
|
}
|
|
|
|
/* ===================== 勿扰模式 ===================== */
|
|
|
|
const MOCK_DND_SETTING: DndSetting = {
|
|
enabled: true,
|
|
startTime: '22:00',
|
|
endTime: '08:00',
|
|
allowUrgent: true,
|
|
blockedCount: 128,
|
|
};
|
|
|
|
/**
|
|
* 获取勿扰模式设置
|
|
* @method GET
|
|
* @url /api/settings/privacy/dnd
|
|
* @returns Promise<DndSetting>
|
|
*/
|
|
export async function fetchDndSetting(): Promise<DndSetting> {
|
|
return mockRequest({ ...MOCK_DND_SETTING });
|
|
}
|
|
|
|
/**
|
|
* 更新勿扰模式设置
|
|
* @method PATCH
|
|
* @url /api/settings/privacy/dnd
|
|
* @param payload { Partial<DndSetting> } 需更新的字段
|
|
* @returns Promise<DndSetting>
|
|
*/
|
|
export async function updateDndSetting(payload: Partial<DndSetting>): Promise<DndSetting> {
|
|
Object.assign(MOCK_DND_SETTING, payload);
|
|
return mockRequest({ ...MOCK_DND_SETTING });
|
|
}
|
|
|
|
/* ===================== 关键词屏蔽 ===================== */
|
|
|
|
const MOCK_KEYWORD_BLOCK: KeywordBlockSetting = {
|
|
keywords: ['外包', '兼职', '刷单'],
|
|
blockedCount: 3,
|
|
suggestions: ['无责底薪', '日结', '在家兼职', '急招临时工'],
|
|
};
|
|
|
|
/**
|
|
* 获取关键词屏蔽设置
|
|
* @method GET
|
|
* @url /api/settings/privacy/keywords
|
|
* @returns Promise<KeywordBlockSetting>
|
|
*/
|
|
export async function fetchKeywordBlock(): Promise<KeywordBlockSetting> {
|
|
return mockRequest({ ...MOCK_KEYWORD_BLOCK, keywords: [...MOCK_KEYWORD_BLOCK.keywords] });
|
|
}
|
|
|
|
/**
|
|
* 添加屏蔽关键词
|
|
* @method POST
|
|
* @url /api/settings/privacy/keywords
|
|
* @param keyword { string } 待屏蔽的关键词
|
|
* @returns Promise<KeywordBlockSetting>
|
|
*/
|
|
export async function addBlockKeyword(keyword: string): Promise<KeywordBlockSetting> {
|
|
const value = keyword.trim();
|
|
if (value && !MOCK_KEYWORD_BLOCK.keywords.includes(value)) {
|
|
MOCK_KEYWORD_BLOCK.keywords.push(value);
|
|
}
|
|
return mockRequest({ ...MOCK_KEYWORD_BLOCK, keywords: [...MOCK_KEYWORD_BLOCK.keywords] });
|
|
}
|
|
|
|
/**
|
|
* 移除屏蔽关键词
|
|
* @method DELETE
|
|
* @url /api/settings/privacy/keywords/:keyword
|
|
* @param keyword { string } 待移除的关键词
|
|
* @returns Promise<KeywordBlockSetting>
|
|
*/
|
|
export async function removeBlockKeyword(keyword: string): Promise<KeywordBlockSetting> {
|
|
MOCK_KEYWORD_BLOCK.keywords = MOCK_KEYWORD_BLOCK.keywords.filter((item) => item !== keyword);
|
|
return mockRequest({ ...MOCK_KEYWORD_BLOCK, keywords: [...MOCK_KEYWORD_BLOCK.keywords] });
|
|
}
|
|
|
|
/* ===================== 账号注销 ===================== */
|
|
|
|
const MOCK_ACCOUNT_CANCEL: AccountCancelData = {
|
|
title: '账号注销',
|
|
consequences: [
|
|
'账号将无法登录且不可恢复,平台内所有数据将被清空且永久删除',
|
|
'正在进行的投递、面试邀约与沟通记录将全部终止',
|
|
'已获得的会员权益、优惠券、成长值与勋章将失效且不予退还',
|
|
'若你已完成企业认证,认证信息与关联招聘流程将一并注销',
|
|
],
|
|
checklist: [
|
|
{ id: 'balance', label: '账户余额已清零', resolved: false, actionText: '去提现' },
|
|
{ id: 'orders', label: '无进行中的付费订单', resolved: true },
|
|
{ id: 'delivery', label: '无进行中的投递与面试', resolved: true },
|
|
{ id: 'enterprise', label: '企业认证已解除', resolved: true },
|
|
],
|
|
reasons: ['已找到工作', '不再需要该服务', '隐私担忧', '使用体验不佳', '其他'],
|
|
canCancel: false,
|
|
};
|
|
|
|
/**
|
|
* 获取账号注销前置信息
|
|
* @method GET
|
|
* @url /api/settings/account-cancel
|
|
* @returns Promise<AccountCancelData>
|
|
*/
|
|
export async function fetchAccountCancel(): Promise<AccountCancelData> {
|
|
return mockRequest({
|
|
...MOCK_ACCOUNT_CANCEL,
|
|
canCancel: MOCK_ACCOUNT_CANCEL.checklist.every((item) => item.resolved),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 提交账号注销申请
|
|
* @method POST
|
|
* @url /api/settings/account-cancel
|
|
* @param reason { string } 注销原因
|
|
* @returns Promise<{ success: boolean; message: string }>
|
|
*/
|
|
export async function submitAccountCancel(
|
|
_reason: string,
|
|
): Promise<{ success: boolean; message: string }> {
|
|
// TODO: await fetch(`${API_BASE_URL}/api/settings/account-cancel`, {
|
|
// method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
// body: JSON.stringify({ reason }),
|
|
// });
|
|
return mockRequest({
|
|
success: true,
|
|
message: '注销申请已提交,我们将在 7 个工作日内完成审核并以短信通知你',
|
|
});
|
|
}
|
|
|
|
/* ===================== 删除全部数据 ===================== */
|
|
|
|
const MOCK_DELETE_ALL_DATA: DeleteAllData = {
|
|
intro: '你可以选择删除部分或全部个人数据。删除操作不可撤销,请在确认前仔细阅读各项说明。',
|
|
categories: [
|
|
{ id: 'resume', label: '简历数据', description: '简历正文、附件与历史版本', count: '12 条' },
|
|
{ id: 'delivery', label: '投递记录', description: '历史投递与进展状态', count: '128 条' },
|
|
{ id: 'chat', label: '沟通记录', description: '与企业 HR 的聊天消息', count: '1,024 条' },
|
|
{ id: 'assessment', label: '测评结果', description: '能力测评与职业兴趣测评', count: '8 条' },
|
|
{ id: 'behavior', label: '行为数据', description: '浏览、搜索与点击记录', count: '约 5,000 条' },
|
|
],
|
|
warning: '删除后数据将无法恢复,并可能影响岗位推荐准确度与竞争力排名的计算结果。',
|
|
confirmPlaceholder: '请输入「删除全部数据」以确认',
|
|
};
|
|
|
|
/**
|
|
* 获取可删除的数据分类
|
|
* @method GET
|
|
* @url /api/settings/delete-data
|
|
* @returns Promise<DeleteAllData>
|
|
*/
|
|
export async function fetchDeleteAllData(): Promise<DeleteAllData> {
|
|
return mockRequest({ ...MOCK_DELETE_ALL_DATA });
|
|
}
|
|
|
|
/**
|
|
* 提交删除数据请求
|
|
* @method POST
|
|
* @url /api/settings/delete-data
|
|
* @param categoryIds { string[] } 待删除的分类 id 列表
|
|
* @returns Promise<{ success: boolean; message: string }>
|
|
*/
|
|
export async function submitDeleteData(
|
|
categoryIds: string[],
|
|
): Promise<{ success: boolean; message: string }> {
|
|
// TODO: await fetch(`${API_BASE_URL}/api/settings/delete-data`, {
|
|
// method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
// body: JSON.stringify({ categoryIds }),
|
|
// });
|
|
return mockRequest({
|
|
success: true,
|
|
message: `已提交删除申请,涉及 ${categoryIds.length} 项数据,预计 3 个工作日内处理完成`,
|
|
});
|
|
}
|
|
|
|
/* ===================== 法律文档(隐私政策 / 用户协议) ===================== */
|
|
|
|
/**
|
|
* 隐私政策正文
|
|
* 【待确认】文中公司全称、联系邮箱、客服电话、数据存储地等需法务确认后替换占位内容
|
|
*/
|
|
const MOCK_PRIVACY_POLICY: LegalDocument = {
|
|
id: 'privacy-policy',
|
|
title: '隐私政策',
|
|
version: 'V1.0',
|
|
updatedAt: '2026-08-31',
|
|
effectiveAt: '2026-08-31',
|
|
preamble:
|
|
'有职(以下简称「我们」)深知个人信息对你的重要性,并会尽全力保护你的个人信息安全可靠。我们致力于维持你对我们的信任,恪守以下原则保护你的个人信息:权责一致原则、目的明确原则、选择同意原则、最少够用原则、确保安全原则、主体参与原则、公开透明原则。本政策适用于你通过有职平台(含移动客户端、网站及关联服务)所进行的全部活动。',
|
|
sections: [
|
|
{
|
|
heading: '一、我们收集的信息',
|
|
paragraphs: [
|
|
'在您注册账号或使用平台服务时,我们会收集您主动提供的信息,包括手机号码、验证码、密码等注册信息;以及您后续完善简历时填写的姓名、性别、出生日期、教育经历、工作经历、项目经历、技能标签、期望岗位与期望薪资等。',
|
|
'在您使用服务过程中,我们会收集设备信息(设备型号、操作系统版本、唯一设备标识符)、日志信息(浏览记录、搜索关键词、点击记录、IP 地址、访问时间)以及位置信息(仅在您授权后收集,用于推荐附近岗位)。',
|
|
'若您使用企业端服务,我们还将收集企业工商信息、营业执照、招聘联系人身份信息及认证材料,用于完成企业资质审核。',
|
|
],
|
|
},
|
|
{
|
|
heading: '二、我们如何使用信息',
|
|
paragraphs: [
|
|
'我们收集的信息将用于:为您创建并维护账号、展示与匹配岗位、生成竞争力排名与测评报告、处理您的投递与沟通请求、向您发送面试邀约与重要通知。',
|
|
'为提升推荐的准确性,我们会在您授权的前提下使用匿名化、去标识化后的行为数据优化算法模型。您可在「设置 - 隐私中心」中随时关闭该授权,关闭后您仍可正常使用全部功能。',
|
|
'我们不会将您的个人信息用于本政策未载明的其他目的。如确需变更使用目的,我们将重新征得您的明示同意。',
|
|
],
|
|
},
|
|
{
|
|
heading: '三、信息的共享、转让与公开披露',
|
|
paragraphs: [
|
|
'我们不会向任何第三方出售您的个人信息。除下列情形外,我们不会与第三方共享您的信息:(1)获得您的明示同意;(2)您主动向企业投递简历后,向该企业提供您授权范围内的简历内容;(3)为实现「岗位推荐」等功能而向合作的服务供应商提供必要信息,且该等供应商受保密义务约束;(4)根据法律法规或主管部门的强制性要求。',
|
|
'我们仅在聚合、匿名化后向第三方提供行业薪酬报告与人才流动趋势等统计分析结果,该等结果无法识别到特定个人。',
|
|
'如因合并、分立、重组等导致信息转移,我们将告知您接收方的名称与联系方式,并要求其继续受本政策约束;若接收方变更原有处理目的,将重新征求您的同意。',
|
|
],
|
|
},
|
|
{
|
|
heading: '四、信息的存储与保护',
|
|
paragraphs: [
|
|
'我们在中华人民共和国境内收集和产生的个人信息将存储于境内。我们仅在本政策所述目的所必需的期间内保留您的个人信息,超出保留期限后将进行删除或匿名化处理。',
|
|
'我们已采取符合业界标准的安全防护措施保护您的个人信息,包括传输层加密、存储加密、访问权限控制、安全审计与应急预案。但请您理解,任何安全措施都无法做到绝对无懈可击,我们将持续努力提升防护水平。',
|
|
'若不幸发生个人信息泄露等安全事件,我们将按照法律法规要求,及时以站内信、短信或邮件等方式告知您事件的基本情况、影响范围、已采取的措施及您的应对建议。',
|
|
],
|
|
},
|
|
{
|
|
heading: '五、您的权利',
|
|
paragraphs: [
|
|
'您对自己的个人信息享有查询、更正、补充、删除、复制、撤回同意以及注销账号的权利。您可直接在「设置 - 隐私中心」或「设置 - 个人数据导出」中行使上述权利,我们将在 15 个工作日内予以响应。',
|
|
'您可随时在隐私中心调整真实姓名可见性、联系方式可见性、个性化推荐、算法训练授权、第三方数据分析、位置与通讯录授权等开关。关闭相应授权后,我们将停止对应的信息处理活动。',
|
|
'如您认为我们的处理行为损害了您的合法权益,您可通过本政策载明的联系方式向我们投诉,我们会在收到投诉后 15 个工作日内作出答复;您也可向有关主管部门举报或提起诉讼。',
|
|
],
|
|
},
|
|
{
|
|
heading: '六、未成年人保护',
|
|
paragraphs: [
|
|
'本平台主要面向具有劳动能力的成年人提供求职招聘服务。若您未满 18 周岁,请在监护人的陪同下阅读本政策,并在取得监护人明示同意后再使用本平台服务。',
|
|
'我们不会主动收集未满 18 周岁用户的个人信息。若我们是未事先获得监护人同意而收集了未成年人的个人信息,我们会尽快删除相关数据。',
|
|
'如您对您所监护的未成年人的个人信息有疑问,请通过本政策载明的联系方式与我们联系。',
|
|
],
|
|
},
|
|
{
|
|
heading: '七、Cookie 与同类技术',
|
|
paragraphs: [
|
|
'为保障产品正常运转、提升使用体验,我们会在您的设备上存储名为 Cookie 的小型数据文件。您可根据自己的偏好管理或删除 Cookie。',
|
|
'我们使用 Cookie 主要用于:记住您的登录状态、保存您的筛选偏好、分析页面访问情况以优化产品。关闭 Cookie 后,部分功能可能无法正常使用。',
|
|
],
|
|
},
|
|
{
|
|
heading: '八、本政策的更新',
|
|
paragraphs: [
|
|
'我们的隐私政策可能适时修订。未经您明示同意,我们不会削减您依据本政策所享有的权利。',
|
|
'当政策发生重大变更时,我们会在版本更新后通过站内推送、弹窗或短信等显著方式向您提示,您可查阅最新版本。重大变更包括但不限于:服务模式发生重大变化、个人信息处理目的与方式发生重大变化、您参与个人信息处理方面的权利及其行使方式发生重大变化。',
|
|
],
|
|
},
|
|
{
|
|
heading: '九、联系我们',
|
|
paragraphs: [
|
|
'如您对本政策或您的个人信息处理有任何疑问、意见或建议,可通过以下方式与我们联系:【待确认:客服邮箱 / 客服电话 / 个人信息保护负责人邮箱】。',
|
|
'公司名称:【待确认:公司全称】。注册地址:【待确认:注册地址】。我们将在收到您的请求后 15 个工作日内答复。',
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 用户协议正文
|
|
* 【待确认】文中公司全称、争议解决管辖法院、违约金条款等需法务确认后替换占位内容
|
|
*/
|
|
const MOCK_USER_AGREEMENT: LegalDocument = {
|
|
id: 'user-agreement',
|
|
title: '用户协议',
|
|
version: 'V1.0',
|
|
updatedAt: '2026-08-31',
|
|
effectiveAt: '2026-08-31',
|
|
preamble:
|
|
'本协议是你与有职平台运营方【待确认:公司全称】(以下简称「平台」)之间就使用有职平台服务所订立的协议。在使用平台各项服务前,请你务必审慎阅读、充分理解本协议各条款内容,特别是以粗体或下划线标注的免责条款、责任限制条款与争议解决条款。如你未满 18 周岁,请在法定监护人的陪同下阅读并决定是否同意本协议。',
|
|
sections: [
|
|
{
|
|
heading: '一、协议的范围与接受',
|
|
paragraphs: [
|
|
'本协议适用于你使用有职平台提供的全部服务,包括但不限于职位浏览与搜索、简历创建与投递、在线沟通、能力测评、竞争力排名、AI 模拟面试、求职进度管理以及企业端的职位发布、人才搜索与招聘管理功能。',
|
|
'你通过点击「同意」「注册」或以其他任何方式实际使用平台服务,即视为你已阅读并同意受本协议约束。若你不同意本协议,请立即停止使用平台服务。',
|
|
'平台有权在必要时修订本协议并在平台上公布。修订后的协议自公布之日起生效。若你继续使用平台服务,即视为你已接受修订后的协议。',
|
|
],
|
|
},
|
|
{
|
|
heading: '二、账号注册与使用',
|
|
paragraphs: [
|
|
'你需要使用本人真实手机号码完成注册,并保证所提供的注册信息真实、准确、完整,且及时更新。因你提供的信息不实导致的全部后果由你自行承担。',
|
|
'账号的所有权归平台所有,你仅享有账号的使用权。未经平台书面同意,你不得以任何形式出借、转让、出租或售卖账号。',
|
|
'你应妥善保管账号与密码,因你主动泄露、遗失或被他人盗用而导致的损失由你自行承担。发现账号异常时,你应立即通过平台提供的渠道申请冻结并修改密码。',
|
|
],
|
|
},
|
|
{
|
|
heading: '三、用户行为规范',
|
|
paragraphs: [
|
|
'你承诺在使用平台服务时遵守中华人民共和国法律法规、社会主义制度、国家利益、公民合法权益、公共秩序、社会道德风尚与信息真实性等「七条底线」。',
|
|
'你不得利用平台服务制作、复制、发布、传播如下内容:反对宪法所确定的基本原则的;危害国家安全、泄露国家秘密、颠覆国家政权、破坏国家统一的;损害国家荣誉和利益的;歪曲、丑化、亵渎、否定英雄烈士事迹和精神的;宣扬恐怖主义、极端主义或者煽动实施恐怖活动、极端主义活动的;煽动民族仇恨、民族歧视、破坏民族团结的;破坏国家宗教政策,宣扬邪教和封建迷信的;散布谣言,扰乱经济秩序和社会秩序的;散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;侮辱或者诽谤他人,侵害他人名誉、隐私和其他合法权益的;以及含有其他违反法律法规禁止性内容的信息。',
|
|
'你不得利用平台服务从事以下行为:使用任何插件、外挂、系统或第三方工具干扰平台正常运行;批量注册账号、批量投递或恶意刷取数据;爬取、抓取或以其他技术手段获取平台数据;发布虚假职位或虚假简历;冒用他人身份或企业名义;以及损害平台或第三方合法权益的其他行为。',
|
|
],
|
|
},
|
|
{
|
|
heading: '四、求职者服务规则',
|
|
paragraphs: [
|
|
'你应确保所填写的简历信息真实、准确,不得虚构学历、工作经历、项目经历或技能水平。因简历信息不实导致企业拒绝录用、解除劳动关系或产生其他争议的,由你自行承担全部责任。',
|
|
'平台提供的岗位信息由企业自行发布,平台已尽合理努力对企业资质与职位真实性进行审核,但不对职位的具体内容、薪酬实际发放、劳动条件及后续履约承担保证责任。你在应聘过程中应保持审慎,注意甄别招聘诈骗风险。',
|
|
'平台的竞争力排名、匹配度、测评结果等均为基于现有数据的参考性分析,不构成对你求职结果或录用概率的承诺或保证。',
|
|
],
|
|
},
|
|
{
|
|
heading: '五、企业服务规则',
|
|
paragraphs: [
|
|
'企业用户须提供真实、合法、有效的营业执照与认证材料,并确保所发布的职位信息真实、合法、准确,不存在就业歧视(包括但不限于性别、民族、宗教信仰、地域、年龄、健康状况等)内容。',
|
|
'企业用户不得发布虚假职位、以招聘为名实施培训贷、收取押金、扣押证件、传销或其他违法活动。一经发现,平台有权立即下架职位、冻结或注销账号,并保留向有关主管部门报告的权利。',
|
|
'企业用户应依法保护所获取的求职者个人信息,仅可将简历用于自身招聘目的,不得转售、泄露或用于其他商业用途。',
|
|
],
|
|
},
|
|
{
|
|
heading: '六、知识产权',
|
|
paragraphs: [
|
|
'平台及平台所使用的软件、技术、商标、材料等所有知识产权归平台或相关权利人所有。未经许可,任何人不得擅自使用、修改、复制、传播、反向工程或创造衍生作品。',
|
|
'你在使用平台服务期间发布上传的文字、图片、视频、音频等均由你原创或已获合法授权。你同意授予平台为提供服务之目的在全球范围内免费、非独占的使用许可。',
|
|
'你发布的原创内容著作权仍归你所有,但你的发布行为视为同意平台在本平台内外进行展示、推广及其他合法使用。',
|
|
],
|
|
},
|
|
{
|
|
heading: '七、免责声明与责任限制',
|
|
paragraphs: [
|
|
'鉴于网络服务的特殊性,你同意平台有权随时变更、中断或终止部分或全部服务(包括收费服务)。如因服务器维护、网络拥塞、黑客攻击、电信部门技术调整或故障、第三方网站或页面原因造成服务中断或数据丢失,平台不承担责任。',
|
|
'平台不对下列情形承担责任:你因第三方(包括其他用户、企业用户、外部链接指向的网站)的行为而遭受的损失;你自行泄露账号密码或授权他人使用而造成的损失;因不可抗力导致的损失。',
|
|
'在法律允许的最大范围内,平台就本协议所承担的赔偿责任总额,不超过你因使用该服务而向平台支付的费用总额;如你未支付费用,则平台不承担赔偿责任。',
|
|
],
|
|
},
|
|
{
|
|
heading: '八、违约处理',
|
|
paragraphs: [
|
|
'若你违反本协议任一条款,平台有权独立判断并立即采取以下一项或多项措施:警告并要求限期改正;删除违规内容;限制或禁止使用部分或全部功能;暂停或终止账号服务;注销账号并清除全部数据;以及追究法律责任。',
|
|
'因你的违约行为给平台或第三方造成损失的,你应承担全部赔偿责任,赔偿范围包括但不限于直接经济损失、商誉损失、律师费、诉讼费、公证费以及平台为处理投诉而支出的合理费用。',
|
|
],
|
|
},
|
|
{
|
|
heading: '九、协议变更与终止',
|
|
paragraphs: [
|
|
'平台有权在必要时修订本协议。协议修订后,平台将在相关页面公布修订后的协议。你继续使用平台服务即视为你已接受修订后的协议。',
|
|
'你有权随时停止使用平台服务并注销账号。账号注销后,本协议终止,但你在使用服务期间的既有义务(包括但不限于赔偿责任、保密义务)不因协议终止而免除。',
|
|
],
|
|
},
|
|
{
|
|
heading: '十、法律适用与争议解决',
|
|
paragraphs: [
|
|
'本协议的订立、执行与解释以及争议的解决均适用中华人民共和国大陆地区法律(不含冲突法)。',
|
|
'如双方就本协议内容或其执行发生任何争议,应尽量友好协商解决;协商不成时,任何一方均可向【待确认:管辖法院所在地】有管辖权的人民法院提起诉讼。',
|
|
'本协议所有条款的标题仅为阅读方便,本身并无实际涵义,不能作为本协议涵义解释的依据。',
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取法律文档(隐私政策 / 用户协议)
|
|
* @method GET
|
|
* @url /api/settings/legal-document/:id
|
|
* @param id { string } 文档 id,privacy-policy 或 user-agreement
|
|
* @returns Promise<LegalDocument | null> 找不到时返回 null
|
|
*/
|
|
export async function fetchLegalDocument(id: string): Promise<LegalDocument | null> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/settings/legal-document/${id}`, { method: 'GET' }); return res.json();
|
|
if (id === 'privacy-policy') return mockRequest({ ...MOCK_PRIVACY_POLICY });
|
|
if (id === 'user-agreement') return mockRequest({ ...MOCK_USER_AGREEMENT });
|
|
return mockRequest(null);
|
|
}
|
|
|
|
/* ===================== 个人数据导出 ===================== */
|
|
|
|
const MOCK_PERSONAL_DATA_EXPORT: PersonalDataExport = {
|
|
title: '导出你的个人数据',
|
|
description:
|
|
'你可以导出在平台产生的所有个人数据,包括基本信息、简历数据、求职记录、测评结果、消息记录、行为数据等。数据将以结构化的方式导出,便于你备份或迁移。',
|
|
categories: [
|
|
{
|
|
id: 'basic',
|
|
label: '基本信息',
|
|
description: '姓名、电话、邮箱、头像等',
|
|
size: '约20KB',
|
|
items: [{ id: 'basic-info', label: '基本信息', size: '约20KB', checked: true }],
|
|
},
|
|
{
|
|
id: 'resume',
|
|
label: '简历数据',
|
|
description: '简历内容、投递记录、收藏等',
|
|
size: '约30KB',
|
|
items: [{ id: 'resume-data', label: '简历数据', size: '约30KB', checked: true }],
|
|
},
|
|
{
|
|
id: 'job',
|
|
label: '求职记录',
|
|
description: '投递、沟通、面试记录、Offer记录等',
|
|
size: '约100KB',
|
|
items: [{ id: 'job-records', label: '求职记录', size: '约100KB', checked: true }],
|
|
},
|
|
{
|
|
id: 'assessment',
|
|
label: '测评数据',
|
|
description: '能力测评、职业兴趣测评结果等',
|
|
size: '约30KB',
|
|
items: [{ id: 'assessment-data', label: '测评数据', size: '约30KB', checked: true }],
|
|
},
|
|
{
|
|
id: 'message',
|
|
label: '消息记录',
|
|
description: '聊天消息、系统通知等',
|
|
size: '约200KB',
|
|
items: [{ id: 'message-data', label: '消息记录', size: '约200KB', checked: true }],
|
|
},
|
|
{
|
|
id: 'behavior',
|
|
label: '行为数据',
|
|
description: '浏览、搜索、点击记录等',
|
|
size: '约500KB',
|
|
items: [{ id: 'behavior-data', label: '行为数据', size: '约500KB', checked: true }],
|
|
},
|
|
],
|
|
formats: [
|
|
{
|
|
id: 'json',
|
|
label: 'JSON',
|
|
icon: 'logo-nodejs',
|
|
description: '结构化数据,便于开发',
|
|
},
|
|
{
|
|
id: 'excel',
|
|
label: 'Excel',
|
|
icon: 'grid-outline',
|
|
description: '表格格式,便于查看分析',
|
|
},
|
|
],
|
|
timeRanges: [
|
|
{ id: 'all', label: '全部数据' },
|
|
{ id: 'year', label: '近一年' },
|
|
{ id: 'quarter', label: '近三个月' },
|
|
{ id: 'custom', label: '自定义' },
|
|
],
|
|
selectedFormat: 'json',
|
|
selectedTimeRange: 'all',
|
|
preview: {
|
|
categoryCount: 4,
|
|
totalSize: '约200KB',
|
|
estimatedTime: '1-2分钟',
|
|
validity: '导出后7天',
|
|
},
|
|
recentExports: [
|
|
{
|
|
id: 'r1',
|
|
name: '完整数据导出.zip',
|
|
date: '2024-08-01 14:30',
|
|
size: '25KB',
|
|
status: 'expired',
|
|
},
|
|
{
|
|
id: 'r2',
|
|
name: '简历数据.xlsx',
|
|
date: '2024-07-31 10:12',
|
|
size: '80KB',
|
|
status: 'completed',
|
|
},
|
|
{
|
|
id: 'r3',
|
|
name: '测评数据.json',
|
|
date: '2024-07-25 09:08',
|
|
size: '30KB',
|
|
status: 'expired',
|
|
},
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取个人数据导出配置
|
|
* @method GET
|
|
* @url /api/settings/personal-data-export
|
|
* @returns Promise<PersonalDataExport> 个人数据导出数据
|
|
*/
|
|
export async function fetchPersonalDataExport(): Promise<PersonalDataExport> {
|
|
return mockRequest(MOCK_PERSONAL_DATA_EXPORT);
|
|
}
|
|
|
|
/**
|
|
* 申请导出个人数据
|
|
* @method POST
|
|
* @url /api/settings/personal-data-export
|
|
* @returns Promise<{ success: boolean; message: string }> 申请结果
|
|
*/
|
|
export async function requestPersonalDataExport(): Promise<{ success: boolean; message: string }> {
|
|
return mockRequest({ success: true, message: '导出申请已提交,预计 1-2 分钟后可在最近导出中下载。' });
|
|
}
|
|
|
|
/* ===================== 设置:消息通知 ===================== */
|
|
|
|
/** 通知开关(内存态) */
|
|
const MOCK_NOTIFICATION_SETTINGS: ToggleSetting[] = [
|
|
{
|
|
id: 'interview-remind',
|
|
title: '面试提醒',
|
|
description: '面试开始前 1 小时推送提醒',
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 'offer-notify',
|
|
title: 'Offer 通知',
|
|
description: '收到 Offer 或录用意向时通知我',
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 'job-recommend',
|
|
title: '岗位推荐',
|
|
description: '每日推送匹配度高的新岗位',
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 'hr-reply',
|
|
title: '企业回复',
|
|
description: 'HR 查看简历或回复消息时通知我',
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 'system-msg',
|
|
title: '系统消息',
|
|
description: '平台公告、规则更新等系统通知',
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 'marketing',
|
|
title: '活动与优惠',
|
|
description: '会员活动、法律服务优惠等营销推送',
|
|
enabled: false,
|
|
},
|
|
];
|
|
|
|
/**
|
|
* 获取消息通知开关列表
|
|
* @method GET
|
|
* @url /api/settings/notification
|
|
* @returns Promise<ToggleSetting[]> 通知开关列表
|
|
*/
|
|
export async function fetchNotificationSettings(): Promise<ToggleSetting[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/settings/notification`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_NOTIFICATION_SETTINGS);
|
|
}
|
|
|
|
/**
|
|
* 更新单个通知开关
|
|
* @method PATCH
|
|
* @url /api/settings/notification/:id
|
|
* @param payload {UpdateTogglePayload} 开关 id 与目标状态
|
|
* @returns Promise<ToggleSetting[]> 更新后的开关列表
|
|
*/
|
|
export async function updateNotificationSetting(
|
|
payload: UpdateTogglePayload,
|
|
): Promise<ToggleSetting[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/settings/notification/${payload.id}`, { method: 'PATCH', body: JSON.stringify(payload) }); return res.json();
|
|
const target = MOCK_NOTIFICATION_SETTINGS.find((item) => item.id === payload.id);
|
|
if (target) {
|
|
target.enabled = payload.enabled;
|
|
}
|
|
return mockRequest(MOCK_NOTIFICATION_SETTINGS);
|
|
}
|
|
|
|
/* ===================== 设置:账号与安全 ===================== */
|
|
|
|
/** 账号与安全操作项 */
|
|
const MOCK_ACCOUNT_SECURITY: ActionSetting[] = [
|
|
{
|
|
id: 'phone',
|
|
title: '手机号',
|
|
description: '用于登录与接收重要通知',
|
|
value: '138****8888',
|
|
variant: 'normal',
|
|
},
|
|
{
|
|
id: 'wechat',
|
|
title: '微信账号',
|
|
description: '绑定后可使用微信快捷登录',
|
|
value: '未绑定',
|
|
variant: 'normal',
|
|
},
|
|
{
|
|
id: 'password',
|
|
title: '修改登录密码',
|
|
description: '建议定期更新密码以保护账号安全',
|
|
variant: 'normal',
|
|
},
|
|
{
|
|
id: 'devices',
|
|
title: '登录设备管理',
|
|
description: '查看并移除已登录的设备',
|
|
variant: 'normal',
|
|
},
|
|
{
|
|
id: 'deactivate',
|
|
title: '注销账号',
|
|
description: '注销后账号不可恢复,且注销审核期间无法登录',
|
|
variant: 'danger',
|
|
},
|
|
];
|
|
|
|
/**
|
|
* 获取账号与安全操作项列表
|
|
* @method GET
|
|
* @url /api/settings/account-security
|
|
* @returns Promise<ActionSetting[]> 操作项列表
|
|
*/
|
|
export async function fetchAccountSecurity(): Promise<ActionSetting[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/settings/account-security`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_ACCOUNT_SECURITY);
|
|
}
|
|
|
|
/* ===================== 设置:关于我们 ===================== */
|
|
|
|
/** 关于我们信息 */
|
|
const MOCK_ABOUT_INFO: AboutInfo = {
|
|
appName: '有职',
|
|
version: 'v1.0.0',
|
|
company: '阿布阿诺人工智能科技有限公司',
|
|
description:
|
|
'有职是面向全社会青年求职者的双向透明求职生态平台,提供公司调研、真实评价、行业声誉与岗位评价分级,帮助用户找到更适合自己的工作。',
|
|
links: [
|
|
{ id: 'privacy-policy', title: '隐私政策' },
|
|
{ id: 'terms', title: '用户服务协议' },
|
|
{ id: 'legal', title: '阿布阿诺法律服务', route: '/legal' },
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取关于我们信息
|
|
* @method GET
|
|
* @url /api/settings/about
|
|
* @returns Promise<AboutInfo> 关于我们信息
|
|
*/
|
|
export async function fetchAboutInfo(): Promise<AboutInfo> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/settings/about`, { method: 'GET' }); return res.json();
|
|
return mockRequest(MOCK_ABOUT_INFO);
|
|
}
|
|
|
|
/* ===================== HR 聊天详情 ===================== */
|
|
|
|
/** HR 单聊会话 mock 数据(对应用户截图:张HR / 字节跳动 / 高级前端 / 95%匹配) */
|
|
const MOCK_CONVERSATION_DETAIL: ConversationDetail = {
|
|
id: 'zhang-hr',
|
|
contactName: '张HR',
|
|
companyName: '字节跳动',
|
|
positionName: '高级前端',
|
|
avatar: '张',
|
|
online: true,
|
|
matchInfo: { score: 95 },
|
|
messages: [
|
|
{
|
|
id: 'm0',
|
|
role: 'system',
|
|
content: '你们已经成为好友,开始聊天吧',
|
|
},
|
|
{
|
|
id: 'm1',
|
|
role: 'hr',
|
|
content: '您好,我是字节跳动的张HR,看到您的简历了,非常匹配我们的高级前端岗位。',
|
|
timestamp: '10:30',
|
|
},
|
|
{
|
|
id: 'm2',
|
|
role: 'hr',
|
|
content: '方便约个时间面试吗?我们这边流程比较快~',
|
|
timestamp: '10:30',
|
|
},
|
|
{
|
|
id: 'm3',
|
|
role: 'user',
|
|
content: '您好张HR,感谢关注!请问这个岗位的具体要求是什么?',
|
|
timestamp: '10:32',
|
|
},
|
|
{
|
|
id: 'm4',
|
|
role: 'hr',
|
|
content: '主要考察React、TypeScript和工程化能力,您可以先看看面试题准备一下~',
|
|
timestamp: '10:35',
|
|
},
|
|
{
|
|
id: 'm5',
|
|
role: 'hr',
|
|
timestamp: '10:35',
|
|
card: {
|
|
type: 'interview',
|
|
title: '面试题预测 · 前端高频',
|
|
items: [
|
|
'React Hooks 原理及常见坑',
|
|
'前端性能优化方案',
|
|
'微前端架构实践',
|
|
],
|
|
actionText: '查看全部 12 道预测题',
|
|
},
|
|
},
|
|
{
|
|
id: 'm6',
|
|
role: 'hr',
|
|
timestamp: '10:35',
|
|
card: {
|
|
type: 'salary',
|
|
title: '市场薪资参考',
|
|
subtitle: '该岗位 3-5 年经验市场薪资:22-38K\n你的期望薪资 30K 处于合理区间',
|
|
actionText: '查看详细薪资报告',
|
|
},
|
|
},
|
|
{
|
|
id: 'm7',
|
|
role: 'hr',
|
|
card: {
|
|
type: 'offer',
|
|
title: '你收到了新Offer,点击对比',
|
|
subtitle: '字节跳动 vs 腾讯科技,2个Offer',
|
|
actionText: '立即对比',
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
/**
|
|
* 获取 HR 单聊会话详情
|
|
* @method GET
|
|
* @url /api/chat/conversations/:id
|
|
* @param id {string} 会话 id
|
|
* @returns Promise<ConversationDetail> 会话详情
|
|
*/
|
|
export async function fetchConversation(id: string): Promise<ConversationDetail> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/chat/conversations/${id}`, { method: 'GET' }); return res.json();
|
|
return mockRequest({ ...MOCK_CONVERSATION_DETAIL, id });
|
|
}
|
|
|
|
/**
|
|
* 发送聊天消息(mock 阶段自动追加用户消息 + HR 自动回复)
|
|
* @method POST
|
|
* @url /api/chat/conversations/:id/messages
|
|
* @param conversationId {string} 会话 id
|
|
* @param content {string} 用户输入
|
|
* @returns Promise<ChatMessage[]> 新增的两条消息(用户 + 助手)
|
|
*/
|
|
export async function sendChatMessage(
|
|
_conversationId: string,
|
|
content: string,
|
|
): Promise<ChatMessage[]> {
|
|
// TODO: const res = await fetch(`${API_BASE_URL}/api/chat/conversations/${conversationId}/messages`, { method: 'POST', body: JSON.stringify({ content }) }); return res.json();
|
|
const now = new Date();
|
|
const time = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`;
|
|
const userMessage: ChatMessage = {
|
|
id: `u-${Date.now()}`,
|
|
role: 'user',
|
|
content,
|
|
timestamp: time,
|
|
};
|
|
const hrReply: ChatMessage = {
|
|
id: `a-${Date.now()}`,
|
|
role: 'hr',
|
|
content: '收到,我会尽快查看并回复你。',
|
|
timestamp: time,
|
|
};
|
|
return mockRequest([userMessage, hrReply]);
|
|
}
|
|
|
|
/* ===================== 账号:手机号 + 验证码登录 ===================== */
|
|
|
|
export interface LoginResult {
|
|
/** 登录令牌(mock 态为占位串,真实后端返回 JWT 等) */
|
|
token: string;
|
|
/** 登录手机号 */
|
|
phone: string;
|
|
}
|
|
|
|
/**
|
|
* 手机号 + 短信验证码登录(登录即注册,二合一)。
|
|
* @method POST
|
|
* @url /api/auth/login-by-phone
|
|
* @param phone 手机号(11 位)
|
|
* @param code 短信验证码(6 位)
|
|
* @returns 登录结果(含 token)
|
|
*/
|
|
export function loginByPhone(phone: string, code: string): Promise<LoginResult> {
|
|
if (code.trim().length < 4) {
|
|
return Promise.reject(new Error('验证码不正确'));
|
|
}
|
|
return mockRequest<LoginResult>({ token: `mock_token_${phone}`, phone });
|
|
}
|
|
|
|
/* ===================== 账号:微信登录(三种账号状态) ===================== */
|
|
|
|
/*
|
|
* mock 专用:切换下一次微信登录返回的账号状态,便于在 Expo Go 里逐个验证三种分支。
|
|
* 真实后端就绪后删除本变量与 setMockWechatAccountStatus。
|
|
*/
|
|
let MOCK_WECHAT_STATUS: WechatAccountStatus = 'new';
|
|
|
|
/**
|
|
* 【仅 mock 阶段】设置下一次微信登录命中哪种账号状态。
|
|
* @param status 'new' 第一次登录 | 'active' 第二次及以后 | 'deleted' 注销审核期
|
|
*/
|
|
export function setMockWechatAccountStatus(status: WechatAccountStatus): void {
|
|
MOCK_WECHAT_STATUS = status;
|
|
}
|
|
|
|
/**
|
|
* 用微信授权 code 换取登录结果。
|
|
*
|
|
* 后端职责:code → access_token + openid → 查库判定账号状态。
|
|
* 注意 code 一次性且 5 分钟过期,因此后端需在本响应里下发可复用的临时凭证,
|
|
* 供后续「绑定手机号」回传(前端拿不到第二个 code)。
|
|
*
|
|
* @method POST
|
|
* @url /api/auth/wechat
|
|
* @param code 微信授权返回的一次性 code
|
|
* @returns 账号状态与对应令牌;new 时 token 为临时凭证,deleted 时 token 无意义
|
|
*/
|
|
export function fetchWechatLoginResult(code: string): Promise<WechatLoginResult> {
|
|
if (!code) {
|
|
return Promise.reject(new Error('微信授权失败,请重试'));
|
|
}
|
|
|
|
if (MOCK_WECHAT_STATUS === 'active') {
|
|
return mockRequest<WechatLoginResult>({
|
|
status: 'active',
|
|
token: `mock_wx_token_${code.slice(-6)}`,
|
|
openid: 'mock_openid_active',
|
|
expiresAt: Date.now() + TOKEN_TTL_MS,
|
|
role: 'candidate',
|
|
profile: {
|
|
id: 'u-wx-001',
|
|
nickname: '微信用户_8f3a',
|
|
avatarUri: '',
|
|
gender: 'unspecified',
|
|
},
|
|
});
|
|
}
|
|
|
|
// 注销审核期内:不可登录、不可复活,前端只提示并停留在登录页
|
|
if (MOCK_WECHAT_STATUS === 'deleted') {
|
|
return mockRequest<WechatLoginResult>({
|
|
status: 'deleted',
|
|
token: '',
|
|
openid: 'mock_openid_deleted',
|
|
});
|
|
}
|
|
|
|
return mockRequest<WechatLoginResult>({
|
|
status: 'new',
|
|
token: `mock_wx_temp_${code.slice(-6)}`,
|
|
openid: 'mock_openid_new',
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 微信新用户绑定手机号,完成注册(第一次登录的必经步骤)。
|
|
*
|
|
* 用户走完微信授权后 openid 未绑定任何账号,必须用手机号 + 短信验证码完成注册,
|
|
* 后端据此把 openid 与新账号关联。此时 code 已失效,只能回传临时凭证。
|
|
*
|
|
* @method POST
|
|
* @url /api/auth/wechat/bind-phone
|
|
* @param payload 绑定参数(临时凭证 + 手机号 + 验证码)
|
|
* @returns 正式会话(token + 过期时间 + 用户资料)
|
|
*/
|
|
export function bindWechatAndRegister(
|
|
payload: WechatBindPhonePayload,
|
|
): Promise<WechatSession> {
|
|
if (!/^1\d{10}$/.test(payload.phone)) {
|
|
return Promise.reject(new Error('手机号格式不正确'));
|
|
}
|
|
if (payload.code.trim().length < 4) {
|
|
return Promise.reject(new Error('验证码不正确'));
|
|
}
|
|
return mockRequest<WechatSession>({
|
|
token: `mock_wx_session_${payload.openid.slice(-6)}`,
|
|
expiresAt: Date.now() + TOKEN_TTL_MS,
|
|
profile: {
|
|
id: `u-wx-${payload.openid.slice(-6)}`,
|
|
nickname: `微信用户_${payload.phone.slice(-4)}`,
|
|
avatarUri: '',
|
|
gender: 'unspecified',
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 保存求职意向(求职者选择身份后的必经设置步骤,用于岗位推荐匹配)。
|
|
* @method POST
|
|
* @url /api/user/job-preference
|
|
* @param preference 求职意向(期望职位/城市/薪资/工作类型/求职状态)
|
|
* @returns 保存后的求职意向
|
|
*/
|
|
export function saveJobPreference(preference: JobPreference): Promise<JobPreference> {
|
|
return mockRequest<JobPreference>(preference);
|
|
}
|
|
|