| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- import { getApiBaseUrl, getServerConfig, request } from '@/utils/auth'
- import { defaultOnboardingInvitations, onboardStatus } from '@/data/mock'
- // ============ 模式判断 ============
- function isServerMode() {
- return getServerConfig().mode === 'server'
- }
- function serverPath() {
- const base = getApiBaseUrl()
- if (!base) throw new Error('缺少服务器地址,请先在登录页右上角配置')
- return base
- }
- // ============ 枚举 ============
- // 用工类型枚举值 → 中文标签(与后端约定一致)
- export const EMPLOYMENT_TYPE_LABEL = {
- FULL_TIME: '全职',
- INTERN: '实习',
- PART_TIME: '兼职',
- OTHER: '其他',
- }
- // ============ VO → 前端形状映射 ============
- /**
- * 把后端入职记录 VO 归一为前端可用形状;base 用于演示版合并 mock 字段
- * 后端字段映射约定:
- * - onboardingId / id
- * - employeeName → name
- * - deptName → department、deptId
- * - positionName → position、positionId
- * - companyName → company、legalId
- * - plannedJoinDate → hireDate(取前 10 位)
- * - employmentType / probationMonths / validDays / createMode / idNumber / gender / age / phone / email
- * - onboardingStatus / approvalStatus → status (pending|processing|done)
- * - offerStatus → offer、contractStatus → contract、assetsStatus → assets
- * - publicPath / publicToken / invitationStatus
- */
- export function adaptOnboardingRecord(vo, base = {}) {
- if (!vo || typeof vo !== 'object') return null
- const id = String(vo.onboardingId ?? vo.id ?? base.id ?? '')
- const onboardingStatus = vo.onboardingStatus ?? base.onboardingStatus ?? ''
- const approvalStatus = vo.approvalStatus ?? base.approvalStatus ?? ''
- const status =
- onboardingStatus === 'COMPLETED' || approvalStatus === 'APPROVED'
- ? 'done'
- : onboardingStatus === 'SUBMITTED' || approvalStatus === 'APPROVING'
- ? 'processing'
- : base.status ?? 'pending'
- return {
- id,
- onboardingId: id,
- invitationId: vo.invitationId ?? base.invitationId ?? '',
- invitationNo: vo.invitationNo ?? base.invitationNo ?? '',
- employeeNo: vo.employeeNo ?? base.employeeNo ?? id,
- name: vo.employeeName ?? base.name ?? '',
- department: vo.deptName ?? base.department ?? '',
- deptId: vo.deptId ?? base.deptId ?? null,
- position: vo.positionName ?? base.position ?? '',
- positionId: vo.positionId ?? base.positionId ?? null,
- level: vo.level ?? base.level ?? '',
- hireDate: (vo.plannedJoinDate || base.hireDate || base.expectedEntryDate || '').slice(0, 10),
- company: vo.companyName ?? base.company ?? '',
- legalId: vo.legalId ?? base.legalId ?? null,
- phone: vo.phone ?? base.phone ?? '',
- email: vo.email ?? base.email ?? '',
- idNumber: vo.idNumber ?? base.idNumber ?? '',
- gender: vo.gender ?? base.gender ?? '',
- age: vo.age ?? base.age ?? '',
- employmentType: vo.employmentType ?? base.employmentType ?? 'FULL_TIME',
- probationFlag: vo.probationFlag ?? base.probationFlag ?? 1,
- probationMonths: vo.probationMonths ?? base.probationMonths ?? 3,
- validDays: vo.validDays ?? base.validDays ?? 7,
- createMode: vo.createMode ?? base.createMode ?? 'manual',
- status,
- flow: onboardingStatus === 'SUBMITTED' ? '档案已完善' : base.flow ?? '待补充档案',
- offer: vo.offerStatus ?? base.offer ?? '未发起',
- contract: vo.contractStatus ?? base.contract ?? '未发起',
- assets: vo.assetsStatus ?? base.assets ?? '未领用',
- remark: vo.remark ?? base.remark ?? '',
- publicPath: vo.publicPath ?? base.publicPath ?? '',
- publicToken: vo.publicToken ?? base.publicToken ?? '',
- invitationStatus: vo.invitationStatus ?? base.invitationStatus ?? '',
- sourceType: vo.sourceType ?? 'MOCK',
- rowType: 'record',
- materials: base.materials ?? {},
- socialSecurity: base.socialSecurity ?? { status: '未办理', city: '北京', deadline: '' },
- training: base.training ?? { status: '未开始', name: '新员工入职培训', duration: '2天', deadline: '' },
- workstation: base.workstation ?? { status: '待分配', location: '' },
- itAccount: base.itAccount ?? { status: '未开通', items: [] },
- stage: base.stage ?? 'offer',
- }
- }
- /**
- * 把入职记录(mock 或 API VO)映射成列表卡片渲染形状
- * 两种输入形态:
- * - adaptOnboardingRecord(vo)(服务器模式):id, name, department, position, hireDate, status(key), employeeNo
- * - mock / storage 中的入职记录(演示模式):id, name, avatar, department, position,
- * entryDate, expectedEntryDate, materials, status(key), employeeNo
- * 输出:avatar / name / sub / status(中文 label) / statusClass / label1 / value1 / label2 / value2 / id
- */
- export function mapOnboardingForList(record) {
- if (!record || typeof record !== 'object') return null
- const id = record.id || record.onboardingId || ''
- const name = record.name || record.employeeName || ''
- const department = record.department || record.deptName || ''
- const position = record.position || record.positionName || ''
- const statusKey = record.status || 'pending'
- const statusInfo = onboardStatus[statusKey] || onboardStatus.pending
- const hireDate = record.hireDate || record.entryDate || record.expectedEntryDate || ''
- const employeeNo = record.employeeNo || id
- const isEntered = Boolean(record.entryDate)
- const materials = record.materials
- let materialsLabel = ''
- if (materials && typeof materials === 'object') {
- const total = Object.keys(materials).length
- if (total) {
- const done = Object.values(materials).filter(Boolean).length
- materialsLabel = `${done}/${total}`
- }
- }
- return {
- id,
- avatar: record.avatar || name.charAt(0) || '',
- name,
- sub: [department, position].filter(Boolean).join(' · '),
- status: statusInfo.label,
- statusClass: statusInfo.class,
- label1: isEntered ? '入职日期' : '预计入职',
- value1: isEntered ? record.entryDate : hireDate,
- label2: isEntered ? '员工编号' : (materialsLabel ? '资料进度' : '员工编号'),
- value2: isEntered ? employeeNo : (materialsLabel || employeeNo),
- }
- }
- // ============ 对外 API ============
- /**
- * 把前端表单(name/hireDate/...)映射为后端 HR 移动端待入职创建参数。
- * 后端要求字段:employeeName / plannedJoinDate / legalId / deptId / positionId / positionName
- * / phone / email / idNumber / employmentType / probationFlag / probationMonths
- * / remark / factoryId / managerUserId / teamId
- * 长整型字段在 number 字符串间互转时保留为 number,避免后端 @RequestParam 严格校验失败
- */
- export function serializeOnboardingManual(form = {}) {
- const toId = (v) => {
- if (v == null || v === '') return ""
- const n = String(v)
- return n ? n : ""
- }
- return {
- employeeName: String(form.name || form.employeeName || '').trim(),
- plannedJoinDate: String(form.hireDate || form.plannedJoinDate || '').trim(),
- legalId: toId(form.legalId),
- deptId: toId(form.deptId),
- positionId: toId(form.positionId),
- positionName: String(form.positionName || form.position || '').trim(),
- phone: String(form.phone || '').trim(),
- email: String(form.email || '').trim(),
- idNumber: String(form.idNumber || '').trim(),
- employmentType: String(form.employmentType || 'FULL_TIME').trim(),
- probationFlag: Number(form.probationFlag) === 1 ? 1 : 0,
- probationMonths: Number(form.probationMonths) || 0,
- remark: String(form.remark || '').trim(),
- factoryId: toId(form.factoryId),
- managerUserId: toId(form.managerUserId),
- teamId: toId(form.teamId),
- }
- }
- /**
- * 手动创建入职单
- * POST /hr/ess/admin/onboardings/manual
- *
- * 后端必填字段:plannedJoinDate / legalId / deptId / probationFlag
- *
- * @param {object} payload 入职表单字段(前端语义:name / hireDate / ...)
- * @returns {Promise<{ onboardingId: string|number, id: string|number, employeeNo: string }>}
- */
- export async function createOnboardingManual(payload) {
- const body = serializeOnboardingManual(payload)
- if (!isServerMode()) {
- await new Promise((r) => setTimeout(r, 250))
- const id = `ZY${Date.now()}${String(Math.floor(Math.random() * 1000)).padStart(3, '0')}`
- return { onboardingId: id, id, employeeNo: id }
- }
- const path = `${serverPath()}/hr/ess/admin/onboardings/manual`
- const res = await request({ url: path, method: 'POST', data: body })
- return res.data || {}
- }
- /**
- * 邀请方式创建入职单(生成公开链接)
- * POST /hr/ess/admin/onboardings/invitations?validDays=N
- *
- * @param {object} payload 入职邀请字段
- * @param {number} validDays 链接有效天数
- * @returns {Promise<{ invitationId: string, publicToken: string, publicPath: string, onboardingId: string|null }>}
- */
- export async function createOnboardingInvitation(payload, validDays) {
- // 邀请模式下姓名/手机/邮箱/身份证允许为空,后端字段照常映射即可
- const body = serializeOnboardingManual(payload)
- if (!isServerMode()) {
- await new Promise((r) => setTimeout(r, 250))
- const token = `TKN${Date.now().toString(36)}`
- return {
- invitationId: `INV${Date.now()}`,
- publicToken: token,
- publicPath: `/onboarding/public/${token}`,
- onboardingId: null,
- }
- }
- // validDays 走 query string(与后端约定一致),不放 body
- const qs = validDays != null && validDays !== '' ? `?validDays=${encodeURIComponent(Number(validDays))}` : ''
- const path = `${serverPath()}/hr/ess/admin/onboardings/invitations${qs}`
- const res = await request({ url: path, method: 'POST', data: body })
- return res.data || {}
- }
- /**
- * 入职记录列表
- * GET /hr/ess/admin/onboardings/records
- *
- * @returns {Promise<Array>}
- */
- export async function getOnboardingRecords() {
- if (!isServerMode()) {
- await new Promise((r) => setTimeout(r, 200))
- return []
- }
- const path = `${serverPath()}/hr/ess/admin/onboardings/records`
- const res = await request({ url: path, method: 'GET' })
- return Array.isArray(res.data) ? res.data : []
- }
- /**
- * 入职邀请记录列表(演示版回退到 mock 默认邀请)
- * GET /hr/ess/admin/onboardings/invitations
- *
- * @returns {Promise<Array>}
- */
- export async function getOnboardingInvitationsList() {
- if (!isServerMode()) {
- await new Promise((r) => setTimeout(r, 150))
- return defaultOnboardingInvitations
- }
- const path = `${serverPath()}/hr/ess/admin/onboardings/invitations`
- const res = await request({ url: path, method: 'GET' })
- return Array.isArray(res.data) ? res.data : []
- }
- /**
- * 提交入职审批
- * POST /hr/ess/admin/onboardings/records/{id}/submit
- *
- * @param {string} onboardingId
- * @returns {Promise<object>}
- */
- export async function submitOnboardingRecord(onboardingId) {
- if (!onboardingId) throw new Error('缺少入职单主键')
- if (!isServerMode()) {
- await new Promise((r) => setTimeout(r, 200))
- return { code: 0, message: '演示:已提交审批' }
- }
- const path = `${serverPath()}/hr/ess/admin/onboardings/records/${encodeURIComponent(onboardingId)}/submit`
- const res = await request({ url: path, method: 'POST' })
- return res
- }
|