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 统一导出 |
* 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 { 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 { 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 { useJobDetail } from './useJobDetail'; |
||||
|
export { useJobEvaluationEmptyState } from './useJobEvaluationEmptyState'; |
||||
|
export { useJobEvaluations } from './useJobEvaluations'; |
||||
export { useJobList } from './useJobList'; |
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 { 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 { 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