export const APPLY_KINDS = [ { kind: 'expense', title: '报销申请', group: '费用', hint: '一张报销单可添加多张发票', multiInvoice: true, icon: 'receipt_long' }, { kind: 'loan', title: '借支申请', group: '资金', hint: '因公业务需向公司预支资金', icon: 'payments', fields: [ { key: 'amount', label: '借支金额', type: 'money', placeholder: '请输入借支金额(元)' }, { key: 'costType', label: '费用归属', type: 'select', options: ['投标费用', '正式项目', '部门费用', '行政'] }, { key: 'project', label: '关联项目', type: 'text', placeholder: '投标案件或正式项目名称' }, { key: 'days', label: '预计归还期限', type: 'text', placeholder: '例如:30 天' }, { key: 'reason', label: '借支用途', type: 'textarea', placeholder: '请说明借支用途' } ] }, { kind: 'leave', title: '请假申请', group: '休假', hint: '年假、事假、病假等', icon: 'date_range', fields: [ { key: 'leaveType', label: '假期类型', type: 'select', options: ['年假', '事假', '病假', '婚假', '产假/陪产假', '调休'] }, { key: 'start', label: '开始时间', type: 'datetime' }, { key: 'end', label: '结束时间', type: 'datetime' }, { key: 'days', label: '请假时长(天)', type: 'number', placeholder: '0.5 / 1 / 2' }, { key: 'reason', label: '请假事由', type: 'textarea', placeholder: '请说明请假原因' } ] }, { kind: 'trip', title: '出差申请', group: '差旅', hint: '跨城市差旅报备', icon: 'flight_takeoff', fields: [ { key: 'city', label: '出差地点', type: 'text', placeholder: '例如:上海 · 浦东' }, { key: 'start', label: '出发日期', type: 'date' }, { key: 'end', label: '返回日期', type: 'date' }, { key: 'reason', label: '出差事由', type: 'textarea' } ] }, { kind: 'procurement', title: '采购申请', group: '采购', hint: '设备、物资、服务采购', icon: 'shopping_cart', fields: [ { key: 'goods', label: '采购内容', type: 'text' }, { key: 'amount', label: '预算金额', type: 'money' }, { key: 'needDate', label: '期望到货日期', type: 'date' }, { key: 'supplier', label: '意向供应商', type: 'text', optional: true }, { key: 'reason', label: '采购理由', type: 'textarea' } ] }, { kind: 'overtime', title: '加班申请', group: '考勤', hint: '加班时长申报', icon: 'schedule', fields: [ { key: 'date', label: '加班日期', type: 'date' }, { key: 'hours', label: '加班时长(小时)', type: 'number' }, { key: 'reason', label: '申请说明', type: 'textarea' } ] }, { kind: 'outing', title: '外出申请', group: '考勤', hint: '工作期间临时外出', icon: 'near_me', fields: [ { key: 'place', label: '外出地点', type: 'text' }, { key: 'start', label: '开始时间', type: 'datetime' }, { key: 'end', label: '结束时间', type: 'datetime' }, { key: 'reason', label: '申请说明', type: 'textarea' } ] }, { kind: 'card', title: '补卡申请', group: '考勤', hint: '漏打卡补卡说明', icon: 'edit_calendar', fields: [ { key: 'punchDate', label: '补卡日期', type: 'date' }, { key: 'punchKind', label: '补卡类型', type: 'select', options: ['上班', '下班'] }, { key: 'reason', label: '申请说明', type: 'textarea' } ] }, { kind: 'transfer', title: '转岗申请', group: '人事', hint: '岗位 / 部门调动', icon: 'swap_horiz', fields: [ { key: 'fromDept', label: '原部门', type: 'text' }, { key: 'toDept', label: '拟调入部门', type: 'text' }, { key: 'newTitle', label: '拟任岗位', type: 'text', optional: true }, { key: 'reason', label: '申请说明', type: 'textarea' } ] }, { kind: 'regularize', title: '转正申请', group: '人事', hint: '实习/试用期满转正', onlyRegularize: true, icon: 'how_to_reg', fields: [ { key: 'currentStatus', label: '当前身份', type: 'select', options: ['实习', '试用'] }, { key: 'hiredAt', label: '入职日期', type: 'date' }, { key: 'summary', label: '试用/实习工作总结', type: 'textarea' }, { key: 'nextPlan', label: '转正后工作计划', type: 'textarea' }, { key: 'reason', label: '转正说明', type: 'textarea' } ] }, { kind: 'resign', title: '离职申请', group: '人事', hint: '本人提前申请离职', icon: 'logout', fields: [ { key: 'lastDay', label: '最后工作日', type: 'date' }, { key: 'reason', label: '离职原因', type: 'textarea' } ] }, { kind: 'layoff', title: '裁员', group: '人事', hint: '人事或总监发起', onlyLayoff: true, icon: 'person_off', fields: [ { key: 'targetUsername', label: '被裁员工', type: 'select' }, { key: 'lastDay', label: '最后工作日', type: 'date' }, { key: 'reason', label: '裁员说明', type: 'textarea' } ] }, { kind: 'seal', title: '用章申请', group: '印章', hint: '选择鲁兵或风影', icon: 'approval', fields: [ { key: 'sealCompany', label: '用章主体', type: 'select', options: ['鲁兵', '风影'] }, { key: 'sealType', label: '申请印章', type: 'select', options: ['法人章', '公章', '法人章和公章'] }, { key: 'reason', label: '申请说明', type: 'textarea' } ] }, { kind: 'receive', title: '领用申请', group: '物资', hint: '办公物资与耗材领用', icon: 'inventory_2', fields: [ { key: 'goods', label: '领用物品', type: 'text' }, { key: 'qty', label: '数量', type: 'text' }, { key: 'reason', label: '申请说明', type: 'textarea' } ] } ] export function applyKindById(kind) { return APPLY_KINDS.find((k) => k.kind === kind) || null } export function isFixedChain(kind) { return kind === 'expense' || kind === 'loan' || kind === 'procurement' || kind === 'seal' } export function canApplyRegularize(status) { const s = String(status || '').trim() return s === '实习' || s === '试用' || s === 'INTERN' || s === 'PROBATION' } export function canApplyLayoff({ isSuper, role }) { if (isSuper) return true const r = String(role || '') return r === '企业负责人' || r.includes('总监') || r.includes('负责人') } export function visibleApplyKinds({ employeeStatus, isSuper, role }) { return APPLY_KINDS.filter((t) => { if (t.onlyRegularize && !canApplyRegularize(employeeStatus)) return false if (t.onlyLayoff && !canApplyLayoff({ isSuper, role })) return false return true }) } export function groupedApplyKinds(kinds) { const groups = [] const map = {} kinds.forEach((k) => { if (!map[k.group]) { map[k.group] = { category: k.group, items: [] } groups.push(map[k.group]) } map[k.group].items.push(k) }) return groups } export function buildApplyPayload({ spec, title, values, invoices, expenseRemark, approverUsername, suggestedUsername, me, layoffTargetName, attachments }) { const form = { reason: values.reason || '', ...values } if (Array.isArray(attachments) && attachments.length) form.attachments = attachments let amount = null const moneyField = (spec.fields || []).find((f) => f.type === 'money' && !f.optional) if (moneyField) amount = Number(values[moneyField.key]) || null if (spec.kind === 'resign' && me) { form.targetUsername = me.username form.targetName = me.name } if (spec.kind === 'layoff') form.targetName = layoffTargetName || '' if (spec.multiInvoice) { const rows = (invoices || []).map((r, i) => ({ seq: i + 1, ticketType: r.ticketType, amount: Number(r.amount) || 0, invoiceNo: r.invoiceNo || '', date: r.date || '', note: r.note || '', files: Array.isArray(r.files) ? r.files : [], proxyPay: r.proxyPay === true, proxyPayee: r.proxyPayee || '', proxyPayeeName: r.proxyPayeeName || '' })) amount = rows.reduce((s, r) => s + r.amount, 0) form.remark = expenseRemark || '' form.reason = expenseRemark || rows.map((r) => r.note).filter(Boolean).join(';') form.invoices = rows form.receipts = rows.length } const approver = isFixedChain(spec.kind) ? (suggestedUsername || approverUsername) : approverUsername return { type: spec.kind, typeLabel: spec.title, title: String(title || '').trim(), amount: amount && amount > 0 ? amount : null, reason: form.reason || '', approverUsername: approver, form } }