Browse Source

feat(data): 扩展类型定义、mock 数据与 hooks 层

类型层(src/types):
- 新增 14 个业务领域类型:enterprise / data-export / settings / rank / compare / offer-compare / evaluation / game / interview / legal / operation / resume / tools / weight
- 扩展 JobManagement、TalentRecommendation、EnterpriseCommunication,新增 hasData 字段驱动空态与数据态分支

接口层(src/api):
- 重写企业端 mock 数据(岗位管理 / 人才推荐 / 企业沟通),默认 hasData: true
- 新增隐私中心与个人数据导出的 mock 数据与接口
- 预留真实 fetch 示例,签名保持不变

Hooks 层(src/hooks):
- 新增 42 个业务 hooks,统一基于 useAsyncData 封装
- 新增 usePrivacyCenter、usePersonalDataExport、useWeightSettings
- 扩展 useProfileData 接入企业端入口
master
doublex001 2 months ago
parent
commit
37182f47ab
  1. 3670
      src/api/api.ts
  2. 73
      src/api/index.ts
  3. 43
      src/hooks/index.ts
  4. 64
      src/hooks/useAIInterview.ts
  5. 19
      src/hooks/useAbilityAssessment.ts
  6. 30
      src/hooks/useAboutInfo.ts
  7. 30
      src/hooks/useAccountSecurity.ts
  8. 79
      src/hooks/useAppealForm.ts
  9. 18
      src/hooks/useAuditReport.ts
  10. 36
      src/hooks/useCandidateDetail.ts
  11. 26
      src/hooks/useCompareData.ts
  12. 59
      src/hooks/useConversation.ts
  13. 35
      src/hooks/useEnterpriseCertification.ts
  14. 19
      src/hooks/useEnterpriseCommunication.ts
  15. 19
      src/hooks/useEnterpriseOverview.ts
  16. 84
      src/hooks/useGameData.ts
  17. 19
      src/hooks/useHREvaluation.ts
  18. 38
      src/hooks/useHollandAssessment.ts
  19. 19
      src/hooks/useHollandIntro.ts
  20. 20
      src/hooks/useIdentityOptions.ts
  21. 73
      src/hooks/useInterviewScore.ts
  22. 19
      src/hooks/useJobEvaluationEmptyState.ts
  23. 32
      src/hooks/useJobEvaluations.ts
  24. 19
      src/hooks/useJobManagement.ts
  25. 20
      src/hooks/useJobProgress.ts
  26. 96
      src/hooks/useJobPublish.ts
  27. 111
      src/hooks/useLegalData.ts
  28. 9
      src/hooks/useNotificationSettings.ts
  29. 26
      src/hooks/useOfferCompareData.ts
  30. 34
      src/hooks/useOnboardingChecklist.ts
  31. 33
      src/hooks/useOperationRules.ts
  32. 40
      src/hooks/usePersonalDataExport.ts
  33. 41
      src/hooks/usePrivacyCenter.ts
  34. 9
      src/hooks/usePrivacySettings.ts
  35. 21
      src/hooks/useProfileData.ts
  36. 75
      src/hooks/useRankData.ts
  37. 25
      src/hooks/useResume.ts
  38. 48
      src/hooks/useResumeExport.ts
  39. 21
      src/hooks/useSalaryBrackets.ts
  40. 24
      src/hooks/useSalaryCalculator.ts
  41. 24
      src/hooks/useSkillCloud.ts
  42. 19
      src/hooks/useTalentRecommendation.ts
  43. 54
      src/hooks/useToggleSettings.ts
  44. 91
      src/hooks/useWeightSettings.ts
  45. 41
      src/types/compare.ts
  46. 101
      src/types/data-export.ts
  47. 637
      src/types/enterprise.ts
  48. 92
      src/types/evaluation.ts
  49. 114
      src/types/game.ts
  50. 191
      src/types/growth.ts
  51. 211
      src/types/index.ts
  52. 105
      src/types/interview.ts
  53. 22
      src/types/job.ts
  54. 103
      src/types/legal.ts
  55. 63
      src/types/message.ts
  56. 83
      src/types/offer-compare.ts
  57. 121
      src/types/operation.ts
  58. 96
      src/types/rank.ts
  59. 171
      src/types/resume.ts
  60. 123
      src/types/settings.ts
  61. 348
      src/types/tools.ts
  62. 8
      src/types/user.ts
  63. 31
      src/types/weight.ts
  64. 10
      src/utils/index.ts

3670
src/api/api.ts

File diff suppressed because it is too large

73
src/api/index.ts

@ -2,31 +2,92 @@
* 接口统一导出
*/
export {
fetchAboutInfo,
fetchAbilityAssessment,
fetchAccountSecurity,
fetchAIInterviewData,
fetchAITools,
fetchAppealForm,
fetchArticles,
fetchAssessmentTools,
fetchAuditReport,
fetchBadges,
fetchCategories,
fetchCandidateDetail,
fetchCompareData,
fetchConversations,
fetchConversation,
fetchDimensionScores,
sendChatMessage,
fetchEnterpriseCertification,
fetchEnterpriseCommunication,
fetchEnterpriseOverview,
fetchGameLevels,
fetchGameProgress,
fetchHollandIntro,
fetchInterviewScore,
fetchJobEvaluationEmptyState,
fetchJobEvaluationManagement,
fetchJobManagement,
fetchJobPublishForm,
fetchTalentRecommendation,
fetchLegalBrand,
fetchLegalConversation,
fetchLegalFAQs,
fetchLegalPlans,
fetchLegalServices,
fetchLegalTemplates,
fetchEmployeeReviews,
fetchEnterpriseEntry,
fetchHollandAssessment,
fetchHomeAITools,
fetchHotJobs,
fetchHollandResult,
fetchHREvaluation,
fetchIdentityOptions,
fetchInterviews,
fetchJobDetail,
fetchJobList,
fetchOfferCompareData,
fetchJobOverallScore,
fetchJobPreference,
fetchMatchData,
fetchJobProgress,
fetchMatchResult,
fetchMenuItems,
fetchNotifications,
fetchNotificationSettings,
fetchOnboardingChecklist,
fetchOperationRules,
fetchOverallScore,
fetchPrivacySettings,
fetchPrivacyCenter,
updatePrivacyCenterSetting,
fetchPersonalDataExport,
requestPersonalDataExport,
fetchRankData,
fetchResume,
fetchResumeExportSettings,
fetchSalaryBrackets,
fetchSalaryData,
fetchUserInfo,
fetchSkillCloud,
fetchUserProfile,
fetchUserStats,
fetchWeightSettings,
inviteCandidate,
estimateJobQuality,
optimizeResume,
publishJob,
replyJobReview,
saveOperationRules,
saveResumeExportSettings,
exportResume,
saveWeightSettings,
submitEnterpriseCertification,
submitInterviewScore,
computeSalaryResult,
resetGameProgress,
sendLegalMessage,
startInterview,
submitAppeal,
submitGameLevel,
submitHollandAnswer,
toggleChecklistItem,
updateNotificationSetting,
updatePrivacySetting,
} from './api';

43
src/hooks/index.ts

@ -1,13 +1,48 @@
/**
* hooks 统一导出
*/
export { useAbilityAssessment } from './useAbilityAssessment';
export { useAboutInfo } from './useAboutInfo';
export { useAccountSecurity } from './useAccountSecurity';
export { useAIInterview } from './useAIInterview';
export { useAppealForm } from './useAppealForm';
export { useAsyncData } from './useAsyncData';
export { useCategoryData } from './useCategoryData';
export { useAuditReport } from './useAuditReport';
export { useCandidateDetail } from './useCandidateDetail';
export { useCompareData } from './useCompareData';
export { useConversation } from './useConversation';
export { useEnterpriseCertification } from './useEnterpriseCertification';
export { useEnterpriseCommunication } from './useEnterpriseCommunication';
export { useEnterpriseOverview } from './useEnterpriseOverview';
export { useGameData } from './useGameData';
export { useGrowthData } from './useGrowthData';
export { useHomeData } from './useHomeData';
export { useHollandAssessment } from './useHollandAssessment';
export { useHollandIntro } from './useHollandIntro';
export { useHREvaluation } from './useHREvaluation';
export { useIdentityOptions } from './useIdentityOptions';
export { useInterviewScore } from './useInterviewScore';
export { useJobDetail } from './useJobDetail';
export { useJobEvaluationEmptyState } from './useJobEvaluationEmptyState';
export { useJobEvaluations } from './useJobEvaluations';
export { useJobList } from './useJobList';
export { useMatchData } from './useMatchData';
export { useJobManagement } from './useJobManagement';
export { useJobProgress } from './useJobProgress';
export { useJobPublish } from './useJobPublish';
export { useLegalData } from './useLegalData';
export { useMessageData } from './useMessageData';
export { useNotificationSettings } from './useNotificationSettings';
export { useOfferCompareData } from './useOfferCompareData';
export { useOnboardingChecklist } from './useOnboardingChecklist';
export { useOperationRules } from './useOperationRules';
export { usePrivacyCenter } from './usePrivacyCenter';
export { usePrivacySettings } from './usePrivacySettings';
export { useProfileData } from './useProfileData';
export { useUserProfile } from './useUserProfile';
export { usePersonalDataExport } from './usePersonalDataExport';
export { useRankData } from './useRankData';
export { useResume } from './useResume';
export { useResumeExport } from './useResumeExport';
export { useSalaryBrackets } from './useSalaryBrackets';
export { useSalaryCalculator } from './useSalaryCalculator';
export { useSkillCloud } from './useSkillCloud';
export { useTalentRecommendation } from './useTalentRecommendation';
export { useWeightSettings } from './useWeightSettings';

64
src/hooks/useAIInterview.ts

@ -0,0 +1,64 @@
/**
* AI 模拟面试 hook:拉取面试配置 + 开始面试。
*/
import { useCallback, useState } from 'react';
import { fetchAIInterviewData, startInterview } from '../api';
import type { AIInterviewData, InterviewMode, InterviewQuestionTypeCode, InterviewSession } from '../types';
import { useAsyncData } from './useAsyncData';
export function useAIInterview() {
const fetcher = useCallback(() => fetchAIInterviewData(), []);
const { data, loading, error, refresh } = useAsyncData<AIInterviewData>(fetcher);
/** 本地交互状态:模式 / 题型 / 专项 */
const [mode, setMode] = useState<InterviewMode | null>(null);
const [questionTypes, setQuestionTypes] = useState<InterviewQuestionTypeCode[]>([]);
const [specials, setSpecials] = useState<string[]>([]);
const [starting, setStarting] = useState(false);
const resolvedMode = mode ?? data?.mode ?? 'full';
const resolvedTypes = questionTypes.length
? questionTypes
: (data?.questionTypes.filter((t) => t.selected).map((t) => t.id) ?? []);
const resolvedSpecials = specials.length
? specials
: (data?.specials.filter((s) => s.selected).map((s) => s.id) ?? []);
const toggleQuestionType = useCallback((id: InterviewQuestionTypeCode) => {
setQuestionTypes((prev) => {
const base =
prev.length > 0
? prev
: ([] as InterviewQuestionTypeCode[]);
return base.includes(id) ? base.filter((t) => t !== id) : [...base, id];
});
}, []);
const toggleSpecial = useCallback((id: string) => {
setSpecials((prev) => (prev.includes(id) ? prev.filter((s) => s !== id) : [...prev, id]));
}, []);
const start = useCallback(async (): Promise<InterviewSession> => {
setStarting(true);
try {
return await startInterview();
} finally {
setStarting(false);
}
}, []);
return {
data: data ?? null,
loading,
error,
refresh,
mode: resolvedMode,
setMode,
questionTypes: resolvedTypes,
toggleQuestionType,
specials: resolvedSpecials,
toggleSpecial,
start,
starting,
};
}

19
src/hooks/useAbilityAssessment.ts

