243 lines
7.9 KiB
Vue
243 lines
7.9 KiB
Vue
<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>
|