Files
2026-09-23 16:52:48 +08:00

202 lines
8.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}
}