@ -0,0 +1,19 @@
/**
* 四维能力测评 hook:拉取能力测评结果,统一管理 loading / error / refresh。
*/
import { useCallback } from 'react';
import { fetchAbilityAssessment } from '../api';
import { useAsyncData } from './useAsyncData';
export function useAbilityAssessment() {
const fetcher = useCallback(() => fetchAbilityAssessment(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
ability: data ?? null,
loading,
error,
refresh,
};
}

30
src/hooks/useAboutInfo.ts

@ -0,0 +1,30 @@
/**
* 关于我们 hook:拉取应用版本、公司主体与协议链接。
*/
import { useCallback } from 'react';
import { fetchAboutInfo } from '../api';
import type { AboutInfo } from '../types';
import { useAsyncData } from './useAsyncData';
export interface AboutInfoState {
/** 关于我们信息 */
about: AboutInfo | null;
/** 是否加载中 */
loading: boolean;
/** 错误对象 */
error: Error | null;
/** 刷新数据 */
refresh: () => void;
}
export function useAboutInfo(): AboutInfoState {
const fetcher = useCallback(() => fetchAboutInfo(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
about: (data as AboutInfo | null) ?? null,
loading,
error,
refresh,
};
}

30
src/hooks/useAccountSecurity.ts

@ -0,0 +1,30 @@
/**
* 账号与安全 hook:拉取账号安全操作项列表(绑定手机 / 微信 / 改密 / 注销等)。
*/
import { useCallback } from 'react';
import { fetchAccountSecurity } from '../api';
import type { ActionSetting } from '../types';
import { useAsyncData } from './useAsyncData';
export interface AccountSecurityState {
/** 操作项列表 */
items: ActionSetting[];
/** 是否加载中 */
loading: boolean;
/** 错误对象 */
error: Error | null;
/** 刷新数据 */
refresh: () => void;
}
export function useAccountSecurity(): AccountSecurityState {
const fetcher = useCallback(() => fetchAccountSecurity(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
items: (data as ActionSetting[] | null) ?? [],
loading,
error,
refresh,
};
}

79
src/hooks/useAppealForm.ts

@ -0,0 +1,79 @@
/**
* 评价申诉 hook:拉取申诉表单 + 提交申诉。
*/
import { useCallback, useEffect, useState } from 'react';
import { fetchAppealForm, submitAppeal } from '../api';
import type { AppealReason, AppealType, EvaluationAppealForm } from '../types';
import { useAsyncData } from './useAsyncData';
export function useAppealForm() {
const fetcher = useCallback(() => fetchAppealForm(), []);
const { data, loading, error, refresh } = useAsyncData<EvaluationAppealForm>(fetcher);
/** 本地表单状态 */
const [type, setType] = useState<AppealType>('overall');
const [dimensions, setDimensions] = useState<string[]>([]);
const [reason, setReason] = useState<AppealReason | null>(null);
const [reasonText, setReasonText] = useState('');
const [contactPhone, setContactPhone] = useState('');
const [contactEmail, setContactEmail] = useState('');
const [agree, setAgree] = useState(false);
const [submitting, setSubmitting] = useState(false);
useEffect(() => {
if (data && dimensions.length === 0) {
setType(data.type);
setReason(data.reason);
}
}, [data, dimensions.length]);
const toggleDimension = useCallback((id: string) => {
setDimensions((prev) => (prev.includes(id) ? prev.filter((d) => d !== id) : [...prev, id]));
}, []);
const submit = useCallback(async () => {
if (!data) return null;
setSubmitting(true);
try {
const payload: EvaluationAppealForm = {
...data,
type,
dimensions: data.dimensions.map((d) => ({
...d,
selected: dimensions.includes(d.id),
})),
reason: reason ?? data.reason,
reasonText,
contactPhone,
contactEmail,
agree,
};
return await submitAppeal(payload);
} finally {
setSubmitting(false);
}
}, [data, type, dimensions, reason, reasonText, contactPhone, contactEmail, agree]);
return {
form: data ?? null,
loading,
error,
refresh,
type,
setType,
dimensions,
toggleDimension,
reason,
setReason,
reasonText,
setReasonText,
contactPhone,
setContactPhone,
contactEmail,
setContactEmail,
agree,
setAgree,
submit,
submitting,
};
}

18
src/hooks/useAuditReport.ts

@ -0,0 +1,18 @@
/**
* 数据审计报告 hook
*/
import { useCallback } from 'react';
import { fetchAuditReport } from '../api';
import { useAsyncData } from './useAsyncData';
export function useAuditReport() {
const fetcher = useCallback(() => fetchAuditReport(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
report: data,
loading,
error,
refresh,
};
}

36
src/hooks/useCandidateDetail.ts

@ -0,0 +1,36 @@
/**
* 候选人详情 hook:拉取候选人详情 + 邀请面试。
*/
import { useCallback, useState } from 'react';
import { fetchCandidateDetail, inviteCandidate } from '../api';
import type { CandidateDetail } from '../types';
import { useAsyncData } from './useAsyncData';
export function useCandidateDetail(candidateId: string) {
const fetcher = useCallback(() => fetchCandidateDetail(candidateId), [candidateId]);
const { data, loading, error, refresh } = useAsyncData<CandidateDetail>(fetcher);
const [inviting, setInviting] = useState(false);
const [invited, setInvited] = useState(false);
const invite = useCallback(async () => {
setInviting(true);
try {
await inviteCandidate(candidateId);
setInvited(true);
return true;
} finally {
setInviting(false);
}
}, [candidateId]);
return {
candidate: data ?? null,
loading,
error,
refresh,
invite,
inviting,
invited,
};
}

26
src/hooks/useCompareData.ts

@ -0,0 +1,26 @@
/**
* 岗位对比数据 hook
*/
import { useCallback } from 'react';
import { fetchCompareData } from '../api';
import type { CompareData } from '../types';
import { useAsyncData } from './useAsyncData';
/** 默认对比岗位 id(未显式传入时使用,保持引用稳定避免重复请求) */
const DEFAULT_COMPARE_JOB_IDS = ['1', '2', '3'];
export function useCompareData(jobIds: string[] = DEFAULT_COMPARE_JOB_IDS) {
const fetcher = useCallback(() => fetchCompareData(jobIds), [jobIds]);
const { data, loading, error, refresh } = useAsyncData<CompareData>(fetcher);
return {
jobs: data?.jobs ?? [],
dimensionScores: data?.dimensionScores ?? [],
basicInfo: data?.basicInfo ?? { fields: [], values: [] },
benefits: data?.benefits ?? [],
loading,
error,
refresh,
};
}

59
src/hooks/useConversation.ts

@ -0,0 +1,59 @@
/**
* HR 单聊会话详情 hook:拉取会话详情,并提供发送消息能力。
* 发送消息后自动刷新会话,把新消息追加到本地列表。
*/
import { useCallback, useState } from 'react';
import { fetchConversation, sendChatMessage } from '../api';
import type { ConversationDetail } from '../types';
import { useAsyncData } from './useAsyncData';
export interface ConversationState {
/** 会话详情 */
detail: ConversationDetail | null;
/** 是否加载中 */
loading: boolean;
/** 错误对象 */
error: Error | null;
/** 刷新数据 */
refresh: () => void;
/** 发送消息 */
sendMessage: (content: string) => Promise<void>;
/** 发送中状态 */
sending: boolean;
}
export function useConversation(conversationId: string): ConversationState {
const [sending, setSending] = useState(false);
const fetcher = useCallback(() => fetchConversation(conversationId), [conversationId]);
const { data, loading, error, refresh } = useAsyncData(fetcher);
const sendMessage = useCallback(
async (content: string) => {
setSending(true);
try {
const newMessages = await sendChatMessage(conversationId, content);
// mock 阶段:直接把新消息合并到本地数据,避免重新拉取整屏
const current = data as ConversationDetail | null;
if (current) {
current.messages.push(...newMessages);
refresh();
} else {
refresh();
}
} finally {
setSending(false);
}
},
[conversationId, data, refresh],
);
return {
detail: (data as ConversationDetail | null) ?? null,
loading,
error,
refresh,
sendMessage,
sending,
};
}

35
src/hooks/useEnterpriseCertification.ts

@ -0,0 +1,35 @@
/**
* 企业认证 hook
*/
import { useCallback, useState } from 'react';
import { fetchEnterpriseCertification, submitEnterpriseCertification } from '../api';
import type { EnterpriseCertificationForm, EnterpriseCertificationStatus } from '../types';
import { useAsyncData } from './useAsyncData';
export function useEnterpriseCertification() {
const fetcher = useCallback(() => fetchEnterpriseCertification(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
const [submitting, setSubmitting] = useState(false);
const [status, setStatus] = useState<EnterpriseCertificationStatus | null>(null);
const submit = useCallback(async (form: EnterpriseCertificationForm) => {
setSubmitting(true);
try {
const res = await submitEnterpriseCertification(form);
setStatus(res.status);
return res;
} finally {
setSubmitting(false);
}
}, []);
return {
certification: data,
status,
loading,
error,
submitting,
refresh,
submit,
};
}

19
src/hooks/useEnterpriseCommunication.ts

@ -0,0 +1,19 @@
/**
* 企业端沟通数据 hook。
*/
import { useCallback } from 'react';
import { fetchEnterpriseCommunication } from '../api';
import type { EnterpriseCommunication } from '../types';
import { useAsyncData } from './useAsyncData';
export function useEnterpriseCommunication() {
const fetcher = useCallback(async (): Promise<EnterpriseCommunication> => fetchEnterpriseCommunication(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
communication: data ?? null,
loading,
error,
refresh,
};
}

19
src/hooks/useEnterpriseOverview.ts

@ -0,0 +1,19 @@
/**
* 企业数据总览 hook:拉取企业总览数据。
*/
import { useCallback } from 'react';
import { fetchEnterpriseOverview } from '../api';
import type { EnterpriseOverview } from '../types';
import { useAsyncData } from './useAsyncData';
export function useEnterpriseOverview() {
const fetcher = useCallback(() => fetchEnterpriseOverview(), []);
const { data, loading, error, refresh } = useAsyncData<EnterpriseOverview>(fetcher);
return {
overview: data ?? null,
loading,
error,
refresh,
};
}

84
src/hooks/useGameData.ts

@ -0,0 +1,84 @@
/**
* 通关小游戏 hook:获取关卡配置与进度,并提供提交答案、重置进度能力。
*/
import { useCallback, useState } from 'react';
import { fetchGameLevels, fetchGameProgress, resetGameProgress, submitGameLevel } from '../api';
import type { GameLevel, GameProgress, SubmitGameLevelPayload } from '../types';
import { useAsyncData } from './useAsyncData';
export interface GameDataState {
/** 关卡配置列表 */
levels: GameLevel[];
/** 当前游戏进度 */
progress: GameProgress | null;
/** 是否加载中 */
loading: boolean;
/** 错误对象 */
error: Error | null;
/** 刷新数据 */
refresh: () => void;
/** 提交关卡答案 */
submitLevel: (payload: SubmitGameLevelPayload) => Promise<GameProgress | null>;
/** 提交中状态 */
submitting: boolean;
/** 重置进度 */
reset: () => Promise<void>;
}
export function useGameData(): GameDataState {
const [submitting, setSubmitting] = useState(false);
const levelsFetcher = useCallback(() => fetchGameLevels(), []);
const progressFetcher = useCallback(() => fetchGameProgress(), []);
const {
data: levelsData,
loading: levelsLoading,
error: levelsError,
refresh: refreshLevels,
} = useAsyncData(levelsFetcher);
const {
data: progressData,
loading: progressLoading,
error: progressError,
refresh: refreshProgress,
} = useAsyncData(progressFetcher);
const refresh = useCallback(() => {
refreshLevels();
refreshProgress();
}, [refreshLevels, refreshProgress]);
const submitLevel = useCallback(
async (payload: SubmitGameLevelPayload): Promise<GameProgress | null> => {
setSubmitting(true);
try {
const next = await submitGameLevel(payload);
refreshProgress();
return next;
} catch (err) {
return null;
} finally {
setSubmitting(false);
}
},
[refreshProgress],
);
const reset = useCallback(async () => {
await resetGameProgress();
refreshProgress();
}, [refreshProgress]);
return {
levels: (levelsData as GameLevel[] | null) ?? [],
progress: (progressData as GameProgress | null) ?? null,
loading: levelsLoading || progressLoading,
error: levelsError ?? progressError,
refresh,
submitLevel,
submitting,
reset,
};
}

19
src/hooks/useHREvaluation.ts

@ -0,0 +1,19 @@
/**
* HR 评价详情 hook:拉取评价详情。
*/
import { useCallback } from 'react';
import { fetchHREvaluation } from '../api';
import type { HREvaluation } from '../types';
import { useAsyncData } from './useAsyncData';
export function useHREvaluation() {
const fetcher = useCallback(() => fetchHREvaluation(), []);
const { data, loading, error, refresh } = useAsyncData<HREvaluation>(fetcher);
return {
evaluation: data ?? null,
loading,
error,
refresh,
};
}

38
src/hooks/useHollandAssessment.ts

@ -0,0 +1,38 @@
/**
* 霍兰德职业兴趣测评 hook:拉取测评进度与结果,支持提交答案。
*/
import { useCallback, useState } from 'react';
import { fetchHollandAssessment, fetchHollandResult, submitHollandAnswer } from '../api';
import { useAsyncData } from './useAsyncData';
export function useHollandAssessment() {
const fetcher = useCallback(() => fetchHollandAssessment(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
const [submitting, setSubmitting] = useState(false);
const submit = useCallback(
async (questionId: string, value: number) => {
setSubmitting(true);
try {
const next = await submitHollandAnswer(questionId, value);
refresh();
return next;
} finally {
setSubmitting(false);
}
},
[refresh],
);
const loadResult = useCallback(async () => fetchHollandResult(), []);
return {
assessment: data ?? null,
loading,
error,
refresh,
submit,
submitting,
loadResult,
};
}

19
src/hooks/useHollandIntro.ts

@ -0,0 +1,19 @@
/**
* 霍兰德职业兴趣测评介绍页数据 hook。
*/
import { useCallback } from 'react';
import { fetchHollandIntro } from '../api';
import type { HollandIntro } from '../types';
import { useAsyncData } from './useAsyncData';
export function useHollandIntro() {
const fetcher = useCallback(async (): Promise<HollandIntro> => fetchHollandIntro(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
intro: data ?? null,
loading,
error,
refresh,
};
}

20
src/hooks/useIdentityOptions.ts

@ -0,0 +1,20 @@
/**
* 选择身份 hook:拉取身份选项(求职者 / 企业 HR),统一管理 loading / error / refresh。
*/
import { useCallback } from 'react';
import { fetchIdentityOptions } from '../api';
import type { IdentityOption } from '../types';
import { useAsyncData } from './useAsyncData';
export function useIdentityOptions() {
const fetcher = useCallback(() => fetchIdentityOptions(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
options: (data as IdentityOption[] | null) ?? [],
loading,
error,
refresh,
};
}

73
src/hooks/useInterviewScore.ts

@ -0,0 +1,73 @@
/**
* 面试评分 hook:拉取评分数据 + 本地编辑 + 提交评分。
*/
import { useCallback, useEffect, useState } from 'react';
import { fetchInterviewScore, submitInterviewScore } from '../api';
import type { HireSuggestion, InterviewScoreData } from '../types';
import { useAsyncData } from './useAsyncData';
export function useInterviewScore(interviewId: string) {
const fetcher = useCallback(() => fetchInterviewScore(interviewId), [interviewId]);
const { data, loading, error, refresh } = useAsyncData<InterviewScoreData>(fetcher);
const [score, setScore] = useState<InterviewScoreData | null>(null);
const [submitting, setSubmitting] = useState(false);
useEffect(() => {
if (data && !score) {
setScore(data);
}
}, [data, score]);
const updateDimensionScore = useCallback((id: string, value: number) => {
setScore((prev) => {
if (!prev) return prev;
const dimensions = prev.dimensions.map((d) => (d.id === id ? { ...d, score: value } : d));
const overallScore = Math.round(dimensions.reduce((sum, d) => sum + d.score, 0) / dimensions.length);
return { ...prev, dimensions, overallScore };
});
}, []);
const toggleTag = useCallback((id: string) => {
setScore((prev) => {
if (!prev) return prev;
const tags = prev.tags.map((t) => (t.id === id ? { ...t, selected: !t.selected } : t));
return { ...prev, tags };
});
}, []);
const updateComment = useCallback((comment: string) => {
setScore((prev) => (prev ? { ...prev, comment } : prev));
}, []);
const selectHireSuggestion = useCallback((type: HireSuggestion['type']) => {
setScore((prev) => {
if (!prev) return prev;
const hireSuggestions = prev.hireSuggestions.map((s) => ({ ...s, selected: s.type === type }));
return { ...prev, hireSuggestions };
});
}, []);
const submit = useCallback(async () => {
if (!score) return null;
setSubmitting(true);
try {
return await submitInterviewScore(interviewId, score);
} finally {
setSubmitting(false);
}
}, [interviewId, score]);
return {
score,
loading,
error,
refresh,
updateDimensionScore,
toggleTag,
updateComment,
selectHireSuggestion,
submit,
submitting,
};
}

19
src/hooks/useJobEvaluationEmptyState.ts

@ -0,0 +1,19 @@
/**
* 岗位评价管理空状态数据 hook。
*/
import { useCallback } from 'react';
import { fetchJobEvaluationEmptyState } from '../api';
import type { JobEvaluationEmptyState } from '../types';
import { useAsyncData } from './useAsyncData';
export function useJobEvaluationEmptyState() {
const fetcher = useCallback(async (): Promise<JobEvaluationEmptyState> => fetchJobEvaluationEmptyState(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
state: data ?? null,
loading,
error,
refresh,
};
}

32
src/hooks/useJobEvaluations.ts

@ -0,0 +1,32 @@
/**
* 岗位评价管理 hook:拉取评价管理数据 + 回复评价。
*/
import { useCallback, useState } from 'react';
import { fetchJobEvaluationManagement, replyJobReview } from '../api';
import type { JobEvaluationManagement } from '../types';
import { useAsyncData } from './useAsyncData';
export function useJobEvaluations() {
const fetcher = useCallback(() => fetchJobEvaluationManagement(), []);
const { data, loading, error, refresh } = useAsyncData<JobEvaluationManagement>(fetcher);
const [replyingId, setReplyingId] = useState<string | null>(null);
const reply = useCallback(async (reviewId: string, content: string) => {
setReplyingId(reviewId);
try {
await replyJobReview(reviewId, content);
} finally {
setReplyingId(null);
}
}, []);
return {
management: data ?? null,
loading,
error,
refresh,
reply,
replyingId,
};
}

19
src/hooks/useJobManagement.ts

@ -0,0 +1,19 @@
/**
* 企业端岗位管理数据 hook。
*/
import { useCallback } from 'react';
import { fetchJobManagement } from '../api';
import type { JobManagement } from '../types';
import { useAsyncData } from './useAsyncData';
export function useJobManagement() {
const fetcher = useCallback(async (): Promise<JobManagement> => fetchJobManagement(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
management: data ?? null,
loading,
error,
refresh,
};
}

20
src/hooks/useJobProgress.ts

@ -0,0 +1,20 @@
/**
* 求职进度 hook:拉取求职进度时间轴,统一管理 loading / error / refresh。
*/
import { useCallback } from 'react';
import { fetchJobProgress } from '../api';
import type { JobProgress } from '../types';
import { useAsyncData } from './useAsyncData';
export function useJobProgress() {
const fetcher = useCallback(() => fetchJobProgress(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
progress: (data as JobProgress | null) ?? null,
loading,
error,
refresh,
};
}

96
src/hooks/useJobPublish.ts

@ -0,0 +1,96 @@
/**
* 发布岗位 hook:拉取表单、更新字段、预估质量、发布岗位。
*/
import { useCallback, useEffect, useState } from 'react';
import { estimateJobQuality, fetchJobPublishForm, publishJob } from '../api';
import type { JobPublishFormData, JobQualityEstimate } from '../types';
import { useAsyncData } from './useAsyncData';
export function useJobPublish() {
const fetcher = useCallback(() => fetchJobPublishForm(), []);
const { data, loading, error, refresh } = useAsyncData<JobPublishFormData>(fetcher);
const [form, setForm] = useState<JobPublishFormData | null>(null);
const [estimate, setEstimate] = useState<JobQualityEstimate | null>(null);
const [estimating, setEstimating] = useState(false);
const [publishing, setPublishing] = useState(false);
useEffect(() => {
if (data && !form) {
setForm(data);
}
}, [data, form]);
const updateField = useCallback(<K extends keyof JobPublishFormData>(
field: K,
value: JobPublishFormData[K],
) => {
setForm((prev) => (prev ? { ...prev, [field]: value } : prev));
}, []);
const updateStringField = useCallback((field: keyof JobPublishFormData, value: string) => {
setForm((prev) => (prev ? { ...prev, [field]: value } : prev));
}, []);
const updateNumberField = useCallback((field: keyof JobPublishFormData, value: string) => {
setForm((prev) => (prev ? { ...prev, [field]: Number(value) || 0 } : prev));
}, []);
const toggleBenefit = useCallback((benefit: string) => {
setForm((prev) => {
if (!prev) return prev;
const benefits = prev.benefits.includes(benefit)
? prev.benefits.filter((b) => b !== benefit)
: [...prev.benefits, benefit];
return { ...prev, benefits };
});
}, []);
const toggleSkill = useCallback((skill: string) => {
setForm((prev) => {
if (!prev) return prev;
const skills = prev.skills.includes(skill)
? prev.skills.filter((s) => s !== skill)
: [...prev.skills, skill];
return { ...prev, skills };
});
}, []);
const runEstimate = useCallback(async () => {
if (!form) return;
setEstimating(true);
try {
const result = await estimateJobQuality(form);
setEstimate(result);
} finally {
setEstimating(false);
}
}, [form]);
const publish = useCallback(async () => {
if (!form) return null;
setPublishing(true);
try {
return await publishJob(form);
} finally {
setPublishing(false);
}
}, [form]);
return {
form,
loading,
error,
refresh,
updateField,
updateStringField,
updateNumberField,
toggleBenefit,
toggleSkill,
estimate,
estimating,
runEstimate,
publishing,
publish,
};
}

111
src/hooks/useLegalData.ts

@ -0,0 +1,111 @@
/**
* 阿布阿诺 AI 法律服务助手 hook:聚合获取品牌、服务、FAQ、模板、会员套餐,
* 并提供法律咨询对话消息发送能力。
*/
import { useCallback } from 'react';
import {
fetchLegalBrand,
fetchLegalConversation,
fetchLegalFAQs,
fetchLegalPlans,
fetchLegalServices,
fetchLegalTemplates,
sendLegalMessage,
} from '../api';
import type {
LegalBrand,
LegalConversation,
LegalFAQ,
LegalPlan,
LegalService,
LegalTemplate,
} from '../types';
import { useAsyncData } from './useAsyncData';
export interface LegalDataState {
/** 阿布阿诺品牌信息 */
brand: LegalBrand | null;
/** 法律服务入口列表 */
services: LegalService[];
/** 常见问题 */
faqs: LegalFAQ[];
/** 文书模板 */
templates: LegalTemplate[];
/** 会员套餐 */
plans: LegalPlan[];
/** 当前咨询会话 */
conversation: LegalConversation | null;
/** 是否加载中 */
loading: boolean;
/** 错误对象 */
error: Error | null;
/** 刷新全部数据 */
refresh: () => void;
/** 发送咨询消息 */
sendMessage: (content: string) => Promise<void>;
}
interface LegalStaticData {
brand: LegalBrand;
services: LegalService[];
faqs: LegalFAQ[];
templates: LegalTemplate[];
plans: LegalPlan[];
}
export function useLegalData(): LegalDataState {
const staticFetcher = useCallback(async (): Promise<LegalStaticData> => {
const [brand, services, faqs, templates, plans] = await Promise.all([
fetchLegalBrand(),
fetchLegalServices(),
fetchLegalFAQs(),
fetchLegalTemplates(),
fetchLegalPlans(),
]);
return { brand, services, faqs, templates, plans };
}, []);
const conversationFetcher = useCallback(() => fetchLegalConversation(), []);
const {
data: staticData,
loading: staticLoading,
error: staticError,
refresh: refreshStatic,
} = useAsyncData(staticFetcher);
const {
data: conversationData,
loading: conversationLoading,
error: conversationError,
refresh: refreshConversation,
} = useAsyncData(conversationFetcher);
const refresh = useCallback(() => {
refreshStatic();
refreshConversation();
}, [refreshStatic, refreshConversation]);
const sendMessage = useCallback(
async (content: string) => {
await sendLegalMessage(content);
refreshConversation();
},
[refreshConversation],
);
const resolved = (staticData as LegalStaticData | null) ?? null;
return {
brand: resolved?.brand ?? null,
services: resolved?.services ?? [],
faqs: resolved?.faqs ?? [],
templates: resolved?.templates ?? [],
plans: resolved?.plans ?? [],
conversation: (conversationData as LegalConversation | null) ?? null,
loading: staticLoading || conversationLoading,
error: staticError ?? conversationError,
refresh,
sendMessage,
};
}

9
src/hooks/useNotificationSettings.ts

@ -0,0 +1,9 @@
/**
* 消息通知设置 hook:拉取通知开关列表并提供切换能力。
*/
import { fetchNotificationSettings, updateNotificationSetting } from '../api';
import { useToggleSettings, type ToggleSettingsState } from './useToggleSettings';
export function useNotificationSettings(): ToggleSettingsState {
return useToggleSettings(fetchNotificationSettings, updateNotificationSetting);
}

26
src/hooks/useOfferCompareData.ts

@ -0,0 +1,26 @@
/**
* Offer 对比数据 hook
*/
import { useCallback } from 'react';
import { fetchOfferCompareData } from '../api';
import type { OfferCompareData } from '../types';
import { useAsyncData } from './useAsyncData';
export function useOfferCompareData(offerIds?: string[]) {
const fetcher = useCallback(() => fetchOfferCompareData(offerIds), [offerIds?.join(',')]);
const { data, loading, error, refresh } = useAsyncData<OfferCompareData>(fetcher);
return {
data,
offers: data?.offers ?? [],
scores: data?.scores ?? [],
benefitGroups: data?.benefitGroups ?? [],
workLifeBalance: data?.workLifeBalance ?? [],
aiSuggestion: data?.aiSuggestion,
actions: data?.actions,
loading,
error,
refresh,
};
}

34
src/hooks/useOnboardingChecklist.ts

@ -0,0 +1,34 @@
/**
* 入职准备清单 hook:拉取清单 + 切换待办完成状态。
*/
import { useCallback, useState } from 'react';
import { fetchOnboardingChecklist, toggleChecklistItem } from '../api';
import type { OnboardingChecklist } from '../types';
import { useAsyncData } from './useAsyncData';
export function useOnboardingChecklist() {
const fetcher = useCallback(() => fetchOnboardingChecklist(), []);
const { data, loading, error, refresh } = useAsyncData<OnboardingChecklist>(fetcher);
const [togglingId, setTogglingId] = useState<string | null>(null);
const toggle = useCallback(
async (itemId: string, done: boolean) => {
setTogglingId(itemId);
try {
return await toggleChecklistItem(itemId, done);
} finally {
setTogglingId(null);
}
},
[],
);
return {
checklist: data ?? null,
loading,
error,
refresh,
toggle,
togglingId,
};
}

33
src/hooks/useOperationRules.ts

@ -0,0 +1,33 @@
/**
* 运营规则配置 hook
*/
import { useCallback, useState } from 'react';
import { fetchOperationRules, saveOperationRules } from '../api';
import type { OperationRules } from '../types';
import { useAsyncData } from './useAsyncData';
export function useOperationRules() {
const fetcher = useCallback(() => fetchOperationRules(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
const [saving, setSaving] = useState(false);
const updateRules = useCallback(async (updater: (prev: OperationRules) => OperationRules) => {
if (!data) return;
const next = updater(data);
setSaving(true);
try {
await saveOperationRules(next);
} finally {
setSaving(false);
}
}, [data]);
return {
rules: data,
loading,
error,
saving,
refresh,
updateRules,
};
}

40
src/hooks/usePersonalDataExport.ts

@ -0,0 +1,40 @@
/**
* 个人数据导出 hook。
*/
import { useCallback, useState } from 'react';
import { fetchPersonalDataExport, requestPersonalDataExport } from '../api';
import type { PersonalDataExport } from '../types';
import { useAsyncData } from './useAsyncData';
export interface PersonalDataExportState {
exportData: PersonalDataExport | null;
loading: boolean;
error: Error | null;
refresh: () => void;
submit: () => Promise<{ success: boolean; message: string }>;
submitting: boolean;
}
export function usePersonalDataExport(): PersonalDataExportState {
const fetcher = useCallback(async (): Promise<PersonalDataExport> => fetchPersonalDataExport(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
const [submitting, setSubmitting] = useState(false);
const submit = useCallback(async () => {
setSubmitting(true);
try {
return await requestPersonalDataExport();
} finally {
setSubmitting(false);
}
}, []);
return {
exportData: data ?? null,
loading,
error,
refresh,
submit,
submitting,
};
}

41
src/hooks/usePrivacyCenter.ts

@ -0,0 +1,41 @@
/**
* 隐私中心(截图版)hook。
*/
import { useCallback } from 'react';
import { fetchPrivacyCenter, updatePrivacyCenterSetting } from '../api';
import type { PrivacyCenter, UpdateTogglePayload } from '../types';
import { useAsyncData } from './useAsyncData';
export interface PrivacyCenterState {
privacyCenter: PrivacyCenter | null;
loading: boolean;
error: Error | null;
refresh: () => void;
toggle: (id: string, enabled: boolean) => Promise<void>;
}
export function usePrivacyCenter(): PrivacyCenterState {
const fetcher = useCallback(async (): Promise<PrivacyCenter> => fetchPrivacyCenter(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
const toggle = useCallback(
async (id: string, enabled: boolean) => {
const payload: UpdateTogglePayload = { id, enabled };
try {
await updatePrivacyCenterSetting(payload);
refresh();
} catch (err) {
console.error('[usePrivacyCenter] toggle error', err);
}
},
[refresh],
);
return {
privacyCenter: data ?? null,
loading,
error,
refresh,
toggle,
};
}

9
src/hooks/usePrivacySettings.ts

@ -0,0 +1,9 @@
/**
* 隐私设置 hook:拉取隐私开关列表并提供切换能力。
*/
import { fetchPrivacySettings, updatePrivacySetting } from '../api';
import { useToggleSettings, type ToggleSettingsState } from './useToggleSettings';
export function usePrivacySettings(): ToggleSettingsState {
return useToggleSettings(fetchPrivacySettings, updatePrivacySetting);
}

21
src/hooks/useProfileData.ts

@ -1,53 +1,68 @@
/**
* 个人中心数据 hook:并行组合用户信息、综合评分、维度评分、求职偏好、勋章五类数据,
* 个人中心数据 hook:并行组合用户信息、综合评分、维度评分、求职偏好、勋章、菜单六类数据,
* 统一管理 loading / error / refresh 三态。
*/
import { useCallback } from 'react';
import {
fetchBadges,
fetchDimensionScores,
fetchEnterpriseEntry,
fetchJobPreference,
fetchMenuItems,
fetchOverallScore,
fetchUserProfile,
fetchUserStats,
} from '../api';
import type {
Badge,
DimensionScore,
EnterpriseEntry,
JobPreference,
MenuItem,
OverallScore,
UserProfile,
UserStats,
} from '../types';
import { useAsyncData } from './useAsyncData';
/** 个人中心聚合数据 */
interface ProfileData {
profile: UserProfile;
userStats: UserStats;
overallScore: OverallScore;
dimensions: DimensionScore[];
preference: JobPreference;
badges: Badge[];
menuItems: MenuItem[];
enterpriseEntry: EnterpriseEntry;
}
export function useProfileData() {
const fetcher = useCallback(async (): Promise<ProfileData> => {
const [profile, overallScore, dimensions, preference, badges] = await Promise.all([
const [profile, userStats, overallScore, dimensions, preference, badges, menuItems, enterpriseEntry] = await Promise.all([
fetchUserProfile(),
fetchUserStats(),
fetchOverallScore(),
fetchDimensionScores(),
fetchJobPreference(),
fetchBadges(),
fetchMenuItems(),
fetchEnterpriseEntry(),
]);
return { profile, overallScore, dimensions, preference, badges };
return { profile, userStats, overallScore, dimensions, preference, badges, menuItems, enterpriseEntry };
}, []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
profile: data?.profile ?? null,
userStats: data?.userStats ?? null,
overallScore: data?.overallScore ?? null,
dimensions: data?.dimensions ?? [],
preference: data?.preference ?? null,
badges: data?.badges ?? [],
menuItems: data?.menuItems ?? [],
enterpriseEntry: data?.enterpriseEntry ?? null,
loading,
error,
refresh,

75
src/hooks/useRankData.ts

@ -0,0 +1,75 @@
/**
* 同岗位竞争力排名数据 hook:加载综合排名 + 六维度百分位 + TOP10 候选人 + 提升建议。
* 支持数据态与空状态。
*
* 数据链路:页面 → useRankData → API 层
*/
import { useCallback, useEffect, useState } from 'react';
import { fetchRankData } from '../api';
import type {
DimensionPercentile,
ImprovementTip,
RankData,
RankEmptyState,
TopCandidate,
} from '../types';
export function useRankData(jobId: string) {
const [rankData, setRankData] = useState<RankData | null>(null);
const [dimensions, setDimensions] = useState<DimensionPercentile[]>([]);
const [candidates, setCandidates] = useState<TopCandidate[]>([]);
const [tips, setTips] = useState<ImprovementTip[]>([]);
const [emptyState, setEmptyState] = useState<RankEmptyState | null>(null);
const [hasData, setHasData] = useState(false);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const fetchData = useCallback(async () => {
if (!jobId) {
return;
}
setLoading(true);
setError(null);
try {
const data = await fetchRankData(jobId);
setHasData(data.hasData);
if (data.hasData) {
setRankData(data.rankData ?? null);
setDimensions(data.dimensions ?? []);
setCandidates(data.candidates ?? []);
setTips(data.tips ?? []);
setEmptyState(null);
} else {
setRankData(null);
setDimensions([]);
setCandidates([]);
setTips([]);
setEmptyState(data.emptyState ?? null);
}
} catch (err) {
setError(err instanceof Error ? err : new Error('加载失败'));
} finally {
setLoading(false);
}
}, [jobId]);
const refresh = useCallback(() => {
void fetchData();
}, [fetchData]);
useEffect(() => {
void fetchData();
}, [fetchData]);
return {
rankData,
dimensions,
candidates,
tips,
emptyState,
hasData,
loading,
error,
refresh,
};
}

25
src/hooks/useResume.ts

@ -0,0 +1,25 @@
/**
* 简历数据 hook:拉取简历 + AI 一键优化。
*/
import { useCallback } from 'react';
import { fetchResume, optimizeResume } from '../api';
import type { Resume } from '../types';
import { useAsyncData } from './useAsyncData';
export function useResume() {
const fetcher = useCallback(() => fetchResume(), []);
const { data, loading, error, refresh } = useAsyncData<Resume>(fetcher);
const optimize = useCallback(async () => {
const result = await optimizeResume();
return result.resume;
}, []);
return {
resume: data ?? null,
loading,
error,
refresh,
optimize,
};
}

48
src/hooks/useResumeExport.ts

@ -0,0 +1,48 @@
/**
* 简历导出 hook:拉取/保存导出设置,执行导出。
*/
import { useCallback, useState } from 'react';
import {
exportResume,
fetchResumeExportSettings,
saveResumeExportSettings,
} from '../api';
import type { ResumeExportSettings } from '../types';
import { useAsyncData } from './useAsyncData';
export function useResumeExport() {
const fetcher = useCallback(() => fetchResumeExportSettings(), []);
const { data, loading, error, refresh } = useAsyncData<ResumeExportSettings>(fetcher);
const [exporting, setExporting] = useState(false);
const save = useCallback(
async (settings: ResumeExportSettings) => {
await saveResumeExportSettings(settings);
await refresh();
},
[refresh],
);
const exportFile = useCallback(
async (settings: ResumeExportSettings) => {
setExporting(true);
try {
const result = await exportResume(settings);
return result.url;
} finally {
setExporting(false);
}
},
[],
);
return {
settings: data ?? null,
loading,
error,
refresh,
save,
exportFile,
exporting,
};
}

21
src/hooks/useSalaryBrackets.ts

@ -0,0 +1,21 @@
/**
* 行业薪资参考 hook:拉取各行业平均月薪,用于薪资计算器对比。
* 统一管理 loading / error / refresh。
*/
import { useCallback } from 'react';
import { fetchSalaryBrackets } from '../api';
import type { SalaryBracket } from '../types';
import { useAsyncData } from './useAsyncData';
export function useSalaryBrackets() {
const fetcher = useCallback(() => fetchSalaryBrackets(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
brackets: (data as SalaryBracket[] | null) ?? [],
loading,
error,
refresh,
};
}

24
src/hooks/useSalaryCalculator.ts

@ -0,0 +1,24 @@
/**
* 薪资计算器 hook:本地维护输入(月薪 / 城市 / 社保基数 / 公积金比例 / 专项扣除 / 年终奖),
* 通过接口层纯函数 computeSalaryResult 实时派生结果(业务规则在 api 层)。
* 计算为同步派生,避免 mock 延迟影响实时输入体验。
*/
import { useMemo, useState } from 'react';
import { computeSalaryResult } from '../api';
import type { SalaryCalcInput, SalaryCalcResult } from '../types';
export function useSalaryCalculator(initial?: Partial<SalaryCalcInput>) {
const [inputs, setInputs] = useState<SalaryCalcInput>({
monthlyGross: 30000,
city: '北京',
socialBase: 25000,
providentFundRate: 0.12,
specialDeduction: 2000,
bonusMonths: 0,
...initial,
});
const result: SalaryCalcResult = useMemo(() => computeSalaryResult(inputs), [inputs]);
return { inputs, setInputs, result };
}

24
src/hooks/useSkillCloud.ts

@ -0,0 +1,24 @@
/**
* 行业技能词云 hook:按行业拉取技能词数据,统一管理 loading / error / refresh。
* 切换行业时自动重新取数(fetcher 依赖 industry)。
*/
import { useCallback, useState } from 'react';
import { fetchSkillCloud } from '../api';
import type { SkillCloudData } from '../types';
import { useAsyncData } from './useAsyncData';
export function useSkillCloud(initialIndustry = '互联网/IT') {
const [industry, setIndustry] = useState(initialIndustry);
const fetcher = useCallback(() => fetchSkillCloud(industry), [industry]);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
industry,
setIndustry,
cloud: (data as SkillCloudData | null) ?? null,
loading,
error,
refresh,
};
}

19
src/hooks/useTalentRecommendation.ts

@ -0,0 +1,19 @@
/**
* 企业端人才推荐数据 hook。
*/
import { useCallback } from 'react';
import { fetchTalentRecommendation } from '../api';
import type { TalentRecommendation } from '../types';
import { useAsyncData } from './useAsyncData';
export function useTalentRecommendation() {
const fetcher = useCallback(async (): Promise<TalentRecommendation> => fetchTalentRecommendation(), []);
const { data, loading, error, refresh } = useAsyncData(fetcher);
return {
recommendation: data ?? null,
loading,
error,
refresh,
};
}

54
src/hooks/useToggleSettings.ts

@ -0,0 +1,54 @@
/**
* 通用开关型设置 hook:拉取开关列表并提供切换能力。
* 供 usePrivacySettings、useNotificationSettings 复用,避免重复实现三态与提交逻辑。
*/
import { useCallback, useState } from 'react';
import type { ToggleSetting, UpdateTogglePayload } from '../types';
import { useAsyncData } from './useAsyncData';
export interface ToggleSettingsState {
/** 开关列表 */
settings: ToggleSetting[];
/** 是否加载中 */
loading: boolean;
/** 错误对象 */
error: Error | null;
/** 刷新数据 */
refresh: () => void;
/** 切换某个开关 */
toggle: (id: string, enabled: boolean) => Promise<void>;
/** 正在更新的开关 id(用于禁用交互,避免连点) */
updatingId: string | null;
}
export function useToggleSettings(
fetchFn: () => Promise<ToggleSetting[]>,
updateFn: (payload: UpdateTogglePayload) => Promise<ToggleSetting[]>,
): ToggleSettingsState {
const [updatingId, setUpdatingId] = useState<string | null>(null);
const fetcher = useCallback(() => fetchFn(), [fetchFn]);
const { data, loading, error, refresh } = useAsyncData(fetcher);
const toggle = useCallback(
async (id: string, enabled: boolean) => {
setUpdatingId(id);
try {
await updateFn({ id, enabled });
refresh();
} finally {
setUpdatingId(null);
}
},
[updateFn, refresh],
);
return {
settings: (data as ToggleSetting[] | null) ?? [],
loading,
error,
refresh,
toggle,
updatingId,
};
}

91
src/hooks/useWeightSettings.ts

@ -0,0 +1,91 @@
/**
* 评分权重设置 hook:拉取维度与预设模板,提供权重调整、模板应用与保存能力。
* 业务规则(权重总和校验)放在 api 层,本层只负责本地编辑态与远端数据的合并。
*/
import { useCallback, useMemo, useState } from 'react';
import { fetchWeightSettings, saveWeightSettings } from '../api';
import type { WeightDimension, WeightSettings } from '../types';
import { useAsyncData } from './useAsyncData';
export interface WeightSettingsState {
/** 远端原始设置(含预设模板) */
settings: WeightSettings | null;
/** 当前生效的维度列表(本地编辑态优先,未编辑时回退远端) */
dimensions: WeightDimension[];
/** 各维度权重总和,正常应为 100 */
totalWeight: number;
loading: boolean;
error: Error | null;
saving: boolean;
refresh: () => void;
/** 调整单个维度权重 */
updateWeight: (id: string, weight: number) => void;
/** 应用预设模板(按维度名称匹配权重) */
applyTemplate: (templateId: string) => void;
/** 保存当前维度列表 */
save: () => Promise<void>;
}
export function useWeightSettings(): WeightSettingsState {
const fetcher = useCallback(() => fetchWeightSettings(), []);
const { data, loading, error, refresh } = useAsyncData<WeightSettings>(fetcher);
const [localDimensions, setLocalDimensions] = useState<WeightDimension[] | null>(null);
const [saving, setSaving] = useState(false);
const remoteDimensions = data?.dimensions ?? [];
const dimensions = localDimensions ?? remoteDimensions;
const totalWeight = useMemo(
() => dimensions.reduce((sum, item) => sum + item.weight, 0),
[dimensions],
);
const updateWeight = useCallback(
(id: string, weight: number) => {
setLocalDimensions((prev) => {
const base = prev ?? remoteDimensions;
return base.map((item) => (item.id === id ? { ...item, weight } : item));
});
},
[remoteDimensions],
);
const applyTemplate = useCallback(
(templateId: string) => {
const template = data?.templates.find((item) => item.id === templateId);
if (!template) return;
setLocalDimensions((prev) => {
const base = prev ?? remoteDimensions;
return base.map((item) => ({
...item,
weight: template.weights[item.name] ?? item.weight,
}));
});
},
[data, remoteDimensions],
);
const save = useCallback(async () => {
setSaving(true);
try {
const saved = await saveWeightSettings(dimensions);
setLocalDimensions(saved);
} finally {
setSaving(false);
}
}, [dimensions]);
return {
settings: data ?? null,
dimensions,
totalWeight,
loading,
error,
saving,
refresh,
updateWeight,
applyTemplate,
save,
};
}

41
src/types/compare.ts

@ -0,0 +1,41 @@
/**
* 岗位对比相关类型定义
*/
/** 岗位对比 - 单个岗位 */
export interface CompareJob {
id: string;
/** 岗位名称,如 "高级前端工程师" */
title: string;
/** 公司名,如 "字节跳动" */
company: string;
/** 薪资范围,如 "25-40K" */
salary: string;
/** 雷达图系列颜色(从 theme 导入) */
color: string;
}
/** 岗位对比 - 单个维度评分(一行:维度名 + 各岗位评分) */
export interface CompareDimensionScore {
/** 维度名称,如 "综合评分" */
dimension: string;
/** 各岗位评分,[92, 85, 78] 与 jobs 顺序一一对应 */
scores: number[];
}
/** 岗位对比 - 完整数据 */
export interface CompareData {
/** 参与对比的岗位列表(2-3 个) */
jobs: CompareJob[];
/** 九大维度评分 */
dimensionScores: CompareDimensionScore[];
/** 基本信息(字段名 + 各岗位对应值) */
basicInfo: {
/** 字段名,如 ["公司规模", "所在行业", ...] */
fields: string[];
/** 各字段对应各岗位的值,如 [["10000人以上", ...], ...] */
values: string[][];
};
/** 各岗位福利标签,如 [["五险一金", ...], ...] */
benefits: string[][];
}

101
src/types/data-export.ts

@ -0,0 +1,101 @@
/**
* 个人数据导出领域模型
*
* 包含:
* - 可导出数据分类与勾选项
* - 导出格式(JSON / Excel)
* - 时间范围
* - 导出预览(大小 / 数量 / 处理时间)
* - 最近导出记录
*/
/** 单个可导出数据项 */
export interface DataExportItem {
/** 项 id */
id: string;
/** 展示文案 */
label: string;
/** 预估大小 */
size: string;
/** 是否默认选中 */
checked: boolean;
}
/** 数据分类(如基本信息 / 简历数据 / 求职记录) */
export interface DataExportCategory {
/** 分类 id */
id: string;
/** 分类名称 */
label: string;
/** 分类描述 */
description: string;
/** 分类总大小 */
size: string;
/** 分类下数据项 */
items: DataExportItem[];
}
/** 导出格式选项 */
export interface DataExportFormat {
/** 格式 id */
id: string;
/** 展示名称 */
label: string;
/** 图标名(Ionicons) */
icon: string;
/** 格式说明 */
description: string;
}
/** 导出时间范围选项 */
export interface DataExportTimeRange {
/** 选项 id */
id: string;
/** 展示文案 */
label: string;
}
/** 最近导出记录 */
export interface RecentExportRecord {
/** 记录 id */
id: string;
/** 导出文件名 */
name: string;
/** 导出日期 */
date: string;
/** 文件大小 */
size: string;
/** 状态:已完成 / 已过期 */
status: 'completed' | 'expired';
}
/** 个人数据导出聚合数据 */
export interface PersonalDataExport {
/** 页面标题 */
title: string;
/** 功能说明 */
description: string;
/** 可导出数据分类 */
categories: DataExportCategory[];
/** 导出格式选项 */
formats: DataExportFormat[];
/** 时间范围选项 */
timeRanges: DataExportTimeRange[];
/** 当前选中的导出格式 id */
selectedFormat: string;
/** 当前选中的时间范围 id */
selectedTimeRange: string;
/** 导出预览 */
preview: {
/** 已选分类数 */
categoryCount: number;
/** 预估总大小 */
totalSize: string;
/** 预计处理时间 */
estimatedTime: string;
/** 有效期说明 */
validity: string;
};
/** 最近导出记录 */
recentExports: RecentExportRecord[];
}

637
src/types/enterprise.ts

@ -0,0 +1,637 @@
/**
* 企业端 / HR 端领域模型
*
* 包含:
* - 企业数据总览(公司主页)
* - 发布岗位
* - 岗位评价管理
* - 候选人详情
* - 面试评分
*/
/* ===================== 企业数据总览 ===================== */
/** 企业核心统计项 */
export interface EnterpriseStat {
id: string;
/** 统计名称 */
label: string;
/** 数值 */
value: number;
/** 较上期变化(可为负) */
change: number;
/** Ionicons 图标名 */
icon: string;
/** 颜色 token */
color: string;
}
/** 岗位评价摘要(企业总览展示) */
export interface JobEvaluationSummary {
id: string;
/** 岗位名称 */
positionName: string;
/** 部门 */
department: string;
/** 综合评分 */
score: number;
/** 评价数 */
reviewCount: number;
/** 趋势变化 */
change: number;
}
/** 企业数据总览 */
export interface EnterpriseOverview {
/** 企业 id */
id: string;
/** 企业名称 */
name: string;
/** 企业 Logo 字符 */
logo: string;
/** 所属行业 */
industry: string;
/** 企业规模 */
scale: string;
/** 企业阶段 */
stage: string;
/** 综合评分 */
overallScore: number;
/** 评分人数 */
reviewCount: number;
/** 统计项 */
stats: EnterpriseStat[];
/** 岗位评价摘要列表 */
jobEvaluations: JobEvaluationSummary[];
/** 快捷入口 */
quickActions: EnterpriseQuickAction[];
}
/** 企业总览快捷入口 */
export interface EnterpriseQuickAction {
id: string;
/** 名称 */
label: string;
/** 图标 */
icon: string;
/** 路由 */
route: string;
/** 颜色 token */
color: string;
}
/* ===================== 发布岗位 ===================== */
/** 工作类型 */
export type JobWorkType = 'full-time' | 'part-time' | 'intern' | 'remote';
/** 学历要求 */
export type JobEducationRequirement = '不限' | '大专' | '本科' | '硕士' | '博士';
/** 经验要求 */
export type JobExperienceRequirement =
| '应届生'
| '1年以内'
| '1-3年'
| '3-5年'
| '5-10年'
| '10年以上';
/** 发布岗位表单数据 */
export interface JobPublishFormData {
/** 岗位名称 */
positionName: string;
/** 所属部门 */
department: string;
/** 招聘人数 */
headcount: number;
/** 工作城市 */
city: string;
/** 详细地址 */
address: string;
/** 工作类型 */
workType: JobWorkType;
/** 学历要求 */
education: JobEducationRequirement;
/** 经验要求 */
experience: JobExperienceRequirement;
/** 最低薪资(K) */
salaryMin: number;
/** 最高薪资(K) */
salaryMax: number;
/** 薪资月数 */
salaryMonths: number;
/** 福利标签 */
benefits: string[];
/** 岗位职责 */
responsibilities: string;
/** 岗位要求 */
requirements: string;
/** 技能要求 */
skills: string[];
/** 岗位可见范围 */
visibility: 'all' | 'internal' | 'campus';
/** 是否急招 */
urgent: boolean;
/** 是否接受应届生 */
acceptFresh: boolean;
/** 截止日期 */
deadline: string;
}
/** 岗位质量预估结果 */
export interface JobQualityEstimate {
/** 预估分数 0-100 */
score: number;
/** 等级 */
level: string;
/** 优化建议 */
suggestions: string[];
}
/* ===================== 岗位评价管理 ===================== */
/** 岗位评价维度 */
export interface JobEvaluationDimension {
id: string;
/** 维度名称 */
name: string;
/** 得分 */
score: number;
/** 满分 */
maxScore: number;
/** 较上期变化 */
change: number;
}
/** 单条岗位评价 */
export interface JobReview {
id: string;
/** 评价者(脱敏) */
userName: string;
/** 身份标签 */
userRole: string;
/** 岗位名 */
positionName: string;
/** 评分 */
rating: number;
/** 评价日期 */
date: string;
/** 评价内容 */
content: string;
/** 标签 */
tags: string[];
/** 是否已回复 */
replied: boolean;
}
/** 岗位评价管理聚合数据 */
export interface JobEvaluationManagement {
/** 企业综合评分 */
overallScore: number;
/** 较上期变化 */
change: number;
/** 评价总数 */
reviewCount: number;
/** 好评率 0-100 */
positiveRate: number;
/** 回复率 0-100 */
replyRate: number;
/** 投诉率 0-100 */
complaintRate: number;
/** 评分趋势(月份 -> 分数) */
trend: { month: string; score: number }[];
/** 六维评分 */
dimensions: JobEvaluationDimension[];
/** 各岗位评分 */
jobScores: JobEvaluationSummary[];
/** 评价列表 */
reviews: JobReview[];
}
/* ===================== 候选人详情 ===================== */
/** 候选人基本信息 */
export interface CandidateBasicInfo {
/** 姓名 */
name: string;
/** 年龄 */
age: number;
/** 性别 */
gender: string;
/** 工作年限 */
experience: string;
/** 学历 */
education: string;
/** 所在城市 */
city: string;
/** 手机号(脱敏) */
phone: string;
/** 邮箱(脱敏) */
email: string;
/** 求职状态 */
jobStatus: string;
}
/** 候选人能力维度 */
export interface CandidateAbility {
id: string;
/** 能力名称 */
name: string;
/** 得分 */
score: number;
/** 满分 */
maxScore: number;
}
/** 候选人评价 */
export interface CandidateReview {
id: string;
/** 面试官 */
interviewer: string;
/** 岗位 */
position: string;
/** 日期 */
date: string;
/** 评分 */
rating: number;
/** 内容 */
content: string;
/** 标签 */
tags: string[];
}
/** 录用建议 */
export interface HireSuggestion {
/** 建议类型:strong / consider / reject */
type: 'strong' | 'consider' | 'reject';
/** 建议标题 */
title: string;
/** 建议描述 */
desc: string;
/** 是否已选中 */
selected?: boolean;
}
/** 薪资建议 */
export interface SalarySuggestion {
/** 建议薪资范围 */
range: string;
/** 职级 */
level: string;
/** 说明 */
note: string;
}
/** 候选人详情 */
export interface CandidateDetail {
/** 候选人 id */
id: string;
/** 基本信息 */
basicInfo: CandidateBasicInfo;
/** 匹配度 0-100 */
matchPercent: number;
/** 综合评分 */
overallScore: number;
/** 超过同岗位百分比 */
percentile: number;
/** 评分趋势 */
trend: { month: string; score: number }[];
/** 八维能力评分 */
abilities: CandidateAbility[];
/** 面试评价 */
reviews: CandidateReview[];
/** 录用建议 */
hireSuggestions: HireSuggestion[];
/** 薪资建议 */
salarySuggestion: SalarySuggestion;
/** 是否已发送面试邀请 */
invited: boolean;
}
/* ===================== 面试评分 ===================== */
/** 面试评分维度 */
export interface InterviewScoreDimension {
id: string;
/** 维度名称 */
name: string;
/** 得分 */
score: number;
/** 满分 */
maxScore: number;
}
/** 面试评价标签 */
export interface InterviewScoreTag {
id: string;
/** 标签名 */
label: string;
/** 是否已选中 */
selected: boolean;
/** 颜色 token */
color: string;
}
/** 面试评分聚合数据 */
export interface InterviewScoreData {
/** 候选人 id */
candidateId: string;
/** 候选人姓名 */
candidateName: string;
/** 面试岗位 */
positionName: string;
/** 面试时间 */
interviewTime: string;
/** 综合评分 */
overallScore: number;
/** 维度评分 */
dimensions: InterviewScoreDimension[];
/** 评价标签 */
tags: InterviewScoreTag[];
/** 文字评价 */
comment: string;
/** 录用建议选项 */
hireSuggestions: HireSuggestion[];
/** 薪资建议 */
salarySuggestion: SalarySuggestion;
}
/* ===================== 岗位管理 ===================== */
/** 岗位管理 Tab 项 */
export interface JobManagementTab {
/** tab key */
key: string;
/** 展示文案 */
label: string;
/** 数量 */
count: number;
}
/** 岗位管理顶部数据概览项 */
export interface JobManagementStat {
/** 统计 id */
id: string;
/** 名称 */
label: string;
/** 数值 */
value: number;
/** 较上期变化文案(如 '+2' / '-5' / '+23%') */
change: string;
/** Ionicons 图标名 */
icon: string;
/** 颜色 token */
color: string;
}
/** 岗位状态 */
export type JobManagementJobStatus = 'active' | 'offline' | 'draft';
/** 岗位管理列表项 */
export interface JobManagementJob {
/** 岗位 id */
id: string;
/** 岗位名称 */
title: string;
/** 薪资范围文案(如 '25-35K') */
salary: string;
/** 薪资月数 */
salaryMonths: number;
/** 工作城市 */
city: string;
/** 经验要求 */
experience: string;
/** 学历要求 */
education: string;
/** 岗位标签 */
tags: string[];
/** 岗位状态 */
status: JobManagementJobStatus;
/** 所属部门 */
department: string;
/** 招聘人数 */
headcount: number;
/** 综合评分 */
overallScore: number;
/** 同岗排名文案 */
rankText: string;
/** 投递数 */
applicants: number;
/** 查看数 */
views: number;
/** 匹配数 */
matches: number;
}
/** 岗位管理聚合数据 */
export interface JobManagement {
/** 企业名称 */
companyName: string;
/** 是否有数据(true=展示数据态,false=展示空态) */
hasData: boolean;
/** 数据概览 */
stats: JobManagementStat[];
/** tab 列表 */
tabs: JobManagementTab[];
/** 岗位列表 */
jobs: JobManagementJob[];
/** 发布岗位后可享受的权益(空态用) */
benefits: string[];
}
/* ===================== 人才推荐 ===================== */
/** 人才推荐 Tab 项 */
export interface TalentRecommendationTab {
key: string;
label: string;
count: number;
}
/** 数据参考项 */
export interface DataReferenceItem {
label: string;
value: string;
}
/** 人才池统计项 */
export interface TalentPoolStat {
/** 统计 id */
id: string;
/** 数值 */
value: string;
/** 名称 */
label: string;
}
/** 人才推荐候选人 */
export interface TalentCandidate {
/** 候选人 id */
id: string;
/** 姓名 */
name: string;
/** 头像字符 */
avatar: string;
/** 当前职位 */
title: string;
/** 工作经验 */
experience: string;
/** 年龄 */
age: number;
/** 学校 */
school: string;
/** 城市 */
city: string;
/** 期望薪资 */
expectedSalary: string;
/** 匹配度 0-100 */
matchPercent: number;
/** 综合评分 */
overallScore: number;
/** 超越同岗百分比 */
percentile: number;
/** 技能 / 优势标签 */
tags: string[];
}
/** 人才推荐聚合数据 */
export interface TalentRecommendation {
companyName: string;
/** 是否有数据 */
hasData: boolean;
/** 今日推荐人数 */
todayCount: number;
tabs: TalentRecommendationTab[];
/** 推荐候选人列表 */
candidates: TalentCandidate[];
/** 人才池统计 */
poolStats: TalentPoolStat[];
/** 推荐操作 */
recommendedActions: string[];
/** 数据参考 */
dataReference: DataReferenceItem[];
}
/* ===================== 企业沟通 ===================== */
/** 企业沟通 Tab 项 */
export interface EnterpriseCommunicationTab {
key: string;
label: string;
count: number;
}
/** 待处理任务项 */
export interface PendingTask {
/** 任务 id */
id: string;
/** 任务标题 */
label: string;
/** 数量 */
count: number;
/** 提示说明 */
hint?: string;
/** Ionicons 图标名 */
icon: string;
/** 颜色 token */
color: string;
}
/** 企业沟通会话项 */
export interface EnterpriseChatItem {
/** 会话 id */
id: string;
/** 用户 id */
userId: string;
/** 姓名 */
name: string;
/** 头像字符 */
avatar: string;
/** 当前职位 */
title: string;
/** 最后一条消息 */
lastMessage: string;
/** 时间 */
time: string;
/** 未读数 */
unreadCount: number;
/** 标签 */
labels: string[];
}
/** 企业沟通聚合数据 */
export interface EnterpriseCommunication {
companyName: string;
/** 是否有数据 */
hasData: boolean;
/** 待处理任务 */
pendingTasks: PendingTask[];
tabs: EnterpriseCommunicationTab[];
/** 会话列表 */
chats: EnterpriseChatItem[];
/** 数据参考 */
dataReference: DataReferenceItem[];
}
/* ===================== 岗位评价管理空状态 ===================== */
/** 岗位评价管理 Tab 项 */
export interface JobEvaluationEmptyTab {
key: string;
label: string;
count: number;
}
/** 岗位评价管理空状态 */
export interface JobEvaluationEmptyState {
/** 是否有评价数据 */
hasData: boolean;
/** tab 列表 */
tabs: JobEvaluationEmptyTab[];
/** 空状态标题 */
emptyTitle: string;
/** 空状态副标题 */
emptySubtitle: string;
/** 如何获得更多好评 */
tips: string[];
}
/* ===================== 企业认证 ===================== */
/** 企业认证状态 */
export type EnterpriseCertificationStatus = 'unverified' | 'pending' | 'verified' | 'rejected';
/** 认证权益项 */
export interface EnterpriseCertificationBenefit {
/** 权益 id */
id: string;
/** 权益文案 */
text: string;
}
/** 企业认证表单数据 */
export interface EnterpriseCertificationForm {
/** 企业名称 */
companyName: string;
/** 统一社会信用代码 */
creditCode: string;
/** 营业执照图片 URL */
licenseUrl?: string;
/** 联系人姓名 */
contactName: string;
/** 联系人手机号 */
contactPhone: string;
}
/** 企业认证聚合数据 */
export interface EnterpriseCertificationData {
/** 当前认证状态 */
status: EnterpriseCertificationStatus;
/** 状态说明 */
statusText: string;
/** 认证权益列表 */
benefits: EnterpriseCertificationBenefit[];
/** 表单数据 */
form: EnterpriseCertificationForm;
}

92
src/types/evaluation.ts

@ -0,0 +1,92 @@
/**
* HR 评价 / 评价申诉相关类型定义
*/
/** 评价维度 */
export interface EvaluationDimension {
id: string;
/** 维度名称 */
name: string;
/** 得分 */
score: number;
/** 满分 */
maxScore: number;
/** 评级 */
level: string;
/** 相比上次变化 */
change?: number;
}
/** 评分趋势点 */
export interface EvaluationTrendPoint {
/** 月份 */
month: string;
/** 综合评分 */
score: number;
}
/** 单条 HR 评价 */
export interface EvaluationReview {
id: string;
/** 评价公司 */
company: string;
/** 评分 */
rating: number;
/** 评价日期 */
date: string;
/** 评价内容 */
content: string;
/** 标签 */
tags: string[];
/** 状态 */
status: 'accepted' | 'appealed';
}
/** HR 评价详情 */
export interface HREvaluation {
/** 综合评分 */
overallScore: number;
/** 超过同岗位百分比 */
percentile: number;
/** 评分趋势 */
trend: EvaluationTrendPoint[];
/** 六维能力评分 */
dimensions: EvaluationDimension[];
/** 评价列表 */
reviews: EvaluationReview[];
/** 评分规则说明 */
rules: string[];
}
/** 申诉类型 */
export type AppealType = 'overall' | 'single';
/** 申诉原因 */
export type AppealReason = 'malicious' | 'untrue' | 'privacy' | 'other';
/** 申诉维度 */
export interface AppealDimension {
id: string;
name: string;
selected: boolean;
}
/** 评价申诉表单 */
export interface EvaluationAppealForm {
/** 申诉类型 */
type: AppealType;
/** 申诉维度(单条申诉时选择) */
dimensions: AppealDimension[];
/** 申诉原因 */
reason: AppealReason;
/** 原因描述 */
reasonText: string;
/** 证据图片 URL */
evidence: string[];
/** 联系电话 */
contactPhone: string;
/** 联系邮箱 */
contactEmail: string;
/** 是否同意申诉须知 */
agree: boolean;
}

114
src/types/game.ts

@ -0,0 +1,114 @@
/**
* 通关小游戏领域模型。
* 针对求职者角色,通过 7 关小游戏帮助其认识自身、能力、岗位、企业、简历、面试、签约。
*/
/** 关卡类型:对应求职准备的 7 个环节 */
export type GameLevelType =
| 'self'
| 'ability'
| 'job'
| 'company'
| 'resume'
| 'interview'
| 'offer';
/** 关卡状态 */
export type GameLevelStatus = 'locked' | 'available' | 'completed';
/** 单道题目类型 */
export type GameQuestionType = 'single' | 'multiple' | 'boolean' | 'input';
/** 单道题目选项 */
export interface GameOption {
/** 选项 id */
id: string;
/** 选项文案 */
text: string;
/** 是否正确/推荐选项 */
isCorrect: boolean;
}
/** 单个关卡题目 */
export interface GameQuestion {
/** 题目 id */
id: string;
/** 题干 */
title: string;
/** 题目类型 */
type: GameQuestionType;
/** 选项(单选/多选/判断必有) */
options?: GameOption[];
/** 输入题默认值提示 */
placeholder?: string;
/** 题目解析(答完后展示) */
explanation: string;
/** 本题对应的能力标签(用于后续岗位推荐) */
tags: string[];
}
/** 单个关卡 */
export interface GameLevel {
/** 关卡 id */
id: GameLevelType;
/** 关卡序号 1-7 */
order: number;
/** 关卡标题 */
title: string;
/** 关卡副标题 */
subtitle: string;
/** 图标名(Ionicons) */
icon: string;
/** 关卡描述 */
description: string;
/** 题目列表 */
questions: GameQuestion[];
/** 完成本关后解锁的下一关 id */
nextLevel?: GameLevelType;
}
/** 用户在某关的作答记录 */
export interface GameAnswer {
/** 题目 id */
questionId: string;
/** 用户选择的选项 id 列表(单选/判断为 1 个) */
selectedIds: string[];
/** 输入题文本答案 */
inputValue?: string;
/** 是否正确(接口层判定) */
isCorrect: boolean;
}
/** 用户在单关的完成结果 */
export interface GameLevelResult {
/** 关卡 id */
levelId: GameLevelType;
/** 是否完成 */
completed: boolean;
/** 得分(0-100) */
score: number;
/** 作答记录 */
answers: GameAnswer[];
/** 完成时间 ISO 字符串 */
completedAt?: string;
}
/** 通关小游戏总进度 */
export interface GameProgress {
/** 当前可用/进行中的关卡 id */
currentLevel: GameLevelType;
/** 各关完成结果 */
results: GameLevelResult[];
/** 已解锁关卡 id */
unlockedLevels: GameLevelType[];
/** 总进度百分比 0-100 */
totalPercent: number;
}
/** 提交关卡答案的请求体 */
export interface SubmitGameLevelPayload {
/** 关卡 id */
levelId: GameLevelType;
/** 作答记录 */
answers: GameAnswer[];
}

191
src/types/growth.ts

@ -3,12 +3,191 @@
*/
import type { JobLevel } from './job';
/** 霍兰德六维类型 */
export type HollandType = 'R' | 'I' | 'A' | 'S' | 'E' | 'C';
/** 霍兰德维度得分 */
export interface HollandDimension {
/** 类型代码 */
type: HollandType;
/** 类型中文名,如 "现实型" */
name: string;
/** 原始得分 */
score: number;
/** 百分位 0-100 */
percent: number;
}
/** 霍兰德测评题目 */
export interface HollandQuestion {
id: string;
/** 题号(从 1 开始) */
order: number;
/** 题目描述 */
text: string;
}
/** 霍兰德选项 */
export interface HollandOption {
/** 分值 */
value: number;
/** 展示文案 */
label: string;
}
/** 霍兰德主导类型解析 */
export interface HollandTypeAnalysis {
/** 类型代码 */
type: HollandType;
/** 类型中文名 */
name: string;
/** 得分 */
score: number;
/** 类型特征描述 */
description: string;
/** 适合职业 */
careers: string[];
}
/** 霍兰德推荐岗位 */
export interface HollandJobRecommendation {
id: string;
/** 岗位名 */
title: string;
/** 公司名 */
companyName: string;
/** 薪资 */
salary: string;
/** 城市 */
city: string;
/** 标签 */
tags: string[];
/** 匹配度 0-100 */
match: number;
}
/** 霍兰德结果行动建议 */
export interface HollandAction {
id: string;
/** 标题 */
title: string;
/** 描述 */
desc: string;
/** Ionicons 图标名 */
icon: string;
/** 跳转路由 */
route?: string;
}
/** 霍兰德六维类型简介 */
export interface HollandDimensionIntro {
/** 类型代码 */
type: HollandType;
/** 类型中文名 */
name: string;
/** 一句话描述 */
desc: string;
}
/** 霍兰德职业兴趣测评介绍页 */
export interface HollandIntro {
/** 测评标题 */
title: string;
/** 测评副标题 */
subtitle: string;
/** 累计完成人数 */
totalParticipants: number;
/** 预计用时 */
duration: string;
/** 六维类型简介 */
dimensions: HollandDimensionIntro[];
/** 核心亮点 */
features: {
id: string;
icon: string;
title: string;
desc: string;
}[];
/** 测评优势 */
advantages: string[];
}
/** 霍兰德职业兴趣测评 */
export interface HollandAssessment {
/** 累计完成人数 */
totalParticipants: number;
/** 当前题目索引(从 0 开始) */
currentQuestionIndex: number;
/** 题目总数 */
totalQuestions: number;
/** 当前题目 */
question: HollandQuestion;
/** 选项 */
options: HollandOption[];
/** 当前六维分布(答题过程中实时更新) */
dimensions: HollandDimension[];
/** 测评结果(完成时返回) */
result?: {
/** 类型代码,如 "ISR" */
code: string;
/** 组合名称 */
name: string;
/** 六维得分 */
dimensions: HollandDimension[];
/** 主导类型解析 */
analyses: HollandTypeAnalysis[];
/** 推荐岗位 */
jobs: HollandJobRecommendation[];
/** 下一步行动 */
actions: HollandAction[];
};
}
/** 能力维度(四维能力测评的单条维度) */
export interface AbilityDimension {
/** 维度名称,如 "专业能力" */
label: string;
/** 维度得分(0-100) */
value: number;
/** 百分位描述(如 "超过 78% 同岗位求职者") */
percentileText?: string;
/** 该维度下子项得分明细 */
subItems?: AbilitySubItem[];
}
/** 四维能力测评子项 */
export interface AbilitySubItem {
id: string;
label: string;
score: number;
maxScore: number;
}
/** 能力成长趋势点 */
export interface AbilityTrendPoint {
/** 月份 */
month: string;
/** 专业能力 */
professional: number;
/** 项目经验 */
project: number;
/** 技能广度 */
skill: number;
/** 成长潜力 */
growth: number;
}
/** 能力提升建议 */
export interface AbilityImprovement {
id: string;
/** 建议标题 */
title: string;
/** 建议描述 */
description: string;
/** Ionicons 图标名 */
icon: string;
/** 跳转路由 */
route?: string;
}
/** 四维能力测评结果 */
@ -19,6 +198,12 @@ export interface AbilityAssessment {
level: JobLevel;
/** 4 个能力维度 */
dimensions: AbilityDimension[];
/** 超过同岗位求职者百分比 */
percentile?: number;
/** 能力成长趋势(最近 6 个月) */
trends: AbilityTrendPoint[];
/** 针对性提升建议 */
improvements: AbilityImprovement[];
}
/** AI 求职工具 */
@ -50,9 +235,3 @@ export interface Article {
/** Ionicons 图标名 */
icon: string;
}
/** 霍兰德职业兴趣测评 */
export interface HollandAssessment {
/** 累计完成人数 */
totalParticipants: number;
}

211
src/types/index.ts

@ -2,7 +2,6 @@
* 类型统一导出
*/
export type {
Category,
EmployeeReview,
Job,
JobDetail,
@ -10,7 +9,6 @@ export type {
JobLevel,
JobListParams,
JobOverallScore,
MatchData,
SalaryData,
SalaryRange,
SortType,
@ -18,12 +16,106 @@ export type {
export type {
AbilityAssessment,
AbilityDimension,
AbilityImprovement,
AbilitySubItem,
AbilityTrendPoint,
AITool,
Article,
HollandAction,
HollandAssessment,
HollandDimension,
HollandDimensionIntro,
HollandIntro,
HollandJobRecommendation,
HollandOption,
HollandQuestion,
HollandType,
HollandTypeAnalysis,
} from './growth';
export type { AssessmentTool, HomeAITool, HomeTool, HotJob } from './home';
export type { Conversation, Interview, MatchResult, Notification } from './message';
export type {
CandidateAbility,
CandidateBasicInfo,
CandidateDetail,
CandidateReview,
DataReferenceItem,
EnterpriseCertificationBenefit,
EnterpriseCertificationData,
EnterpriseCertificationForm,
EnterpriseCertificationStatus,
EnterpriseCommunication,
EnterpriseCommunicationTab,
EnterpriseChatItem,
EnterpriseOverview,
EnterpriseQuickAction,
EnterpriseStat,
HireSuggestion,
InterviewScoreData,
InterviewScoreDimension,
InterviewScoreTag,
JobEducationRequirement,
JobEvaluationDimension,
JobEvaluationEmptyState,
JobEvaluationEmptyTab,
JobEvaluationManagement,
JobEvaluationSummary,
JobExperienceRequirement,
JobManagement,
JobManagementJob,
JobManagementJobStatus,
JobManagementStat,
JobManagementTab,
JobPublishFormData,
JobQualityEstimate,
JobReview,
JobWorkType,
PendingTask,
SalarySuggestion,
TalentCandidate,
TalentPoolStat,
TalentRecommendation,
TalentRecommendationTab,
} from './enterprise';
export type {
AuditGrade,
AuditKPI,
AuditReport,
AuditTrendPoint,
AutoFilterRule,
MatchMode,
MatchModeOption,
OperationRules,
RiskDistributionItem,
} from './operation';
export type {
ChatCard,
ChatCardType,
ChatMessage,
ChatMessageRole,
Conversation,
ConversationDetail,
Interview,
MatchResult,
Notification,
} from './message';
export type { CompareData, CompareDimensionScore, CompareJob } from './compare';
export type {
OfferCompareData,
OfferItem,
OfferScore,
OfferBenefitGroup,
OfferBenefitRow,
WorkLifeBalanceRow,
OfferAISuggestion,
} from './offer-compare';
export type {
DimensionPercentile,
ImprovementTip,
RankData,
RankEmptyState,
RankOverview,
ResumeProgressStep,
TopCandidate,
} from './rank';
export type {
Badge,
DimensionScore,
@ -31,7 +123,116 @@ export type {
JobPreference,
MenuItem,
OverallScore,
UserInfo,
UserProfile,
UserStats,
} from './user';
export type { WeightDimension, WeightSettings, WeightTemplate } from './weight';
export type {
Resume,
ResumeBasicInfo,
ResumeEducation,
ResumeExperience,
ResumeExportFormat,
ResumeExportSettings,
ResumeJobIntention,
ResumeOptimizePoint,
ResumeOptimizeResult,
ResumeProject,
ResumeScore,
ResumeSelfEvaluation,
ResumeSkill,
ResumeTemplate,
} from './resume';
export type {
ChecklistCategory,
ChecklistGroup,
ChecklistItem,
IdentityOption,
JobProgress,
JobProgressCompany,
JobProgressMonthly,
JobProgressOffer,
JobProgressStage,
JobProgressStats,
OnboardingChecklist,
OnboardingTip,
ProgressStatus,
ProgressStep,
SalaryBracket,
SalaryCalcInput,
SalaryCalcResult,
SalaryInsuranceItem,
SalaryStructureItem,
SalaryTaxItem,
SalaryToolLink,
SkillCategory,
SkillCloudData,
SkillCloudFilter,
SkillWord,
TopSkill,
} from './tools';
export type {
GameAnswer,
GameLevel,
GameLevelResult,
GameLevelStatus,
GameLevelType,
GameOption,
GameProgress,
GameQuestion,
SubmitGameLevelPayload,
} from './game';
export type {
LegalBrand,
LegalConversation,
LegalFAQ,
LegalMessage,
LegalPlan,
LegalService,
LegalServiceType,
LegalTemplate,
} from './legal';
export type {
AboutInfo,
AboutLink,
ActionSetting,
ActionSettingVariant,
PrivacyCenter,
PrivacyLevel,
PrivacyLevelInfo,
PrivacySection,
PrivacySettingItem,
PrivacySettingItemType,
ToggleSetting,
UpdateTogglePayload,
} from './settings';
export type {
AIInterviewData,
InterviewAbilityImprovement,
InterviewJobInfo,
InterviewMode,
InterviewModeOption,
InterviewQuestionTypeCode,
InterviewQuestionTypeItem,
InterviewRecent,
InterviewSession,
InterviewSpecial,
} from './interview';
export type {
AppealDimension,
AppealReason,
AppealType,
EvaluationAppealForm,
EvaluationDimension,
EvaluationReview,
EvaluationTrendPoint,
HREvaluation,
} from './evaluation';
export type {
DataExportCategory,
DataExportFormat,
DataExportItem,
DataExportTimeRange,
PersonalDataExport,
RecentExportRecord,
} from './data-export';

105
src/types/interview.ts

@ -0,0 +1,105 @@
/**
* AI 模拟面试相关类型定义
*/
/** 面试模式:全真模拟 / 专项突破 / 快速问答 */
export type InterviewMode = 'full' | 'special' | 'fast';
/** 面试模式选项 */
export interface InterviewModeOption {
id: InterviewMode;
/** 模式名称 */
label: string;
/** 模式描述 */
desc: string;
/** Ionicons 图标名 */
icon: string;
}
/** 面试岗位/公司/难度/时长信息 */
export interface InterviewJobInfo {
/** 面试岗位 */
position: string;
/** 面试公司 */
company: string;
/** 面试难度 */
difficulty: string;
/** 面试时长(分钟) */
duration: number;
}
/** 题型号 */
export type InterviewQuestionTypeCode = 'tech' | 'behavior' | 'logic';
/** 题型设置项 */
export interface InterviewQuestionTypeItem {
id: InterviewQuestionTypeCode;
/** 题型名称 */
label: string;
/** 颜色 token(页面映射为 colors[color]) */
color: string;
/** 是否选中 */
selected: boolean;
}
/** 本次预估可提升能力项 */
export interface InterviewAbilityImprovement {
id: string;
/** 能力名称 */
label: string;
/** 提升百分比 */
percent: number;
/** 颜色 token */
color: string;
}
/** 最近练习记录 */
export interface InterviewRecent {
id: string;
/** 练习日期 */
date: string;
/** 练习名称 */
title: string;
/** 结果评级 */
result: string;
/** 结果颜色 token */
resultColor: string;
}
/** 专项突破选项(专项突破模式下选择弱项) */
export interface InterviewSpecial {
id: string;
/** 专项名称 */
label: string;
/** 是否选中 */
selected: boolean;
}
/** 面试会话(已开始) */
export interface InterviewSession {
id: string;
/** 总题数 */
total: number;
/** 当前题号(从 1 开始) */
currentIndex: number;
/** 剩余时间(秒) */
remainingSeconds: number;
}
/** AI 模拟面试页面聚合数据 */
export interface AIInterviewData {
/** 当前模式 */
mode: InterviewMode;
/** 可选模式 */
modes: InterviewModeOption[];
/** 岗位/公司/难度/时长 */
jobInfo: InterviewJobInfo;
/** 题型设置 */
questionTypes: InterviewQuestionTypeItem[];
/** 专项突破选项 */
specials: InterviewSpecial[];
/** 本次预估可提升能力 */
improvements: InterviewAbilityImprovement[];
/** 最近练习记录 */
recent: InterviewRecent[];
}

22
src/types/job.ts

@ -2,14 +2,6 @@
* 岗位相关类型定义
*/
/** 岗位分类 */
export interface Category {
id: string;
name: string;
/** Ionicons 图标名(类型层不依赖 UI 库,渲染时再断言) */
icon: string;
}
/** 岗位评级等级 */
export type JobLevel = 'excellent' | 'good' | 'medium';
@ -42,16 +34,6 @@ export interface Job {
benefits: string[];
}
/** 岗位匹配度 */
export interface MatchData {
/** 岗位名称 */
position: string;
/** 匹配度(0-100) */
matchRate: number;
/** 副标题/行动文案,如 "双向匹配成功·立即沟通" */
subtitle: string;
}
/** 岗位排序方式 */
export type SortType = 'comprehensive' | 'latest' | 'salary';
@ -89,7 +71,7 @@ export interface JobDetail extends Job {
salaryMonths: number;
}
/** 岗位详情维度评分(雷达图 6 维度,与用户端 DimensionScore 区分命名) */
/** 岗位详情维度评分(雷达图 8 维度,与用户端 DimensionScore 区分命名) */
export interface JobDimensionScore {
id: string;
/** 维度名称,如 "岗位真实性" */
@ -106,7 +88,7 @@ export interface JobOverallScore {
level: string;
/** 排名文案,如 "超过91%同行业岗位" */
rank: string;
/** 6 个维度评分 */
/** 8 个维度评分 */
dimensions: JobDimensionScore[];
}

103
src/types/legal.ts

@ -0,0 +1,103 @@
/**
* 阿布阿诺 AI 法律服务助手领域模型。
* 为大学生求职者提供求职劳动权益、劳动合同、不公平辞退等法律问题的基础与付费法律服务。
*/
/** 法律服务分类 */
export type LegalServiceType = 'free' | 'member';
/** 单条法律服务项 */
export interface LegalService {
/** 服务 id */
id: string;
/** 服务标题 */
title: string;
/** 服务描述 */
description: string;
/** 图标名(Ionicons) */
icon: string;
/** 服务分类:免费 / 会员 */
type: LegalServiceType;
/** 跳转动作:chat=法律咨询对话 / templates=文书模板 / faq=常见问题 / upgrade=开通会员 */
action: 'chat' | 'templates' | 'faq' | 'upgrade';
}
/** 法律常见问题 */
export interface LegalFAQ {
/** FAQ id */
id: string;
/** 问题 */
question: string;
/** 答案 */
answer: string;
/** 所属分类 */
category: string;
}
/** 法律文书模板 */
export interface LegalTemplate {
/** 模板 id */
id: string;
/** 模板名称 */
name: string;
/** 模板描述 */
description: string;
/** 图标名(Ionicons) */
icon: string;
/** 下载/预览地址(mock 阶段为占位) */
url: string;
}
/** 会员套餐 */
export interface LegalPlan {
/** 套餐 id */
id: string;
/** 套餐名称 */
name: string;
/** 价格(元/周期) */
price: number;
/** 周期描述,如「/月」「/年」 */
period: string;
/** 权益列表 */
benefits: string[];
/** 是否推荐 */
recommended?: boolean;
}
/** 法律咨询单条消息 */
export interface LegalMessage {
/** 消息 id */
id: string;
/** 消息角色 */
role: 'user' | 'assistant';
/** 消息内容 */
content: string;
/** 时间戳 */
timestamp: string;
/** 是否为富卡片(如律师推荐、文书入口) */
card?: 'lawyer' | 'template' | 'plan';
/** 卡片附加数据 */
cardData?: Record<string, string>;
}
/** 法律咨询会话 */
export interface LegalConversation {
/** 会话 id */
id: string;
/** 会话标题 */
title: string;
/** 消息列表 */
messages: LegalMessage[];
}
/** 阿布阿诺品牌信息 */
export interface LegalBrand {
/** 品牌名 */
name: string;
/** 公司全称 */
company: string;
/** 品牌口号 */
slogan: string;
/** 品牌 Logo(mock 阶段用 emoji/图标占位) */
logo: string;
}

63
src/types/message.ts

@ -60,3 +60,66 @@ export interface MatchResult {
/** 企业名称列表 */
companies: string[];
}
/* ===================== 聊天详情(HR 单聊会话) ===================== */
/** 聊天消息发送方 */
export type ChatMessageRole = 'user' | 'hr' | 'system';
/** 富消息卡片类型 */
export type ChatCardType = 'interview' | 'salary' | 'offer';
/** 富消息卡片 */
export interface ChatCard {
/** 卡片类型 */
type: ChatCardType;
/** 卡片标题 */
title: string;
/** 卡片副标题/说明 */
subtitle?: string;
/** 列表项(面试题、薪资项等) */
items?: string[];
/** 操作按钮文案 */
actionText: string;
/** 跳转路由 */
route?: string;
}
/** 单条聊天消息 */
export interface ChatMessage {
/** 消息 id */
id: string;
/** 发送方 */
role: ChatMessageRole;
/** 文本内容(卡片消息可空) */
content?: string;
/** 富消息卡片 */
card?: ChatCard;
/** 发送时间,如 "10:30" */
timestamp?: string;
/** 是否已读 */
read?: boolean;
}
/** 聊天会话详情 */
export interface ConversationDetail {
/** 会话 id */
id: string;
/** HR 姓名 */
contactName: string;
/** 公司名 */
companyName: string;
/** 岗位名 */
positionName: string;
/** HR 头像占位 */
avatar: string;
/** 在线状态 */
online: boolean;
/** 双向匹配信息 */
matchInfo?: {
/** 匹配度百分比 0-100 */
score: number;
};
/** 消息列表(时间顺序) */
messages: ChatMessage[];
}

83
src/types/offer-compare.ts

@ -0,0 +1,83 @@
/**
* Offer 对比相关类型定义
*/
/** 单个 Offer */
export interface OfferItem {
id: string;
/** 公司名 */
company: string;
/** 岗位名称 */
position: string;
/** 薪资范围 */
salary: string;
/** 是否已选中参与对比 */
selected: boolean;
/** 主题色(用于图表/高亮) */
color: string;
}
/** 评分项 */
export interface OfferScore {
/** 分数 */
score: number;
/** 等级,如 "优秀" / "良好" */
level: string;
/** 星级 0-5 */
stars: number;
}
/** 薪资福利对比 - 单行 */
export interface OfferBenefitRow {
/** 行标签 */
label: string;
/** 每个 Offer 的值 */
values: (string | null)[];
/** 是否高亮该行的每个值(如更高的基本工资) */
highlights?: boolean[];
}
/** 薪资福利对比 - 分组 */
export interface OfferBenefitGroup {
/** 分组标题,如 "薪资" / "福利" / "发展" */
title: string;
/** 分组下的行 */
rows: OfferBenefitRow[];
}
/** 工作生活平衡对比 */
export interface WorkLifeBalanceRow {
label: string;
values: string[];
highlights?: boolean[];
}
/** AI 智能建议 */
export interface OfferAISuggestion {
/** 一句话总结 */
summary: string;
/** 详细分析 */
detail: string;
/** 查看报告按钮文案 */
reportAction: string;
}
/** Offer 对比 - 完整数据 */
export interface OfferCompareData {
/** 参与对比的 Offer 列表 */
offers: OfferItem[];
/** 综合评分对比 */
scores: OfferScore[];
/** 薪资福利对比分组 */
benefitGroups: OfferBenefitGroup[];
/** 工作生活平衡对比 */
workLifeBalance: WorkLifeBalanceRow[];
/** AI 智能建议 */
aiSuggestion: OfferAISuggestion;
/** 底部操作 */
actions: {
exportLabel: string;
acceptLabel: string;
acceptOfferId: string;
};
}

121
src/types/operation.ts

@ -0,0 +1,121 @@
/**
* 运营 / 风控 / 平台治理领域模型
*
* 包含:
* - 运营规则配置(双向匹配规则、匹配模式、阈值、自动筛选等)
* - 数据审计报告(风控审计、核心数据看板、威胁趋势、风险类型分布)
*/
/* ===================== 运营规则配置 ===================== */
/** 匹配模式 */
export type MatchMode = 'strict' | 'recommended' | 'broad';
/** 匹配模式选项 */
export interface MatchModeOption {
/** 模式 id */
id: MatchMode;
/** 模式名称 */
name: string;
/** 模式说明 */
desc: string;
/** 是否推荐 */
recommended?: boolean;
}
/** 自动筛选规则项 */
export interface AutoFilterRule {
/** 规则 id */
id: string;
/** 规则名称 */
name: string;
/** 规则说明 */
description: string;
/** 是否启用 */
enabled: boolean;
}
/** 双向匹配规则 / 运营规则配置 */
export interface OperationRules {
/** 当前匹配模式 */
matchMode: MatchMode;
/** 可选匹配模式 */
matchModeOptions: MatchModeOption[];
/** 双向匹配阈值 0-100 */
matchThreshold: number;
/** 候选人可见度阈值 0-100 */
visibilityThreshold: number;
/** 每日推荐数量上限 */
dailyRecommendLimit: number;
/** 自动筛选规则列表 */
autoFilters: AutoFilterRule[];
/** 推荐规则:允许急招岗位优先 */
urgentPriority: boolean;
/** 推荐规则:允许校园岗位优先 */
campusPriority: boolean;
/** 通知设置:新候选人投递通知 */
notifyNewDelivery: boolean;
}
/* ===================== 数据审计报告 ===================== */
/** 审计等级 */
export type AuditGrade = 'A' | 'B' | 'C' | 'D';
/** 审计核心指标 */
export interface AuditKPI {
/** 指标 id */
id: string;
/** 指标名称 */
label: string;
/** 数值 */
value: number;
/** 较上期变化(可为负) */
change: number;
/** 单位 */
unit?: string;
/** Ionicons 图标名 */
icon: string;
/** 颜色 token */
color: string;
}
/** 威胁趋势点 */
export interface AuditTrendPoint {
/** 日期 / 月份 */
date: string;
/** 风险事件数 */
riskCount: number;
/** 处理数 */
handledCount: number;
}
/** 风险类型分布项 */
export interface RiskDistributionItem {
/** 类型 id */
id: string;
/** 类型名称 */
label: string;
/** 占比 0-100 */
percent: number;
/** 颜色 token */
color: string;
}
/** 数据审计报告 */
export interface AuditReport {
/** 审计周期 */
period: string;
/** 综合等级 */
grade: AuditGrade;
/** 等级说明 */
gradeDesc: string;
/** 今日拦截异常数 */
todayBlocked: number;
/** 核心指标 */
kpis: AuditKPI[];
/** 威胁趋势 */
threatTrend: AuditTrendPoint[];
/** 风险类型分布 */
riskDistribution: RiskDistributionItem[];
}

96
src/types/rank.ts

@ -0,0 +1,96 @@
/**
* 同岗位竞争力排名相关类型定义
*/
/** 排名总览(岗位维度头信息 + 综合排名指标) */
export interface RankData {
/** 当前排名名次,如 127 */
rank: number;
/** 岗位名称,如 "高级前端工程师" */
jobTitle: string;
/** 公司名,如 "字节跳动" */
company: string;
/** 候选人总数,如 1286 */
totalCandidates: number;
/** 超越的候选人百分比,如 90.1 */
percentile: number;
/** 综合评分,如 88 */
overallScore: number;
/** 评分等级,如 "优秀" */
scoreLevel: string;
/** 百分位排名文案,如 "前 9.9%" */
rankPosition: string;
}
/** 维度百分位 */
export interface DimensionPercentile {
id: string;
/** 维度名称,如 "专业技能" */
name: string;
/** 百分位排名文案,如 "前 15%" */
percentile: string;
/** 该项评分,如 92 */
score: number;
}
/** TOP 候选人 */
export interface TopCandidate {
id: string;
/** 候选人名称,如 "候选人 A" */
name: string;
/** 分数,如 96 */
score: number;
/** 候选标签,如 ["清华硕士", "5 年大厂"] */
tags: string[];
}
/** 提升建议 */
export interface ImprovementTip {
id: string;
/** 建议主标题,如 "补充项目经验可提升 5-8 个百分位" */
title: string;
/** 建议描述,如 "完善你的项目经历描述" */
description: string;
/** tip=普通建议,action=可点击操作 */
type: 'tip' | 'action';
}
/** 简历完善进度步骤 */
export interface ResumeProgressStep {
/** 步骤 id */
id: string;
/** 步骤名称 */
label: string;
/** 是否已完成 */
completed: boolean;
/** 补充提示 */
tip?: string;
}
/** 竞争力排名空状态 */
export interface RankEmptyState {
/** 空状态标题 */
title: string;
/** 空状态副标题 */
subtitle: string;
/** 按钮文案 */
actionText: string;
/** 简历完善进度 */
resumeProgress: ResumeProgressStep[];
}
/** 竞争力排名聚合数据(含数据态与空状态) */
export interface RankOverview {
/** 是否有排名数据 */
hasData: boolean;
/** 数据态:排名总览 */
rankData?: RankData;
/** 数据态:维度百分位 */
dimensions?: DimensionPercentile[];
/** 数据态:TOP 候选人 */
candidates?: TopCandidate[];
/** 数据态:提升建议 */
tips?: ImprovementTip[];
/** 空状态 */
emptyState?: RankEmptyState;
}

171
src/types/resume.ts

@ -0,0 +1,171 @@
/**
* 简历相关类型定义
*/
/** 简历基本信息 */
export interface ResumeBasicInfo {
/** 姓名 */
name: string;
/** 头像展示字符 */
avatar: string;
/** 手机号 */
phone: string;
/** 邮箱 */
email: string;
/** 所在城市 */
location: string;
}
/** 求职意向 */
export interface ResumeJobIntention {
/** 目标职位 */
position: string;
/** 期望薪资 */
salary: string;
/** 期望城市 */
city: string;
/** 求职状态 */
status: 'active' | 'inactive';
/** 状态展示文案 */
statusLabel: string;
}
/** 教育背景 */
export interface ResumeEducation {
id: string;
/** 学校 */
school: string;
/** 专业 */
major: string;
/** 学历 */
degree: string;
/** 入学年份 */
startYear: string;
/** 毕业年份 */
endYear: string;
}
/** 工作经历 */
export interface ResumeExperience {
id: string;
/** 公司 */
company: string;
/** 职位 */
position: string;
/** 开始时间 */
startDate: string;
/** 结束时间 */
endDate: string;
/** 工作内容 */
description: string;
/** 是否已 AI 优化 */
aiOptimized: boolean;
}
/** 项目经历 */
export interface ResumeProject {
id: string;
/** 项目名称 */
name: string;
/** 项目角色 */
role: string;
/** 项目描述 */
description: string;
/** 是否已 AI 优化 */
aiOptimized: boolean;
}
/** 技能标签 */
export interface ResumeSkill {
name: string;
}
/** 自我评价 */
export interface ResumeSelfEvaluation {
content: string;
}
/** 简历优化点 */
export interface ResumeOptimizePoint {
id: string;
/** 优化项标题 */
title: string;
/** 预计提升分数 */
score: number;
/** 是否已优化 */
optimized: boolean;
}
/** 简历评分 */
export interface ResumeScore {
/** 当前分数 */
score: number;
/** 满分 */
maxScore: number;
/** 可优化项列表 */
optimizePoints: ResumeOptimizePoint[];
}
/** 完整简历数据 */
export interface Resume {
/** 简历评分 */
score: ResumeScore;
/** 基本信息 */
basicInfo: ResumeBasicInfo;
/** 求职意向 */
jobIntention: ResumeJobIntention;
/** 教育背景 */
education: ResumeEducation[];
/** 工作经历 */
experience: ResumeExperience[];
/** 项目经历 */
projects: ResumeProject[];
/** 技能特长 */
skills: ResumeSkill[];
/** 自我评价 */
selfEvaluation: ResumeSelfEvaluation;
}
/** 简历导出格式 */
export type ResumeExportFormat = 'pdf' | 'word' | 'image';
/** 简历模板 */
export interface ResumeTemplate {
id: string;
/** 模板名称 */
name: string;
/** 缩略图颜色 token(页面映射为 colors[color]) */
color: string;
}
/** 简历导出设置 */
export interface ResumeExportSettings {
/** 导出格式 */
format: ResumeExportFormat;
/** 模板 id */
templateId: string;
/** 隐藏联系方式 */
hideContact: boolean;
/** 导出密码保护 */
exportPassword: boolean;
/** 纸型 */
paperSize: 'A4' | 'letter';
/** 方向 */
orientation: 'portrait' | 'landscape';
/** 文件名 */
fileName: string;
/** 包含封面页 */
includeCover: boolean;
/** 包含自荐信 */
includeRecommendation: boolean;
/** 定向优化:粘贴岗位 JD */
targetJobDescription?: string;
}
/** AI 简历优化结果 */
export interface ResumeOptimizeResult {
/** 优化后总分 */
score: number;
/** 优化后的简历数据 */
resume: Resume;
}

123
src/types/settings.ts

@ -0,0 +1,123 @@
/**
* 设置页领域模型(隐私设置 / 消息通知 / 账号与安全 / 关于我们)。
* 开关型与操作型设置分离建模,页面只负责渲染,状态变更由 API 层持久化。
*/
/** 开关型设置项(隐私开关、通知开关等) */
export interface ToggleSetting {
/** 设置项 id */
id: string;
/** 标题 */
title: string;
/** 说明文案 */
description: string;
/** 是否开启 */
enabled: boolean;
}
/** 操作型设置项展示变体 */
export type ActionSettingVariant = 'normal' | 'danger';
/** 操作型设置项(绑定手机号、修改密码、注销账号、跳转协议等) */
export interface ActionSetting {
/** 设置项 id */
id: string;
/** 标题 */
title: string;
/** 说明文案 */
description: string;
/** 右侧展示值(如已绑定的手机号 138****8888) */
value?: string;
/** 展示变体:normal 普通 / danger 危险操作(红色) */
variant: ActionSettingVariant;
/** 跳转路由(未配置则为待开发项) */
route?: string;
}
/** 关于我们:可跳转的协议/信息链接 */
export interface AboutLink {
/** 链接 id */
id: string;
/** 标题 */
title: string;
/** 跳转路由(未配置则为待开发项) */
route?: string;
}
/** 关于我们信息 */
export interface AboutInfo {
/** 应用名 */
appName: string;
/** 版本号 */
version: string;
/** 公司主体 */
company: string;
/** 应用简介 */
description: string;
/** 协议与信息链接 */
links: AboutLink[];
}
/** 更新开关型设置的请求体 */
export interface UpdateTogglePayload {
/** 设置项 id */
id: string;
/** 目标开关状态 */
enabled: boolean;
}
/* ===================== 隐私中心(截图版) ===================== */
/** 隐私等级 */
export type PrivacyLevel = 'low' | 'medium' | 'high';
/** 隐私等级信息 */
export interface PrivacyLevelInfo {
/** 等级 key */
level: PrivacyLevel;
/** 展示文案 */
label: string;
/** 等级说明 */
description: string;
}
/** 隐私设置项类型 */
export type PrivacySettingItemType = 'switch' | 'link' | 'danger-link' | 'value';
/** 隐私中心单条设置项 */
export interface PrivacySettingItem {
/** 设置项 id */
id: string;
/** 标题 */
title: string;
/** 说明文案 */
description?: string;
/** 类型 */
type: PrivacySettingItemType;
/** 开关状态(type=switch 时有效) */
enabled?: boolean;
/** 右侧展示值(type=value 时有效) */
value?: string;
/** 跳转路由 */
route?: string;
}
/** 隐私设置分组 */
export interface PrivacySection {
/** 分组 id */
id: string;
/** 分组标题 */
title: string;
/** 分组项 */
items: PrivacySettingItem[];
}
/** 隐私中心聚合数据 */
export interface PrivacyCenter {
/** 当前隐私等级 */
privacyLevel: PrivacyLevelInfo;
/** 分组列表 */
sections: PrivacySection[];
/** 底部权限说明 */
explanation: string;
}

348
src/types/tools.ts

@ -0,0 +1,348 @@
/**
* 工具类页面领域模型(行业技能词云 / 薪资计算器 / 求职进度 / 选择身份 / 入职准备清单)。
* 与职来设计稿中「行业技能词云」「薪资计算器」「求职进度」「选择身份」「入职准备清单」五屏对应。
*/
/** 入职准备清单分类 */
export type ChecklistCategory = 'material' | 'procedure' | 'study' | 'life' | 'daily';
/** 入职准备清单单项 */
export interface ChecklistItem {
id: string;
/** 待办标题 */
title: string;
/** 是否已完成 */
done: boolean;
/** 是否必填 */
required?: boolean;
}
/** 入职准备清单分组 */
export interface ChecklistGroup {
id: ChecklistCategory;
/** 分组标题 */
title: string;
/** Ionicons 图标名 */
icon: string;
/** 是否展开 */
expanded: boolean;
/** 分组待办 */
items: ChecklistItem[];
}
/** 入职小贴士 */
export interface OnboardingTip {
id: string;
/** 标题 */
title: string;
/** 描述 */
desc: string;
/** Ionicons 图标名 */
icon: string;
}
/** 入职准备清单数据 */
export interface OnboardingChecklist {
/** 距离入职天数 */
daysLeft: number;
/** 岗位名 */
position: string;
/** 公司名 */
company: string;
/** 总待办数 */
total: number;
/** 已完成数 */
completed: number;
/** 分组清单 */
groups: ChecklistGroup[];
/** 入职日期 */
onboardingDate: string;
/** 每日提醒 */
dailyReminder: {
enabled: boolean;
time: string;
};
/** 入职小贴士 */
tips: OnboardingTip[];
}
/** 行业技能词云:单个技能词(权重决定字号与色深) */
export interface SkillWord {
/** 唯一 id */
id: string;
/** 技能名称(如 React、数据分析) */
text: string;
/** 热度权重 0-100,决定字号与色深 */
weight: number;
/** 所属技能分组(用于配色区分) */
group: 'tech' | 'soft' | 'domain' | 'tool';
}
/** 技能分类 */
export interface SkillCategory {
id: string;
label: string;
}
/** 热门技能 TOP N */
export interface TopSkill {
id: string;
/** 技能名 */
name: string;
/** 排名 */
rank: number;
/** 热度变化百分比 */
change: number;
/** 需求热度 0-100 */
demand: number;
}
/** 词云筛选标签 */
export interface SkillCloudFilter {
/** 行业选项 */
industries: string[];
/** 岗位选项 */
positions: string[];
/** 城市选项 */
cities: string[];
/** 经验选项 */
experiences: string[];
}
/** 行业技能词云数据 */
export interface SkillCloudData {
/** 行业名称 */
industry: string;
/** 岗位名称(展示用) */
position: string;
/** 城市 */
city: string;
/** 经验要求 */
experience: string;
/** 当前选中的技能分类 id */
activeCategoryId: string;
/** 筛选可选项 */
filters: SkillCloudFilter;
/** 技能分类 */
categories: SkillCategory[];
/** 技能词列表 */
words: SkillWord[];
/** 热门技能 TOP 10 */
topSkills: TopSkill[];
/** 个人技能匹配度 0-100 */
matchPercent: number;
/** 数据更新时间(展示用) */
updatedAt: string;
}
/** 薪资计算器输入参数 */
export interface SalaryCalcInput {
/** 税前月薪(元) */
monthlyGross: number;
/** 所在城市(决定社保公积金上下限,mock 阶段仅展示) */
city: string;
/** 社保缴费基数(元) */
socialBase: number;
/** 公积金缴纳比例 0-1(如 0.12) */
providentFundRate: number;
/** 专项附加扣除(元/月) */
specialDeduction: number;
/** 年终奖月数 */
bonusMonths: number;
}
/** 五险一金单项明细 */
export interface SalaryInsuranceItem {
/** 险种名称 */
label: string;
/** 个人缴纳比例(0-1) */
rate: number;
/** 个人缴纳金额(元/月) */
amount: number;
}
/** 个人所得税明细项 */
export interface SalaryTaxItem {
/** 税项名称 */
label: string;
/** 金额(元) */
amount: number;
/** 适用税率(可选) */
rate?: number;
}
/** 薪资结构饼图单项 */
export interface SalaryStructureItem {
/** 分类名称 */
label: string;
/** 金额(元) */
value: number;
/** 占比 0-100 */
percent: number;
/** 主题色 token(页面中映射为 colors[color]) */
color: string;
}
/** 薪资计算器更多工具入口 */
export interface SalaryToolLink {
id: string;
/** 工具名称 */
title: string;
/** 工具描述 */
desc: string;
/** Ionicons 图标名 */
icon: string;
/** 跳转路由 */
route?: string;
}
/** 薪资计算器结果(接口层计算,页面只渲染) */
export interface SalaryCalcResult {
/** 输入参数快照 */
input: SalaryCalcInput;
/** 税前月薪(元) */
monthlyGross: number;
/** 年终奖月数 */
bonusMonths: number;
/** 月度税前合计(月薪 + 年终摊入) */
monthlyTotal: number;
/** 月度五险一金个人缴纳合计(元) */
monthlySocial: number;
/** 月度个人所得税(元) */
monthlyTax: number;
/** 月到手(元) */
monthlyNet: number;
/** 年税前合计(元) */
annualGross: number;
/** 年到手合计(元) */
annualNet: number;
/** 五险一金明细 */
insuranceItems: SalaryInsuranceItem[];
/** 个税明细 */
taxItems: SalaryTaxItem[];
/** 薪资结构(税前/五险一金/个税/到手) */
structure: SalaryStructureItem[];
}
/** 行业薪资参考(计算器对比用) */
export interface SalaryBracket {
/** 行业名称 */
industry: string;
/** 该行业平均月薪(K) */
avgMonthly: number;
/** 样本量 */
sampleCount: number;
}
/** 求职进度统计卡 */
export interface JobProgressStats {
/** 投递总数 */
total: number;
/** 待反馈数 */
pending: number;
/** 面试中 */
interviewing: number;
/** Offer 数 */
offer: number;
/** 已结束 */
ended: number;
}
/** 求职进度:即将到期 Offer */
export interface JobProgressOffer {
id: string;
/** 公司名 */
companyName: string;
/** 岗位名 */
positionName: string;
/** 薪资 */
salary: string;
/** 截止日期展示文本 */
deadline: string;
/** 剩余天数 */
daysLeft: number;
}
/** 求职进度:公司流程条目 */
export type JobProgressStage = '投递' | '待反馈' | '面试中' | 'Offer' | '已结束';
export interface JobProgressCompany {
id: string;
/** 公司名 */
companyName: string;
/** 岗位名 */
positionName: string;
/** 薪资 */
salary: string;
/** 当前阶段 */
stage: JobProgressStage;
/** 阶段展示标签 */
stageLabel: string;
/** 下一步动作或状态 */
nextAction: string;
/** 更新时间 */
date: string;
/** 未读数 */
unread?: number;
}
/** 求职进度:月度统计 */
export interface JobProgressMonthly {
/** 月份,如「8月」 */
month: string;
/** 投递数 */
deliveries: number;
/** 面试数 */
interviews: number;
/** Offer 数 */
offers: number;
}
/** 求职进度步骤状态(派生联合类型,禁止页面硬编码判断) */
export type ProgressStatus = 'done' | 'active' | 'todo';
/** 求职进度单个步骤 */
export interface ProgressStep {
id: string;
/** 阶段标题(如 已投递) */
title: string;
/** 阶段描述 */
desc: string;
/** 状态:已完成 / 进行中 / 未开始 */
status: ProgressStatus;
/** 完成时间(已完成步骤展示,进行中/未开始为空) */
date?: string;
}
/** 求职进度数据 */
export interface JobProgress {
/** 统计概览 */
stats: JobProgressStats;
/** 即将到期 Offer */
offers: JobProgressOffer[];
/** 公司流程列表 */
companies: JobProgressCompany[];
/** 月度统计 */
monthlyStats: JobProgressMonthly[];
/** 时间轴步骤(可选,保留旧结构) */
steps?: ProgressStep[];
/** 面试中数 */
interviewCount?: number;
/** 总投递数 */
deliveryCount?: number;
/** 已拿 Offer 数 */
offerCount?: number;
}
/** 身份选项(C 端求职者 / 企业端 HR) */
export interface IdentityOption {
id: string;
/** 身份标题 */
title: string;
/** 身份描述 */
desc: string;
/** 图标名(Ionicons) */
icon: string;
/** 角色:求职者 / HR */
role: 'candidate' | 'hr';
}

8
src/types/user.ts

@ -2,14 +2,6 @@
* 用户相关类型定义
*/
/** 用户信息 */
export interface UserInfo {
/** 姓名 */
name: string;
/** 个人 slogan */
slogan: string;
}
/** 用户个人信息(我的页) */
export interface UserProfile {
id: string;

31
src/types/weight.ts

@ -0,0 +1,31 @@
/**
* 权重设置相关类型定义
*/
/** 权重模板 */
export interface WeightTemplate {
id: string;
/** 模板名称,如 "薪资优先" */
name: string;
/** 模板描述,如 "薪资30% · 发展15%" */
description: string;
/** 模板各维度权重,键为维度名称,如 { "薪资竞争力": 30, "发展空间": 15 } */
weights: Record<string, number>;
}
/** 权重维度 */
export interface WeightDimension {
id: string;
/** 维度名称,如 "薪资竞争力" */
name: string;
/** 权重百分比,如 20 */
weight: number;
/** true 表示反向指标(值越低评分越高,如工作强度) */
isReversed?: boolean;
}
/** 权重设置 */
export interface WeightSettings {
dimensions: WeightDimension[];
templates: WeightTemplate[];
}

10
src/utils/index.ts

@ -9,6 +9,16 @@ export function formatThousands(n: number): string {
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
/**
* 金额格式化:30000 → "30,000";decimals 控制小数位,默认 0。
*/
export function formatCurrency(n: number, decimals = 0): string {
const fixed = n.toFixed(decimals);
const parts = fixed.split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return decimals > 0 ? parts.join('.') : parts[0];
}
/**
* 阅读量格式化:23000 → "2.3万";不足一万返回原数字字符串(如 "8500")。
*/

Loading…
Cancel
Save