添加工资查看功能
This commit is contained in:
@@ -210,3 +210,30 @@ export const getFinanceAttendanceList = (params) => {
|
|||||||
params
|
params
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取工资条列表
|
||||||
|
export const getPayrollSheetsList = (params) => {
|
||||||
|
return service({
|
||||||
|
url: '/payroll/sheets/list',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 修改工资条
|
||||||
|
export const updatePayrollSheet = (data) => {
|
||||||
|
return service({
|
||||||
|
url: '/payroll/sheets',
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 展示工资条给员工
|
||||||
|
export const showPayrollSheet = (data) => {
|
||||||
|
return service({
|
||||||
|
url: '/payroll/sheets/show',
|
||||||
|
method: 'put',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ export const generateMenu = (routes, userRole) => {
|
|||||||
|
|
||||||
return menuList;
|
return menuList;
|
||||||
};
|
};
|
||||||
export const menuTable = generateMenu(routerList, 1)
|
// 权限检查
|
||||||
|
const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
|
||||||
|
export const menuTable = generateMenu(routerList, userInfo.role)
|
||||||
// 使用示例
|
// 使用示例
|
||||||
/*
|
/*
|
||||||
const userRole = 2; // 用户角色
|
const userRole = 2; // 用户角色
|
||||||
|
|||||||
@@ -68,18 +68,7 @@ const routerList = [
|
|||||||
title: '综合部初审列表',
|
title: '综合部初审列表',
|
||||||
name: "综合部初审列表",
|
name: "综合部初审列表",
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
// roles: [2] // 只有role=2的用户可以查看
|
roles: [2] // 只有role=2的用户可以查看
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/manager',
|
|
||||||
name: 'ManagerAttendance',
|
|
||||||
component: () => import('../views/performance/attendance/manager/index.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '主管审批列表',
|
|
||||||
name: "主管审批列表",
|
|
||||||
requiresAuth: true,
|
|
||||||
// roles: [1] // 只有role=1的用户可以查看
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -90,7 +79,18 @@ const routerList = [
|
|||||||
title: '综合部复审列表',
|
title: '综合部复审列表',
|
||||||
name: "综合部复审列表",
|
name: "综合部复审列表",
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
// roles: [2] // 只有role=2的用户可以查看
|
roles: [2] // 只有role=2的用户可以查看
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/manager',
|
||||||
|
name: 'ManagerAttendance',
|
||||||
|
component: () => import('../views/performance/attendance/manager/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '主管审批列表',
|
||||||
|
name: "主管审批列表",
|
||||||
|
requiresAuth: true,
|
||||||
|
roles: [1] // 只有role=1的用户可以查看
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -101,7 +101,33 @@ const routerList = [
|
|||||||
title: '财务查看考勤列表',
|
title: '财务查看考勤列表',
|
||||||
name: "财务查看考勤列表",
|
name: "财务查看考勤列表",
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
// roles: [3] // 只有role=3的用户可以查看
|
roles: [3] // 只有role=3的用户可以查看
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/payroll',
|
||||||
|
component: () => import("../layout/index.vue"),
|
||||||
|
redirect: '/payroll/sheets',
|
||||||
|
name: 'Payroll',
|
||||||
|
meta: {
|
||||||
|
title: '工资查看',
|
||||||
|
icon: 'el-icon-money',
|
||||||
|
name: "工资查看",
|
||||||
|
requiresAuth: true,
|
||||||
|
roles: [3] // 只有财务角色可以访问
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/sheets',
|
||||||
|
name: 'PayrollSheets',
|
||||||
|
component: () => import('../views/payroll/sheets/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '工资条列表',
|
||||||
|
name: "工资条列表",
|
||||||
|
requiresAuth: true,
|
||||||
|
roles: [3] // 只有财务角色可以访问
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,905 @@
|
|||||||
|
<template>
|
||||||
|
<div class="payroll-sheets-container">
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="title">工资条列表</span>
|
||||||
|
<div class="header-right">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
type="month"
|
||||||
|
placeholder="选择月份"
|
||||||
|
:default-time="defaultTime"
|
||||||
|
:shortcuts="dateShortcuts"
|
||||||
|
@change="handleDateChange"
|
||||||
|
style="width: 200px; margin-right: 8px;"
|
||||||
|
/>
|
||||||
|
<el-input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="请输入姓名搜索"
|
||||||
|
clearable
|
||||||
|
@input="handleSearch"
|
||||||
|
style="width: 200px; margin-right: 8px;"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<el-icon><Search /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<el-button type="success" @click="handleBatchShowToEmployee">批量展示给员工</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table
|
||||||
|
:data="payrollList"
|
||||||
|
style="width: 100%"
|
||||||
|
height="calc(100vh - 280px)"
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
:header-cell-style="{
|
||||||
|
background: '#f5f7fa',
|
||||||
|
color: '#606266',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
textAlign: 'center',
|
||||||
|
padding: '12px 0'
|
||||||
|
}"
|
||||||
|
:cell-style="{
|
||||||
|
padding: '8px 0'
|
||||||
|
}"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column label="头像" width="80" align="center" fixed="left">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-avatar :size="40" :src="scope.row.avatar" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="name" label="姓名" width="100" show-overflow-tooltip fixed="left" />
|
||||||
|
<el-table-column prop="baseSalary" label="基本工资" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.baseSalary) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="annualPerformanceBonus" label="年度绩效奖金" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.annualPerformanceBonus) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="monthlyPerformanceBonus" label="月度绩效奖金" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.monthlyPerformanceBonus) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="otherBonus" label="其他奖金" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.otherBonus) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="managerAllowance" label="总经理补贴" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.managerAllowance) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="transportationAllowance" label="交通补贴" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.transportationAllowance) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="otherAllowance" label="其他补贴" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.otherAllowance) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="senioritySalary" label="工龄工资" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.senioritySalary) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="overtimePay" label="加班工资" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.overtimePay) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="grossPaySummary" label="应发小计" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.grossPaySummary) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="annualPerformanceDeduction" label="年度绩效扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.annualPerformanceDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="monthlyPerformanceDeduction" label="月度绩效扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.monthlyPerformanceDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="personalLeaveDeduction" label="事假扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.personalLeaveDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="sickLeaveDeduction" label="病假扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.sickLeaveDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="lateEarlyDeduction" label="迟到早退扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.lateEarlyDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="missedCheckInOutDeduction" label="签到签退扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.missedCheckInOutDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="absentCardDeduction" label="缺卡扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.absentCardDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="absenteeismDeduction" label="旷工扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.absenteeismDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="violationDeduction" label="违纪扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.violationDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="basicPensionDeduction" label="基本养老" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.basicPensionDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="unemploymentInsuranceDeduction" label="失业保险" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.unemploymentInsuranceDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="basicMedicalDeduction" label="基本医疗" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.basicMedicalDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="medicalDeductionAmount" label="大额医疗" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.medicalDeductionAmount) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="housingFundDeduction" label="住房公积金" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.housingFundDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="otherDeduction" label="其他扣款" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.otherDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="individualTaxDeduction" label="个税" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.individualTaxDeduction) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="deductionTotal" label="扣款小计" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.deductionTotal) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="netSalary" label="实发工资" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.netSalary) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180" fixed="right" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
@click="handleEdit(scope.row)"
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
@click="handleShowToEmployee(scope.row)"
|
||||||
|
>
|
||||||
|
展示给员工
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="pagination-container">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 修改工资条对话框 -->
|
||||||
|
<div class="edit-payroll-dialog">
|
||||||
|
<el-dialog
|
||||||
|
v-model="editDialogVisible"
|
||||||
|
title="修改工资条"
|
||||||
|
width="900px"
|
||||||
|
>
|
||||||
|
<div class="edit-form">
|
||||||
|
<el-form
|
||||||
|
ref="editFormRef"
|
||||||
|
:model="editForm"
|
||||||
|
:rules="editRules"
|
||||||
|
label-width="140px"
|
||||||
|
>
|
||||||
|
<!-- 仅保留可编辑字段 -->
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">违纪扣款</div>
|
||||||
|
<div class="form-grid">
|
||||||
|
<el-form-item label="违纪扣款" prop="violationDeduction">
|
||||||
|
<el-input-number
|
||||||
|
v-model="editForm.violationDeduction"
|
||||||
|
:precision="2"
|
||||||
|
:step="100"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">社保公积金</div>
|
||||||
|
<div class="form-grid">
|
||||||
|
<el-form-item label="基本养老保险" prop="basicPensionDeduction">
|
||||||
|
<el-input-number
|
||||||
|
v-model="editForm.basicPensionDeduction"
|
||||||
|
:precision="2"
|
||||||
|
:step="100"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="失业保险" prop="unemploymentInsuranceDeduction">
|
||||||
|
<el-input-number
|
||||||
|
v-model="editForm.unemploymentInsuranceDeduction"
|
||||||
|
:precision="2"
|
||||||
|
:step="100"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="基本医疗保险" prop="basicMedicalDeduction">
|
||||||
|
<el-input-number
|
||||||
|
v-model="editForm.basicMedicalDeduction"
|
||||||
|
:precision="2"
|
||||||
|
:step="100"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="补充医疗保险" prop="medicalDeductionAmount">
|
||||||
|
<el-input-number
|
||||||
|
v-model="editForm.medicalDeductionAmount"
|
||||||
|
:precision="2"
|
||||||
|
:step="100"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="住房公积金" prop="housingFundDeduction">
|
||||||
|
<el-input-number
|
||||||
|
v-model="editForm.housingFundDeduction"
|
||||||
|
:precision="2"
|
||||||
|
:step="100"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-section">
|
||||||
|
<div class="section-title">其他扣款</div>
|
||||||
|
<div class="form-grid">
|
||||||
|
<el-form-item label="个人所得税" prop="individualTaxDeduction">
|
||||||
|
<el-input-number
|
||||||
|
v-model="editForm.individualTaxDeduction"
|
||||||
|
:precision="2"
|
||||||
|
:step="100"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="其他扣款" prop="otherDeduction">
|
||||||
|
<el-input-number
|
||||||
|
v-model="editForm.otherDeduction"
|
||||||
|
:precision="2"
|
||||||
|
:step="100"
|
||||||
|
:min="0"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="editDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSubmitEdit">确定</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { Search } from '@element-plus/icons-vue';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import {
|
||||||
|
getPayrollSheetsList,
|
||||||
|
updatePayrollSheet,
|
||||||
|
showPayrollSheet
|
||||||
|
} from "../../../api/modules/index";
|
||||||
|
|
||||||
|
// 权限检查
|
||||||
|
const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
|
||||||
|
// if (userInfo.role !== 3) {
|
||||||
|
// ElMessage.error("您没有权限访问此页面");
|
||||||
|
// window.history.back();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 列表数据
|
||||||
|
const payrollList = ref([]);
|
||||||
|
const originalList = ref([]);
|
||||||
|
const searchKeyword = ref('');
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = ref(10);
|
||||||
|
const total = ref(0);
|
||||||
|
|
||||||
|
// 日期范围
|
||||||
|
const dateRange = ref(null);
|
||||||
|
const defaultTime = new Date(2000, 1, 1, 0, 0, 0);
|
||||||
|
|
||||||
|
// 日期快捷选项
|
||||||
|
const dateShortcuts = [
|
||||||
|
{
|
||||||
|
text: '上个月',
|
||||||
|
value: () => {
|
||||||
|
const now = new Date();
|
||||||
|
return new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '本月',
|
||||||
|
value: () => {
|
||||||
|
const now = new Date();
|
||||||
|
return new Date(now.getFullYear(), now.getMonth(), 1);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '下个月',
|
||||||
|
value: () => {
|
||||||
|
const now = new Date();
|
||||||
|
return new Date(now.getFullYear(), now.getMonth() + 1, 1);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 设置默认日期范围(上个月)
|
||||||
|
const setDefaultDateRange = () => {
|
||||||
|
const now = new Date();
|
||||||
|
dateRange.value = new Date(now.getFullYear(), now.getMonth() - 1, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理日期变化
|
||||||
|
const handleDateChange = (val) => {
|
||||||
|
if (val) {
|
||||||
|
currentPage.value = 1;
|
||||||
|
fetchList();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取列表数据
|
||||||
|
const fetchList = async () => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
current: currentPage.value,
|
||||||
|
size: pageSize.value
|
||||||
|
};
|
||||||
|
if (dateRange.value) {
|
||||||
|
params.year = dateRange.value.getFullYear();
|
||||||
|
params.month = dateRange.value.getMonth() + 1;
|
||||||
|
}
|
||||||
|
if (searchKeyword.value) {
|
||||||
|
params.keyword = searchKeyword.value;
|
||||||
|
}
|
||||||
|
const res = await getPayrollSheetsList(params);
|
||||||
|
if (res.errcode === 0 && res.result) {
|
||||||
|
payrollList.value = res.result.records || [];
|
||||||
|
total.value = res.result.total || 0;
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.errmsg || "获取列表失败");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error("获取列表失败");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const handleSearch = () => {
|
||||||
|
currentPage.value = 1;
|
||||||
|
fetchList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 分页大小变化
|
||||||
|
const handleSizeChange = (val) => {
|
||||||
|
pageSize.value = val;
|
||||||
|
fetchList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 页码变化
|
||||||
|
const handleCurrentChange = (val) => {
|
||||||
|
currentPage.value = val;
|
||||||
|
fetchList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化金额
|
||||||
|
const formatMoney = (value) => {
|
||||||
|
if (value === undefined || value === null) return '0.00';
|
||||||
|
return Number(value).toFixed(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取状态标签
|
||||||
|
const getStatusLabel = (status) => {
|
||||||
|
const statusMap = {
|
||||||
|
0: '待确认',
|
||||||
|
1: '已确认',
|
||||||
|
2: '已发放'
|
||||||
|
};
|
||||||
|
return statusMap[status] || '未知';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取状态标签样式
|
||||||
|
const getStatusType = (status) => {
|
||||||
|
const typeMap = {
|
||||||
|
0: 'warning',
|
||||||
|
1: 'success',
|
||||||
|
2: 'info'
|
||||||
|
};
|
||||||
|
return typeMap[status] || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 修改对话框
|
||||||
|
const editDialogVisible = ref(false);
|
||||||
|
const editFormRef = ref(null);
|
||||||
|
const editForm = ref({
|
||||||
|
userid: '',
|
||||||
|
year: 0,
|
||||||
|
month: 0,
|
||||||
|
violationDeduction: 0,
|
||||||
|
basicPensionDeduction: 0,
|
||||||
|
unemploymentInsuranceDeduction: 0,
|
||||||
|
basicMedicalDeduction: 0,
|
||||||
|
medicalDeductionAmount: 0,
|
||||||
|
housingFundDeduction: 0,
|
||||||
|
individualTaxDeduction: 0,
|
||||||
|
otherDeduction: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const editRules = {
|
||||||
|
violationDeduction: [{ type: 'number', min: 0, message: '金额不能小于0' }],
|
||||||
|
basicPensionDeduction: [{ type: 'number', min: 0, message: '金额不能小于0' }],
|
||||||
|
unemploymentInsuranceDeduction: [{ type: 'number', min: 0, message: '金额不能小于0' }],
|
||||||
|
basicMedicalDeduction: [{ type: 'number', min: 0, message: '金额不能小于0' }],
|
||||||
|
medicalDeductionAmount: [{ type: 'number', min: 0, message: '金额不能小于0' }],
|
||||||
|
housingFundDeduction: [{ type: 'number', min: 0, message: '金额不能小于0' }],
|
||||||
|
individualTaxDeduction: [{ type: 'number', min: 0, message: '金额不能小于0' }],
|
||||||
|
otherDeduction: [{ type: 'number', min: 0, message: '金额不能小于0' }]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打开修改对话框
|
||||||
|
const handleEdit = (row) => {
|
||||||
|
editForm.value = {
|
||||||
|
userid: row.userid,
|
||||||
|
year: row.year,
|
||||||
|
month: row.month,
|
||||||
|
violationDeduction: row.violationDeduction || 0,
|
||||||
|
basicPensionDeduction: row.basicPensionDeduction || 0,
|
||||||
|
unemploymentInsuranceDeduction: row.unemploymentInsuranceDeduction || 0,
|
||||||
|
basicMedicalDeduction: row.basicMedicalDeduction || 0,
|
||||||
|
medicalDeductionAmount: row.medicalDeductionAmount || 0,
|
||||||
|
housingFundDeduction: row.housingFundDeduction || 0,
|
||||||
|
individualTaxDeduction: row.individualTaxDeduction || 0,
|
||||||
|
otherDeduction: row.otherDeduction || 0
|
||||||
|
};
|
||||||
|
editDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提交修改
|
||||||
|
const handleSubmitEdit = async () => {
|
||||||
|
if (!editFormRef.value) return;
|
||||||
|
|
||||||
|
await editFormRef.value.validate(async (valid) => {
|
||||||
|
if (valid) {
|
||||||
|
try {
|
||||||
|
const res = await updatePayrollSheet(editForm.value);
|
||||||
|
if (res.errcode === 0) {
|
||||||
|
ElMessage.success('修改成功');
|
||||||
|
editDialogVisible.value = false;
|
||||||
|
fetchList(); // 刷新列表
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.errmsg || '修改失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('修改失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 展示给员工
|
||||||
|
const handleShowToEmployee = async (row) => {
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
year: Number(row.year),
|
||||||
|
month: Number(row.month),
|
||||||
|
userids: [row.userid]
|
||||||
|
};
|
||||||
|
const res = await showPayrollSheet(params);
|
||||||
|
if (res.errcode === 0) {
|
||||||
|
ElMessage.success('展示成功');
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.errmsg || '展示失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('展示失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedRows = ref([]);
|
||||||
|
|
||||||
|
// 处理多选变化
|
||||||
|
const handleSelectionChange = (selection) => {
|
||||||
|
selectedRows.value = selection;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量展示给员工
|
||||||
|
const handleBatchShowToEmployee = async () => {
|
||||||
|
if (!selectedRows.value.length) {
|
||||||
|
ElMessage.warning('请选择要展示的工资条');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
year: Number(selectedRows.value[0].year),
|
||||||
|
month: Number(selectedRows.value[0].month),
|
||||||
|
userids: selectedRows.value.map(item => item.userid)
|
||||||
|
};
|
||||||
|
const res = await showPayrollSheet(params);
|
||||||
|
if (res.errcode === 0) {
|
||||||
|
ElMessage.success('批量展示成功');
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.errmsg || '批量展示失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error('批量展示失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 页面加载时设置默认日期范围并获取数据
|
||||||
|
onMounted(() => {
|
||||||
|
setDefaultDateRange();
|
||||||
|
fetchList();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.payroll-sheets-container {
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
padding: 20px;
|
||||||
|
background-color: var(--el-bg-color-page);
|
||||||
|
|
||||||
|
:deep(.el-card) {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: var(--el-box-shadow-light);
|
||||||
|
|
||||||
|
.el-card__body {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table) {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.el-table__body-wrapper {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__cell {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__header {
|
||||||
|
th {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #606266;
|
||||||
|
height: 48px;
|
||||||
|
padding: 12px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__row {
|
||||||
|
height: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__fixed-right {
|
||||||
|
height: 100% !important;
|
||||||
|
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__fixed {
|
||||||
|
height: 100% !important;
|
||||||
|
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-light);
|
||||||
|
background-color: var(--el-bg-color);
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
border-top: 1px solid var(--el-border-color-light);
|
||||||
|
background-color: var(--el-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.payroll-detail {
|
||||||
|
.detail-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-light);
|
||||||
|
|
||||||
|
.employee-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.info-content {
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.salary-period {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-footer {
|
||||||
|
margin-top: 24px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid var(--el-border-color-light);
|
||||||
|
|
||||||
|
.signature-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
gap: 32px;
|
||||||
|
|
||||||
|
.signature-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-image {
|
||||||
|
height: 100px;
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-signature {
|
||||||
|
height: 100px;
|
||||||
|
border: 1px dashed var(--el-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog__body) {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog__header) {
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-light);
|
||||||
|
background-color: var(--el-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog__footer) {
|
||||||
|
padding: 20px;
|
||||||
|
border-top: 1px solid var(--el-border-color-light);
|
||||||
|
background-color: var(--el-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog) {
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.salary-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-left: 8px;
|
||||||
|
border-left: 4px solid var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-descriptions) {
|
||||||
|
.el-descriptions__label {
|
||||||
|
width: 120px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-descriptions__content {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-payroll-dialog {
|
||||||
|
:deep(.el-dialog__body) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-form {
|
||||||
|
padding: 20px;
|
||||||
|
max-height: calc(90vh - 120px);
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
background-color: #f8fafd;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px 24px 8px 24px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.03);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #409eff;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-left: 8px;
|
||||||
|
border-left: 4px solid #409eff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 24px 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
.el-form-item__label {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.el-input-number {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 120px;
|
||||||
|
.el-input__wrapper {
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-top: 1px solid var(--el-border-color-light);
|
||||||
|
background-color: var(--el-bg-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -52,6 +52,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="name" label="姓名" width="100" show-overflow-tooltip fixed="left" />
|
<el-table-column prop="name" label="姓名" width="100" show-overflow-tooltip fixed="left" />
|
||||||
|
<el-table-column prop="baseSalary" label="基本工资" width="120" align="right" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatMoney(scope.row.baseSalary) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="workDays" label="工作天数" width="100" align="center" />
|
<el-table-column prop="workDays" label="工作天数" width="100" align="center" />
|
||||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@@ -82,8 +87,36 @@
|
|||||||
<el-table-column prop="annualPerformance" label="年度绩效" width="100" align="center" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="annualPerformance" label="年度绩效" width="100" align="center" show-overflow-tooltip></el-table-column>
|
||||||
<el-table-column prop="monthlyPerformance" label="月度绩效" width="100" align="center" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="monthlyPerformance" label="月度绩效" width="100" align="center" show-overflow-tooltip></el-table-column>
|
||||||
<el-table-column prop="penaltyAmount" label="违纪扣奖" width="100" align="center" show-overflow-tooltip />
|
<el-table-column prop="penaltyAmount" label="违纪扣奖" width="100" align="center" show-overflow-tooltip />
|
||||||
<el-table-column label="操作" width="120" fixed="right" align="center">
|
<el-table-column label="签名" width="100" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
<div class="signature-cell">
|
||||||
|
<el-image
|
||||||
|
v-if="scope.row.signatureImg"
|
||||||
|
:src="scope.row.signatureImg"
|
||||||
|
fit="contain"
|
||||||
|
style="width: 40px; height: 40px; cursor: pointer;"
|
||||||
|
@click="handlePreviewSignature(scope.row.signatureImg)"
|
||||||
|
>
|
||||||
|
<template #error>
|
||||||
|
<div class="image-slot">
|
||||||
|
<el-icon><Picture /></el-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<span v-else>无签名</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="240" fixed="right" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
@click="handleViewDetail(scope.row)"
|
||||||
|
>
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
link
|
link
|
||||||
@@ -150,19 +183,142 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 工资条详情对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="detailDialogVisible"
|
||||||
|
title="考勤详情"
|
||||||
|
width="800px"
|
||||||
|
>
|
||||||
|
<div v-if="currentDetail" class="attendance-detail">
|
||||||
|
<div class="detail-header">
|
||||||
|
<div class="employee-info">
|
||||||
|
<el-avatar :size="64" :src="currentDetail.avatar" />
|
||||||
|
<div class="info-content">
|
||||||
|
<h3>{{ currentDetail.name }}</h3>
|
||||||
|
<p>{{ currentDetail.position }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="attendance-period">
|
||||||
|
{{ currentDetail.year }}年{{ currentDetail.month }}月考勤
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="基本工资">{{ formatMoney(currentDetail.baseSalary) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="工作天数">{{ currentDetail.workDays }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">
|
||||||
|
<el-tag :type="currentDetail.status === 0 ? 'success' : 'warning'" size="small">
|
||||||
|
{{ currentDetail.status === 0 ? '正式' : '试用' }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="事假">{{ currentDetail.personalLeave }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="病假">{{ currentDetail.sickLeave }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="产假">{{ currentDetail.maternityLeave }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="陪产假">{{ currentDetail.paternityLeave }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="婚假">{{ currentDetail.marriageLeave }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="丧假">{{ currentDetail.bereavementLeave }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="年休假">{{ currentDetail.annualLeave }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="年休假(管理岗)">{{ currentDetail.managementAnnualLeave }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="调休假">{{ currentDetail.compensatoryLeave }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="早退次数">{{ currentDetail.early }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="迟到次数">{{ currentDetail.late }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="严重迟到">{{ currentDetail.seriousLate }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="旷工次数">{{ currentDetail.absenteeism }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="缺卡次数">{{ currentDetail.missedCount }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="缺勤次数">{{ currentDetail.absenceCount }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="未提交日志">{{ currentDetail.missingLogCount }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="工作日加班">{{ currentDetail.workdayOvertime }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="休息日加班">{{ currentDetail.restdayOvertime }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="节假日加班">{{ currentDetail.holidayOvertime }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="年度绩效">{{ currentDetail.annualPerformance }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="月度绩效">{{ currentDetail.monthlyPerformance }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="违纪扣奖">{{ formatMoney(currentDetail.penaltyAmount) }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="detail-footer">
|
||||||
|
<div class="signature-section">
|
||||||
|
<div class="signature-item">
|
||||||
|
<div class="label">员工签名:</div>
|
||||||
|
<div class="signature-image" v-if="currentDetail.signatureImg">
|
||||||
|
<el-image
|
||||||
|
:src="currentDetail.signatureImg"
|
||||||
|
fit="contain"
|
||||||
|
style="width: 100%; height: 100%;"
|
||||||
|
@click="handlePreviewSignature(currentDetail.signatureImg)"
|
||||||
|
>
|
||||||
|
<template #error>
|
||||||
|
<div class="image-slot">
|
||||||
|
<el-icon><Picture /></el-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</div>
|
||||||
|
<div class="no-signature" v-else>未签名</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 签名预览对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="signaturePreviewVisible"
|
||||||
|
title="签名预览"
|
||||||
|
width="500px"
|
||||||
|
:show-close="true"
|
||||||
|
:close-on-click-modal="true"
|
||||||
|
:close-on-press-escape="true"
|
||||||
|
class="signature-preview-dialog"
|
||||||
|
>
|
||||||
|
<div class="signature-preview-content">
|
||||||
|
<el-image
|
||||||
|
v-if="currentSignatureImg"
|
||||||
|
:src="currentSignatureImg"
|
||||||
|
fit="contain"
|
||||||
|
style="width: 100%; height: 300px;"
|
||||||
|
>
|
||||||
|
<template #error>
|
||||||
|
<div class="image-slot">
|
||||||
|
<el-icon><Picture /></el-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { Search } from '@element-plus/icons-vue';
|
import { Search, Picture } from '@element-plus/icons-vue';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import {
|
import {
|
||||||
getFinanceAttendanceList,
|
getFinanceAttendanceList,
|
||||||
getAbnormalAttendanceDetail,
|
getAbnormalAttendanceDetail,
|
||||||
} from "../../../../api/modules/index";
|
} from "../../../../api/modules/index";
|
||||||
|
|
||||||
|
// 格式化金额
|
||||||
|
const formatMoney = (value) => {
|
||||||
|
if (value === undefined || value === null) return '0.00';
|
||||||
|
|
||||||
|
// 将输入转换为数字
|
||||||
|
const num = Number(value);
|
||||||
|
|
||||||
|
// 检查是否为有效数字
|
||||||
|
if (isNaN(num)) return '0.00';
|
||||||
|
|
||||||
|
// 使用toFixed(2)保留两位小数
|
||||||
|
const fixedNum = num.toFixed(2);
|
||||||
|
|
||||||
|
// 添加千分位分隔符
|
||||||
|
const parts = fixedNum.split('.');
|
||||||
|
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
|
|
||||||
|
return parts.join('.');
|
||||||
|
};
|
||||||
|
|
||||||
// 权限检查
|
// 权限检查
|
||||||
const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
|
const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
|
||||||
// if (userInfo.role !== 3) {
|
// if (userInfo.role !== 3) {
|
||||||
@@ -175,6 +331,10 @@ const attendanceList = ref([]);
|
|||||||
const originalList = ref([]);
|
const originalList = ref([]);
|
||||||
const searchKeyword = ref('');
|
const searchKeyword = ref('');
|
||||||
|
|
||||||
|
// 详情对话框
|
||||||
|
const detailDialogVisible = ref(false);
|
||||||
|
const currentDetail = ref(null);
|
||||||
|
|
||||||
// 日期范围
|
// 日期范围
|
||||||
const dateRange = ref(null);
|
const dateRange = ref(null);
|
||||||
const defaultTime = new Date(2000, 1, 1, 0, 0, 0);
|
const defaultTime = new Date(2000, 1, 1, 0, 0, 0);
|
||||||
@@ -330,6 +490,24 @@ const handleViewAbnormal = async (row) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 查看详情
|
||||||
|
const handleViewDetail = (row) => {
|
||||||
|
currentDetail.value = row;
|
||||||
|
detailDialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 签名预览
|
||||||
|
const signaturePreviewVisible = ref(false);
|
||||||
|
const currentSignatureImg = ref('');
|
||||||
|
|
||||||
|
const handlePreviewSignature = (url) => {
|
||||||
|
if (!url) return;
|
||||||
|
const img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
const newWindow = window.open('');
|
||||||
|
newWindow.document.write(img.outerHTML);
|
||||||
|
};
|
||||||
|
|
||||||
// 页面加载时设置默认日期范围并获取数据
|
// 页面加载时设置默认日期范围并获取数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setDefaultDateRange();
|
setDefaultDateRange();
|
||||||
@@ -387,11 +565,13 @@ onMounted(() => {
|
|||||||
.el-table__fixed-right {
|
.el-table__fixed-right {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table__fixed {
|
.el-table__fixed {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
|
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,6 +595,39 @@ onMounted(() => {
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.signature-cell {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-image-viewer__wrapper) {
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-image-viewer__mask) {
|
||||||
|
z-index: 9998;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-image-viewer__btn) {
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-image-viewer__actions) {
|
||||||
|
z-index: 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-image-viewer__canvas) {
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog__wrapper) {
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog) {
|
||||||
|
z-index: 2001;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.abnormal-date-group {
|
.abnormal-date-group {
|
||||||
@@ -472,4 +685,193 @@ onMounted(() => {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image-slot {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-image {
|
||||||
|
height: 100px;
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #ffffff;
|
||||||
|
position: relative;
|
||||||
|
z-index: 3000;
|
||||||
|
|
||||||
|
:deep(.el-image) {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #ffffff;
|
||||||
|
position: relative;
|
||||||
|
z-index: 3000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-image-viewer__wrapper) {
|
||||||
|
z-index: 3000;
|
||||||
|
|
||||||
|
.el-image-viewer__canvas {
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog) {
|
||||||
|
.el-dialog__body {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-cell {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table) {
|
||||||
|
.el-table__fixed-right {
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__fixed {
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-image-viewer__wrapper) {
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-image-viewer__mask) {
|
||||||
|
z-index: 9998;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-preview-dialog {
|
||||||
|
:deep(.el-dialog__body) {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-preview-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog__wrapper) {
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog) {
|
||||||
|
z-index: 2001;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attendance-detail {
|
||||||
|
.detail-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
|
||||||
|
.employee-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.info-content {
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attendance-period {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-footer {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #ebeef5;
|
||||||
|
|
||||||
|
.signature-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 40px;
|
||||||
|
|
||||||
|
.signature-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-image {
|
||||||
|
width: 120px;
|
||||||
|
height: 60px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-signature {
|
||||||
|
width: 120px;
|
||||||
|
height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px dashed #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-descriptions) {
|
||||||
|
.el-descriptions__label {
|
||||||
|
width: 120px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-descriptions__content {
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user