Merge branch 'main' of http://pms.dtyx.net:3000/wuxueyu/Industrial-image-management-system---web
This commit is contained in:
commit
30eca5da60
|
@ -0,0 +1,242 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:visible="visible"
|
||||||
|
@update:visible="val => emit('update:visible', val)"
|
||||||
|
:title="title"
|
||||||
|
:width="900"
|
||||||
|
:footer="true"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<a-descriptions :data="descriptionsData" bordered :column="2">
|
||||||
|
<a-descriptions-item label="员工姓名">{{ data?.employeeName }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="员工类型">{{ employeeTypeText }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="身份证号">{{ data?.idCard }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="联系电话">{{ data?.phone }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="银行卡号">{{ data?.bankCard }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="开户银行">{{ data?.bankName }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="项目名称">{{ data?.projectName }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="项目周期">{{ data?.projectPeriod }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="工作天数">{{ data?.workDays }}天</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="海上作业天数">{{ data?.offshoreDays }}天</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
|
||||||
|
<a-divider orientation="left">薪酬明细</a-divider>
|
||||||
|
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-statistic
|
||||||
|
title="基本工资"
|
||||||
|
:value="data?.baseSalary"
|
||||||
|
:precision="2"
|
||||||
|
:value-style="{ color: '#3f8600' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-statistic
|
||||||
|
title="绩效奖金"
|
||||||
|
:value="data?.performanceBonus"
|
||||||
|
:precision="2"
|
||||||
|
:value-style="{ color: '#3f8600' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row :gutter="16" style="margin-top: 16px">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-statistic
|
||||||
|
title="各类补助"
|
||||||
|
:value="data?.allowances"
|
||||||
|
:precision="2"
|
||||||
|
:value-style="{ color: '#3f8600' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-statistic
|
||||||
|
title="应发总额"
|
||||||
|
:value="data?.totalPayable"
|
||||||
|
:precision="2"
|
||||||
|
:value-style="{ color: '#1890ff' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row :gutter="16" style="margin-top: 16px">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-statistic
|
||||||
|
title="扣款金额"
|
||||||
|
:value="data?.deductions"
|
||||||
|
:precision="2"
|
||||||
|
:value-style="{ color: '#ff4d4f' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-statistic
|
||||||
|
title="实发金额"
|
||||||
|
:value="data?.netPay"
|
||||||
|
:precision="2"
|
||||||
|
:value-style="{ color: '#52c41a', fontSize: '24px', fontWeight: 'bold' }"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-divider orientation="left">绩效评价</a-divider>
|
||||||
|
<a-table :data="data?.performanceItems" :columns="performanceColumns" :pagination="false">
|
||||||
|
<template #score="{ record }">
|
||||||
|
<a-tag :color="getScoreColor(record.score, record.maxScore)">
|
||||||
|
{{ record.score }}/{{ record.maxScore }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
<template #amount="{ record }">
|
||||||
|
¥{{ (record.score / record.maxScore * record.amount).toFixed(2) }}
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
|
||||||
|
<a-divider orientation="left">补助明细</a-divider>
|
||||||
|
<a-table :data="data?.allowanceItems" :columns="allowanceColumns" :pagination="false">
|
||||||
|
<template #total="{ record }">
|
||||||
|
¥{{ record.total.toFixed(2) }}
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
|
||||||
|
<a-divider orientation="left">审批流程</a-divider>
|
||||||
|
<a-steps :current="approvalCurrent" direction="vertical">
|
||||||
|
<a-step
|
||||||
|
v-for="step in data?.approvalFlow"
|
||||||
|
:key="step.step"
|
||||||
|
:title="step.step"
|
||||||
|
:description="`${step.role} - ${step.approver}`"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<icon-check-circle v-if="step.status === 'approved'" style="color: #52c41a" />
|
||||||
|
<icon-close-circle v-else-if="step.status === 'rejected'" style="color: #ff4d4f" />
|
||||||
|
<icon-clock-circle v-else style="color: #faad14" />
|
||||||
|
</template>
|
||||||
|
</a-step>
|
||||||
|
</a-steps>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="handleCancel">关闭</a-button>
|
||||||
|
<a-button type="primary" @click="handleExport">
|
||||||
|
<template #icon><icon-download /></template>
|
||||||
|
导出Excel
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import type { SalaryRecord } from '../types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean
|
||||||
|
data: SalaryRecord | null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:visible', visible: boolean): void
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
const title = computed(() => `${props.data?.employeeName} - 工资详情`)
|
||||||
|
|
||||||
|
const employeeTypeText = computed(() => {
|
||||||
|
const typeMap = {
|
||||||
|
intern: '实习生',
|
||||||
|
fulltime: '正式员工',
|
||||||
|
parttime: '兼职员工',
|
||||||
|
}
|
||||||
|
return props.data?.employeeType ? typeMap[props.data.employeeType] : ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const approvalCurrent = computed(() => {
|
||||||
|
if (!props.data?.approvalFlow) return 0
|
||||||
|
const approvedSteps = props.data.approvalFlow.filter((step) => step.status === 'approved').length
|
||||||
|
return approvedSteps
|
||||||
|
})
|
||||||
|
|
||||||
|
const descriptionsData = computed(() => [
|
||||||
|
{ label: '员工姓名', value: props.data?.employeeName },
|
||||||
|
{ label: '员工类型', value: employeeTypeText.value },
|
||||||
|
{ label: '身份证号', value: props.data?.idCard },
|
||||||
|
{ label: '联系电话', value: props.data?.phone },
|
||||||
|
{ label: '银行卡号', value: props.data?.bankCard },
|
||||||
|
{ label: '开户银行', value: props.data?.bankName },
|
||||||
|
{ label: '项目名称', value: props.data?.projectName },
|
||||||
|
{ label: '项目周期', value: props.data?.projectPeriod },
|
||||||
|
{ label: '工作天数', value: `${props.data?.workDays}天` },
|
||||||
|
{ label: '海上作业天数', value: `${props.data?.offshoreDays}天` },
|
||||||
|
])
|
||||||
|
|
||||||
|
const performanceColumns = [
|
||||||
|
{ title: '评价项', dataIndex: 'name', width: 200 },
|
||||||
|
{ title: '权重', dataIndex: 'weight', width: 80 },
|
||||||
|
{ title: '得分', dataIndex: 'score', slotName: 'score', width: 100 },
|
||||||
|
{ title: '满分', dataIndex: 'maxScore', width: 80 },
|
||||||
|
{ title: '金额', dataIndex: 'amount', slotName: 'amount', width: 100 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const allowanceColumns = [
|
||||||
|
{ title: '补助类型', dataIndex: 'name', width: 200 },
|
||||||
|
{ title: '标准', dataIndex: 'amount', width: 100 },
|
||||||
|
{ title: '数量', dataIndex: 'quantity', width: 100 },
|
||||||
|
{ title: '小计', dataIndex: 'total', slotName: 'total', width: 100 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const getScoreColor = (score: number, maxScore: number) => {
|
||||||
|
const ratio = score / maxScore
|
||||||
|
if (ratio >= 0.9) return 'green'
|
||||||
|
if (ratio >= 0.7) return 'blue'
|
||||||
|
if (ratio >= 0.6) return 'orange'
|
||||||
|
return 'red'
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('update:visible', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleExport = () => {
|
||||||
|
if (!props.data) return
|
||||||
|
|
||||||
|
// 构建导出数据
|
||||||
|
const exportData = {
|
||||||
|
employee: {
|
||||||
|
name: props.data.employeeName,
|
||||||
|
type: employeeTypeText.value,
|
||||||
|
idCard: props.data.idCard,
|
||||||
|
phone: props.data.phone,
|
||||||
|
bankCard: props.data.bankCard,
|
||||||
|
bankName: props.data.bankName,
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
name: props.data.projectName,
|
||||||
|
period: props.data.projectPeriod,
|
||||||
|
workDays: props.data.workDays,
|
||||||
|
offshoreDays: props.data.offshoreDays,
|
||||||
|
},
|
||||||
|
salary: {
|
||||||
|
baseSalary: props.data.baseSalary,
|
||||||
|
performanceBonus: props.data.performanceBonus,
|
||||||
|
allowances: props.data.allowances,
|
||||||
|
totalPayable: props.data.totalPayable,
|
||||||
|
deductions: props.data.deductions,
|
||||||
|
netPay: props.data.netPay,
|
||||||
|
},
|
||||||
|
performanceItems: props.data.performanceItems,
|
||||||
|
allowanceItems: props.data.allowanceItems,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发下载
|
||||||
|
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' })
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = url
|
||||||
|
a.download = `${props.data.employeeName}_工资单_${props.data.projectPeriod}.json`
|
||||||
|
a.click()
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,468 @@
|
||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
:visible="visible"
|
||||||
|
@update:visible="val => emit('update:visible', val)"
|
||||||
|
:title="title"
|
||||||
|
:width="800"
|
||||||
|
:footer="true"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<a-form ref="formRef" :model="formData" :rules="rules" layout="vertical">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<a-card title="基本信息" :bordered="false">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="员工姓名" field="employeeName">
|
||||||
|
<a-input v-model="formData.employeeName" placeholder="请输入员工姓名" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="员工类型" field="employeeType">
|
||||||
|
<a-select v-model="formData.employeeType" placeholder="请选择员工类型">
|
||||||
|
<a-option value="intern">实习生</a-option>
|
||||||
|
<a-option value="fulltime">正式员工</a-option>
|
||||||
|
<a-option value="parttime">兼职员工</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="身份证号" field="idCard">
|
||||||
|
<a-input v-model="formData.idCard" placeholder="请输入身份证号" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="联系电话" field="phone">
|
||||||
|
<a-input v-model="formData.phone" placeholder="请输入联系电话" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="银行卡号" field="bankCard">
|
||||||
|
<a-input v-model="formData.bankCard" placeholder="请输入银行卡号" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="开户银行" field="bankName">
|
||||||
|
<a-input v-model="formData.bankName" placeholder="请输入开户银行" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 项目信息 -->
|
||||||
|
<a-card title="项目信息" :bordered="false" style="margin-top: 16px">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="项目名称" field="projectName">
|
||||||
|
<a-input v-model="formData.projectName" placeholder="请输入项目名称" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="项目周期" field="projectPeriod">
|
||||||
|
<a-input v-model="formData.projectPeriod" placeholder="如:2025-07" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="工作天数" field="workDays">
|
||||||
|
<a-input-number
|
||||||
|
v-model="formData.workDays"
|
||||||
|
:min="0"
|
||||||
|
:precision="0"
|
||||||
|
placeholder="请输入工作天数"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="海上作业天数" field="offshoreDays">
|
||||||
|
<a-input-number
|
||||||
|
v-model="formData.offshoreDays"
|
||||||
|
:min="0"
|
||||||
|
:precision="0"
|
||||||
|
placeholder="请输入海上作业天数"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 薪酬计算 -->
|
||||||
|
<a-card title="薪酬计算" :bordered="false" style="margin-top: 16px">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="基本工资标准" field="baseSalaryStandard">
|
||||||
|
<a-input-number
|
||||||
|
v-model="formData.baseSalaryStandard"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
placeholder="请输入基本工资标准"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="基本工资" field="baseSalary">
|
||||||
|
<a-input-number
|
||||||
|
v-model="formData.baseSalary"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
placeholder="自动计算"
|
||||||
|
style="width: 100%"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<!-- 绩效评价 -->
|
||||||
|
<a-divider orientation="left">绩效评价</a-divider>
|
||||||
|
<a-table :data="formData.performanceItems" :columns="performanceColumns" :pagination="false">
|
||||||
|
<template #score="{ record, rowIndex }">
|
||||||
|
<a-input-number
|
||||||
|
v-model="record.score"
|
||||||
|
:min="0"
|
||||||
|
:max="record.maxScore"
|
||||||
|
:precision="0"
|
||||||
|
@change="calculatePerformance"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #amount="{ record }">
|
||||||
|
{{ record.amount }}
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
|
||||||
|
<!-- 补助明细 -->
|
||||||
|
<a-divider orientation="left">补助明细</a-divider>
|
||||||
|
<a-table :data="formData.allowanceItems" :columns="allowanceColumns" :pagination="false">
|
||||||
|
<template #quantity="{ record, rowIndex }">
|
||||||
|
<a-input-number
|
||||||
|
v-model="record.quantity"
|
||||||
|
:min="0"
|
||||||
|
:precision="0"
|
||||||
|
@change="calculateAllowances"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #total="{ record }">
|
||||||
|
{{ record.total }}
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
|
||||||
|
<!-- 计算结果 -->
|
||||||
|
<a-divider orientation="left">计算结果</a-divider>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-form-item label="应发总额">
|
||||||
|
<a-input-number
|
||||||
|
v-model="formData.totalPayable"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
readonly
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-form-item label="扣款金额">
|
||||||
|
<a-input-number
|
||||||
|
v-model="formData.deductions"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
placeholder="请输入扣款金额"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-form-item label="实发金额">
|
||||||
|
<a-input-number
|
||||||
|
v-model="formData.netPay"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
readonly
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="handleCancel">取消</a-button>
|
||||||
|
<a-button type="primary" @click="handleSubmit">保存</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, computed, watch } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import type { SalaryRecord, AllowanceItem, PerformanceItem } from '../types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean
|
||||||
|
data: SalaryRecord | null
|
||||||
|
mode: 'add' | 'edit'
|
||||||
|
}
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:visible', visible: boolean): void
|
||||||
|
(e: 'success'): void
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
const formData = reactive<SalaryRecord>({
|
||||||
|
id: '',
|
||||||
|
employeeId: '',
|
||||||
|
employeeName: '',
|
||||||
|
employeeType: 'intern',
|
||||||
|
idCard: '',
|
||||||
|
phone: '',
|
||||||
|
bankCard: '',
|
||||||
|
bankName: '',
|
||||||
|
projectName: '',
|
||||||
|
projectPeriod: '',
|
||||||
|
workDays: 0,
|
||||||
|
offshoreDays: 0,
|
||||||
|
baseSalary: 0,
|
||||||
|
baseSalaryStandard: 3500,
|
||||||
|
performanceBonus: 0,
|
||||||
|
allowances: 0,
|
||||||
|
performanceItems: [],
|
||||||
|
allowanceItems: [],
|
||||||
|
totalPayable: 0,
|
||||||
|
deductions: 0,
|
||||||
|
netPay: 0,
|
||||||
|
status: 'draft',
|
||||||
|
approvalFlow: [],
|
||||||
|
salaryMonth: '',
|
||||||
|
createdAt: '',
|
||||||
|
updatedAt: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const performanceColumns = [
|
||||||
|
{ title: '评价项', dataIndex: 'name', width: 200 },
|
||||||
|
{ title: '权重', dataIndex: 'weight', width: 80 },
|
||||||
|
{ title: '得分', dataIndex: 'score', slotName: 'score', width: 100 },
|
||||||
|
{ title: '满分', dataIndex: 'maxScore', width: 80 },
|
||||||
|
{ title: '金额', dataIndex: 'amount', slotName: 'amount', width: 100 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const allowanceColumns = [
|
||||||
|
{ title: '补助类型', dataIndex: 'name', width: 200 },
|
||||||
|
{ title: '标准', dataIndex: 'amount', width: 100 },
|
||||||
|
{ title: '单位', dataIndex: 'unit', width: 80 },
|
||||||
|
{ title: '数量', dataIndex: 'quantity', slotName: 'quantity', width: 100 },
|
||||||
|
{ title: '小计', dataIndex: 'total', slotName: 'total', width: 100 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const title = computed(() => (props.mode === 'add' ? '新建工资单' : '编辑工资单'))
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
employeeName: [{ required: true, message: '请输入员工姓名' }],
|
||||||
|
employeeType: [{ required: true, message: '请选择员工类型' }],
|
||||||
|
idCard: [{ required: true, message: '请输入身份证号' }],
|
||||||
|
phone: [{ required: true, message: '请输入联系电话' }],
|
||||||
|
projectName: [{ required: true, message: '请输入项目名称' }],
|
||||||
|
workDays: [{ required: true, message: '请输入工作天数' }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化实习生默认配置
|
||||||
|
const initInternConfig = () => {
|
||||||
|
formData.performanceItems = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
name: '平时沟通态度',
|
||||||
|
weight: 30,
|
||||||
|
score: 0,
|
||||||
|
maxScore: 100,
|
||||||
|
amount: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
name: '积极主动性',
|
||||||
|
weight: 50,
|
||||||
|
score: 0,
|
||||||
|
maxScore: 100,
|
||||||
|
amount: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
name: '成长性心态',
|
||||||
|
weight: 20,
|
||||||
|
score: 0,
|
||||||
|
maxScore: 100,
|
||||||
|
amount: 100,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
formData.allowanceItems = [
|
||||||
|
{
|
||||||
|
type: 'construction',
|
||||||
|
name: '施工绩效',
|
||||||
|
amount: 100,
|
||||||
|
unit: 'day',
|
||||||
|
quantity: 0,
|
||||||
|
total: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'offshore',
|
||||||
|
name: '海上作业补助',
|
||||||
|
amount: 30,
|
||||||
|
unit: 'day',
|
||||||
|
quantity: 0,
|
||||||
|
total: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'meal',
|
||||||
|
name: '务餐补助',
|
||||||
|
amount: 45,
|
||||||
|
unit: 'day',
|
||||||
|
quantity: 0,
|
||||||
|
total: 0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算基本工资
|
||||||
|
const calculateBaseSalary = () => {
|
||||||
|
formData.baseSalary = (formData.baseSalaryStandard / 30) * formData.workDays
|
||||||
|
calculateTotal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算绩效奖金
|
||||||
|
const calculatePerformance = () => {
|
||||||
|
formData.performanceBonus = formData.performanceItems.reduce(
|
||||||
|
(sum, item) => sum + (item.score / item.maxScore) * item.amount,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
calculateTotal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算补助
|
||||||
|
const calculateAllowances = () => {
|
||||||
|
formData.allowanceItems.forEach((item) => {
|
||||||
|
item.total = item.amount * item.quantity
|
||||||
|
})
|
||||||
|
formData.allowances = formData.allowanceItems.reduce((sum, item) => sum + item.total, 0)
|
||||||
|
calculateTotal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算总额
|
||||||
|
const calculateTotal = () => {
|
||||||
|
formData.totalPayable = formData.baseSalary + formData.performanceBonus + formData.allowances
|
||||||
|
formData.netPay = formData.totalPayable - formData.deductions
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听工作天数变化
|
||||||
|
watch(
|
||||||
|
() => formData.workDays,
|
||||||
|
() => {
|
||||||
|
calculateBaseSalary()
|
||||||
|
// 更新务餐补助数量
|
||||||
|
const mealItem = formData.allowanceItems.find((item) => item.type === 'meal')
|
||||||
|
if (mealItem) {
|
||||||
|
mealItem.quantity = formData.workDays
|
||||||
|
calculateAllowances()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听海上作业天数变化
|
||||||
|
watch(
|
||||||
|
() => formData.offshoreDays,
|
||||||
|
() => {
|
||||||
|
// 更新海上作业补助数量
|
||||||
|
const offshoreItem = formData.allowanceItems.find((item) => item.type === 'offshore')
|
||||||
|
if (offshoreItem) {
|
||||||
|
offshoreItem.quantity = formData.offshoreDays
|
||||||
|
calculateAllowances()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听扣款变化
|
||||||
|
watch(() => formData.deductions, calculateTotal)
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('update:visible', false)
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
await formRef.value?.validate()
|
||||||
|
// 这里调用API保存数据
|
||||||
|
Message.success('保存成功')
|
||||||
|
emit('success')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('表单验证失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听数据变化
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(newData) => {
|
||||||
|
if (newData) {
|
||||||
|
Object.assign(formData, newData)
|
||||||
|
} else {
|
||||||
|
// 重置表单
|
||||||
|
Object.assign(formData, {
|
||||||
|
id: '',
|
||||||
|
employeeId: '',
|
||||||
|
employeeName: '',
|
||||||
|
employeeType: 'intern',
|
||||||
|
idCard: '',
|
||||||
|
phone: '',
|
||||||
|
bankCard: '',
|
||||||
|
bankName: '',
|
||||||
|
projectName: '',
|
||||||
|
projectPeriod: '',
|
||||||
|
workDays: 0,
|
||||||
|
offshoreDays: 0,
|
||||||
|
baseSalary: 0,
|
||||||
|
baseSalaryStandard: 3500,
|
||||||
|
performanceBonus: 0,
|
||||||
|
allowances: 0,
|
||||||
|
performanceItems: [],
|
||||||
|
allowanceItems: [],
|
||||||
|
totalPayable: 0,
|
||||||
|
deductions: 0,
|
||||||
|
netPay: 0,
|
||||||
|
status: 'draft',
|
||||||
|
approvalFlow: [],
|
||||||
|
salaryMonth: '',
|
||||||
|
createdAt: '',
|
||||||
|
updatedAt: '',
|
||||||
|
})
|
||||||
|
initInternConfig()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听员工类型变化
|
||||||
|
watch(
|
||||||
|
() => formData.employeeType,
|
||||||
|
(newType) => {
|
||||||
|
if (newType === 'intern') {
|
||||||
|
initInternConfig()
|
||||||
|
} else {
|
||||||
|
// 其他员工类型的配置
|
||||||
|
formData.performanceItems = []
|
||||||
|
formData.allowanceItems = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
</script>
|
|
@ -0,0 +1,127 @@
|
||||||
|
// 员工类型
|
||||||
|
export type EmployeeType = 'intern' | 'fulltime' | 'parttime'
|
||||||
|
|
||||||
|
// 工资单状态
|
||||||
|
export type SalaryStatus = 'draft' | 'pending' | 'approved' | 'rejected' | 'paid'
|
||||||
|
|
||||||
|
// 补助类型
|
||||||
|
export interface AllowanceItem {
|
||||||
|
type: string
|
||||||
|
name: string
|
||||||
|
amount: number
|
||||||
|
unit: 'day' | 'project' | 'fixed'
|
||||||
|
quantity: number
|
||||||
|
total: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绩效评价项
|
||||||
|
export interface PerformanceItem {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
weight: number
|
||||||
|
score: number
|
||||||
|
maxScore: number
|
||||||
|
amount: number
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工资记录
|
||||||
|
export interface SalaryRecord {
|
||||||
|
id: string
|
||||||
|
employeeId: string
|
||||||
|
employeeName: string
|
||||||
|
employeeType: EmployeeType
|
||||||
|
idCard: string
|
||||||
|
phone: string
|
||||||
|
bankCard: string
|
||||||
|
bankName: string
|
||||||
|
|
||||||
|
// 项目信息
|
||||||
|
projectName: string
|
||||||
|
projectPeriod: string
|
||||||
|
workDays: number
|
||||||
|
offshoreDays: number
|
||||||
|
|
||||||
|
// 薪酬结构
|
||||||
|
baseSalary: number
|
||||||
|
baseSalaryStandard: number
|
||||||
|
performanceBonus: number
|
||||||
|
allowances: number
|
||||||
|
performanceItems: PerformanceItem[]
|
||||||
|
allowanceItems: AllowanceItem[]
|
||||||
|
|
||||||
|
// 计算结果
|
||||||
|
totalPayable: number
|
||||||
|
deductions: number
|
||||||
|
netPay: number
|
||||||
|
|
||||||
|
// 状态
|
||||||
|
status: SalaryStatus
|
||||||
|
approvalFlow: ApprovalStep[]
|
||||||
|
|
||||||
|
// 时间
|
||||||
|
salaryMonth: string
|
||||||
|
createdAt: string
|
||||||
|
updatedAt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 审批步骤
|
||||||
|
export interface ApprovalStep {
|
||||||
|
step: string
|
||||||
|
role: string
|
||||||
|
approver: string
|
||||||
|
status: 'pending' | 'approved' | 'rejected'
|
||||||
|
comment?: string
|
||||||
|
date?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询参数
|
||||||
|
export interface SalaryQuery {
|
||||||
|
keyword?: string
|
||||||
|
employeeType?: EmployeeType
|
||||||
|
status?: SalaryStatus
|
||||||
|
dateRange?: string[]
|
||||||
|
projectName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 实习生专用配置
|
||||||
|
export interface InternConfig {
|
||||||
|
baseSalaryStandard: {
|
||||||
|
withCertification: number
|
||||||
|
withoutCertification: number
|
||||||
|
}
|
||||||
|
performanceRates: {
|
||||||
|
construction: number
|
||||||
|
offshore: number
|
||||||
|
}
|
||||||
|
allowances: {
|
||||||
|
offshoreCert: number
|
||||||
|
towerWork: {
|
||||||
|
land: {
|
||||||
|
bladeInspection: number
|
||||||
|
lightning: number
|
||||||
|
}
|
||||||
|
sea: {
|
||||||
|
bladeInspection: number
|
||||||
|
lightning: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
droneWork: number
|
||||||
|
meal: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工资单创建请求
|
||||||
|
export interface SalaryCreateRequest {
|
||||||
|
employeeId: string
|
||||||
|
projectId: string
|
||||||
|
workDays: number
|
||||||
|
offshoreDays: number
|
||||||
|
performanceItems: Omit<PerformanceItem, 'id'>[]
|
||||||
|
allowanceItems: Omit<AllowanceItem, 'total'>[]
|
||||||
|
deductions?: {
|
||||||
|
type: string
|
||||||
|
amount: number
|
||||||
|
reason: string
|
||||||
|
}[]
|
||||||
|
}
|
|
@ -0,0 +1,255 @@
|
||||||
|
import * as XLSX from 'xlsx'
|
||||||
|
import type { SalaryRecord } from '../types'
|
||||||
|
|
||||||
|
// 导出工资单到Excel
|
||||||
|
export const exportSalaryToExcel = (records: SalaryRecord[], filename: string) => {
|
||||||
|
// 创建工作簿
|
||||||
|
const workbook = XLSX.utils.book_new()
|
||||||
|
|
||||||
|
// 主表数据
|
||||||
|
const mainData = records.map((record) => ({
|
||||||
|
员工姓名: record.employeeName,
|
||||||
|
员工类型: getEmployeeTypeText(record.employeeType),
|
||||||
|
身份证号: record.idCard,
|
||||||
|
联系电话: record.phone,
|
||||||
|
银行卡号: record.bankCard,
|
||||||
|
开户银行: record.bankName,
|
||||||
|
项目名称: record.projectName,
|
||||||
|
项目周期: record.projectPeriod,
|
||||||
|
工作天数: record.workDays,
|
||||||
|
海上作业天数: record.offshoreDays,
|
||||||
|
基本工资: record.baseSalary,
|
||||||
|
绩效奖金: record.performanceBonus,
|
||||||
|
各类补助: record.allowances,
|
||||||
|
应发总额: record.totalPayable,
|
||||||
|
扣款金额: record.deductions,
|
||||||
|
实发金额: record.netPay,
|
||||||
|
状态: getStatusText(record.status),
|
||||||
|
创建时间: record.createdAt,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 创建主表
|
||||||
|
const mainSheet = XLSX.utils.json_to_sheet(mainData)
|
||||||
|
XLSX.utils.book_append_sheet(workbook, mainSheet, '工资汇总')
|
||||||
|
|
||||||
|
// 绩效明细表
|
||||||
|
const performanceData: any[] = []
|
||||||
|
records.forEach((record) => {
|
||||||
|
record.performanceItems.forEach((item) => {
|
||||||
|
performanceData.push({
|
||||||
|
员工姓名: record.employeeName,
|
||||||
|
项目名称: record.projectName,
|
||||||
|
评价项: item.name,
|
||||||
|
权重: item.weight,
|
||||||
|
得分: item.score,
|
||||||
|
满分: item.maxScore,
|
||||||
|
金额: (item.score / item.maxScore * item.amount).toFixed(2),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (performanceData.length > 0) {
|
||||||
|
const performanceSheet = XLSX.utils.json_to_sheet(performanceData)
|
||||||
|
XLSX.utils.book_append_sheet(workbook, performanceSheet, '绩效明细')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 补助明细表
|
||||||
|
const allowanceData: any[] = []
|
||||||
|
records.forEach((record) => {
|
||||||
|
record.allowanceItems.forEach((item) => {
|
||||||
|
allowanceData.push({
|
||||||
|
员工姓名: record.employeeName,
|
||||||
|
项目名称: record.projectName,
|
||||||
|
补助类型: item.name,
|
||||||
|
标准: item.amount,
|
||||||
|
单位: getUnitText(item.unit),
|
||||||
|
数量: item.quantity,
|
||||||
|
小计: item.total,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (allowanceData.length > 0) {
|
||||||
|
const allowanceSheet = XLSX.utils.json_to_sheet(allowanceData)
|
||||||
|
XLSX.utils.book_append_sheet(workbook, allowanceSheet, '补助明细')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出文件
|
||||||
|
XLSX.writeFile(workbook, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出单个工资单详情
|
||||||
|
export const exportSingleSalaryToExcel = (record: SalaryRecord) => {
|
||||||
|
const workbook = XLSX.utils.book_new()
|
||||||
|
|
||||||
|
// 基本信息
|
||||||
|
const basicInfo = [{
|
||||||
|
项目: '员工姓名',
|
||||||
|
值: record.employeeName,
|
||||||
|
}, {
|
||||||
|
项目: '员工类型',
|
||||||
|
值: getEmployeeTypeText(record.employeeType),
|
||||||
|
}, {
|
||||||
|
项目: '身份证号',
|
||||||
|
值: record.idCard,
|
||||||
|
}, {
|
||||||
|
项目: '联系电话',
|
||||||
|
值: record.phone,
|
||||||
|
}, {
|
||||||
|
项目: '银行卡号',
|
||||||
|
值: record.bankCard,
|
||||||
|
}, {
|
||||||
|
项目: '开户银行',
|
||||||
|
值: record.bankName,
|
||||||
|
}, {
|
||||||
|
项目: '项目名称',
|
||||||
|
值: record.projectName,
|
||||||
|
}, {
|
||||||
|
项目: '项目周期',
|
||||||
|
值: record.projectPeriod,
|
||||||
|
}, {
|
||||||
|
项目: '工作天数',
|
||||||
|
值: record.workDays,
|
||||||
|
}, {
|
||||||
|
项目: '海上作业天数',
|
||||||
|
值: record.offshoreDays,
|
||||||
|
}]
|
||||||
|
|
||||||
|
const basicSheet = XLSX.utils.json_to_sheet(basicInfo)
|
||||||
|
XLSX.utils.book_append_sheet(workbook, basicSheet, '基本信息')
|
||||||
|
|
||||||
|
// 薪酬汇总
|
||||||
|
const salarySummary = [{
|
||||||
|
项目: '基本工资',
|
||||||
|
金额: record.baseSalary,
|
||||||
|
}, {
|
||||||
|
项目: '绩效奖金',
|
||||||
|
金额: record.performanceBonus,
|
||||||
|
}, {
|
||||||
|
项目: '各类补助',
|
||||||
|
金额: record.allowances,
|
||||||
|
}, {
|
||||||
|
项目: '应发总额',
|
||||||
|
金额: record.totalPayable,
|
||||||
|
}, {
|
||||||
|
项目: '扣款金额',
|
||||||
|
金额: record.deductions,
|
||||||
|
}, {
|
||||||
|
项目: '实发金额',
|
||||||
|
金额: record.netPay,
|
||||||
|
}]
|
||||||
|
|
||||||
|
const salarySheet = XLSX.utils.json_to_sheet(salarySummary)
|
||||||
|
XLSX.utils.book_append_sheet(workbook, salarySheet, '薪酬汇总')
|
||||||
|
|
||||||
|
// 绩效明细
|
||||||
|
if (record.performanceItems.length > 0) {
|
||||||
|
const performanceSheet = XLSX.utils.json_to_sheet(record.performanceItems.map((item) => ({
|
||||||
|
评价项: item.name,
|
||||||
|
权重: item.weight,
|
||||||
|
得分: item.score,
|
||||||
|
满分: item.maxScore,
|
||||||
|
金额: (item.score / item.maxScore * item.amount).toFixed(2),
|
||||||
|
})))
|
||||||
|
XLSX.utils.book_append_sheet(workbook, performanceSheet, '绩效明细')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 补助明细
|
||||||
|
if (record.allowanceItems.length > 0) {
|
||||||
|
const allowanceSheet = XLSX.utils.json_to_sheet(record.allowanceItems.map((item) => ({
|
||||||
|
补助类型: item.name,
|
||||||
|
标准: item.amount,
|
||||||
|
单位: getUnitText(item.unit),
|
||||||
|
数量: item.quantity,
|
||||||
|
小计: item.total,
|
||||||
|
})))
|
||||||
|
XLSX.utils.book_append_sheet(workbook, allowanceSheet, '补助明细')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出文件
|
||||||
|
const filename = `${record.employeeName}_工资单_${record.projectPeriod}.xlsx`
|
||||||
|
XLSX.writeFile(workbook, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出实习报酬申领表格式
|
||||||
|
export const exportInternCompensationForm = (record: SalaryRecord) => {
|
||||||
|
const workbook = XLSX.utils.book_new()
|
||||||
|
|
||||||
|
// 实习生基本信息
|
||||||
|
const basicInfo = [
|
||||||
|
['实习生基本信息', '', '', '', ''],
|
||||||
|
['类别', '填写内容', '', '备注', ''],
|
||||||
|
['姓名', record.employeeName, '', '', ''],
|
||||||
|
['身份证号', record.idCard, '', '', ''],
|
||||||
|
['联系电话', record.phone, '', '', ''],
|
||||||
|
['银行卡号', record.bankCard, '', '', ''],
|
||||||
|
['开户银行', record.bankName, '', '', ''],
|
||||||
|
['', '', '', '', ''],
|
||||||
|
['实习项目及项目实习相关信息', '', '', '', ''],
|
||||||
|
['序号', '项目名称', '在项目时间', '出外业施工作业时间(天)', '备注(时间段)'],
|
||||||
|
['1', record.projectName, record.workDays, record.workDays - record.offshoreDays, record.projectPeriod],
|
||||||
|
['', '', '', '', ''],
|
||||||
|
['薪酬、绩效、补助(一)', '', '', '', ''],
|
||||||
|
['项目类别', '计算标准(元/天)或(元/台)', '计酬天数统计(日)', '应发金额', '备注'],
|
||||||
|
['基本工资', record.baseSalaryStandard, record.workDays, record.baseSalary, ''],
|
||||||
|
['施工绩效', 100, record.workDays - record.offshoreDays, record.performanceBonus, ''],
|
||||||
|
['海上作业绩效补助', 30, record.offshoreDays, record.offshoreDays * 30, ''],
|
||||||
|
['务餐补助', 45, record.workDays, record.allowances, ''],
|
||||||
|
['合计应发金额', '', '', record.totalPayable, ''],
|
||||||
|
]
|
||||||
|
|
||||||
|
const sheet = XLSX.utils.aoa_to_sheet(basicInfo)
|
||||||
|
|
||||||
|
// 设置列宽
|
||||||
|
const cols = [
|
||||||
|
{ wch: 20 },
|
||||||
|
{ wch: 20 },
|
||||||
|
{ wch: 15 },
|
||||||
|
{ wch: 25 },
|
||||||
|
{ wch: 20 },
|
||||||
|
]
|
||||||
|
sheet['!cols'] = cols
|
||||||
|
|
||||||
|
// 合并单元格
|
||||||
|
sheet['!merges'] = [
|
||||||
|
{ s: { r: 0, c: 0 }, e: { r: 0, c: 4 } },
|
||||||
|
{ s: { r: 8, c: 0 }, e: { r: 8, c: 4 } },
|
||||||
|
{ s: { r: 12, c: 0 }, e: { r: 12, c: 4 } },
|
||||||
|
]
|
||||||
|
|
||||||
|
XLSX.utils.book_append_sheet(workbook, sheet, '实习报酬申领表')
|
||||||
|
|
||||||
|
// 导出文件
|
||||||
|
const filename = `${record.employeeName}_实习报酬申领表_${record.projectPeriod}.xlsx`
|
||||||
|
XLSX.writeFile(workbook, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数
|
||||||
|
function getEmployeeTypeText(type: string) {
|
||||||
|
const typeMap = {
|
||||||
|
intern: '实习生',
|
||||||
|
fulltime: '正式员工',
|
||||||
|
parttime: '兼职员工',
|
||||||
|
}
|
||||||
|
return typeMap[type as keyof typeof typeMap] || type
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusText(status: string) {
|
||||||
|
const statusMap = {
|
||||||
|
draft: '草稿',
|
||||||
|
pending: '待审批',
|
||||||
|
approved: '已批准',
|
||||||
|
rejected: '已拒绝',
|
||||||
|
paid: '已发放',
|
||||||
|
}
|
||||||
|
return statusMap[status as keyof typeof statusMap] || status
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnitText(unit: string) {
|
||||||
|
const unitMap = {
|
||||||
|
day: '天',
|
||||||
|
project: '项目',
|
||||||
|
fixed: '固定',
|
||||||
|
}
|
||||||
|
return unitMap[unit as keyof typeof unitMap] || unit
|
||||||
|
}
|
Loading…
Reference in New Issue