Industrial-image-management.../src/views/salary-management/components/SalaryFormDrawer.vue

469 lines
13 KiB
Vue
Raw 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.

<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>