Browse Source
类型层(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
64 changed files with 7799 additions and 236 deletions
File diff suppressed because it is too large
@ -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'; |
|||
|
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
/** |
|||
* 消息通知设置 hook:拉取通知开关列表并提供切换能力。 |
|||
*/ |
|||
import { fetchNotificationSettings, updateNotificationSetting } from '../api'; |
|||
import { useToggleSettings, type ToggleSettingsState } from './useToggleSettings'; |
|||
|
|||
export function useNotificationSettings(): ToggleSettingsState { |
|||
return useToggleSettings(fetchNotificationSettings, updateNotificationSetting); |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
/** |
|||
* 隐私设置 hook:拉取隐私开关列表并提供切换能力。 |
|||
*/ |
|||
import { fetchPrivacySettings, updatePrivacySetting } from '../api'; |
|||
import { useToggleSettings, type ToggleSettingsState } from './useToggleSettings'; |
|||
|
|||
export function usePrivacySettings(): ToggleSettingsState { |
|||
return useToggleSettings(fetchPrivacySettings, updatePrivacySetting); |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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 }; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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, |
|||
}; |
|||
} |
|||
@ -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[][]; |
|||
} |
|||
@ -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[]; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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[]; |
|||
} |
|||
@ -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[]; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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; |
|||
}; |
|||
} |
|||
@ -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[]; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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; |
|||
} |
|||
@ -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'; |
|||
} |
|||
@ -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[]; |
|||
} |
|||
Loading…
Reference in new issue