dev-cl
qsh 4 months ago
parent d80be0d7f1
commit 8142e05543
  1. 27
      src/api/home/salary.js
  2. 2
      src/components/Pagination/index.vue
  3. 16
      src/utils/download.ts
  4. 31
      src/views/Finance/Salary/Comp/DialogSalary.vue
  5. 1
      src/views/Finance/Salary/index.vue
  6. 157
      src/views/Home/Salary/Comp/DialogCreateSalary.vue
  7. 89
      src/views/Home/Salary/Comp/DialogSalaryImport.vue
  8. 115
      src/views/Home/Salary/index.vue

@ -1,7 +1,17 @@
import request from '@/config/axios'
// 查询工资条
export const getSalaryPage = async (params) => {
return await request.get({ url: '/admin-api/oa/user-salary-grant/page', params })
}
// 生成工资条
export const createSalarySlip = async (data) => {
return await request.post({ url: '/admin-api/oa/user-salary-grant/create', data })
return await request.post({
url: '/admin-api/oa/user-salary-grant/create',
data,
isSubmitForm: true
})
}
// 导入工资条
@ -18,3 +28,18 @@ export const updateSalarySlip = async (data) => {
export const sealupSalarySlip = async (data) => {
return await request.post({ url: '/admin-api/oa/user-salary-grant/confirm', data })
}
// 获取考勤模板链接
export const getLinkAttendance = async () => {
return await request.get({ url: '/admin-api/oa/user-attendance/get-import-template' })
}
// 获取奖金模板
export const getLinkReward = async () => {
return await request.get({ url: '/admin-api/oa/user-salary-grant/get-import-template' })
}
// 获取历史工资条模板
export const getLinkHistorySalary = async () => {
return await request.get({ url: '/admin-api/oa/user-salary-grant/get-import-template/history' })
}

@ -6,7 +6,7 @@
v-model:page-size="pageSize"
:small="small"
:background="true"
:page-sizes="[10, 20, 30, 50, 100]"
:page-sizes="[10, 20, 50, 100, 200]"
:pager-count="pagerCount"
:total="total"
class="float-right mt-15px mb-15px"

@ -36,3 +36,19 @@ const download = {
}
export default download
export function downloadFile(url: string, fileName: string) {
const link = document.createElement('a')
// 这里是将url转成blob地址,
fetch(url)
.then((res) => res.blob())
.then((blob) => {
// 将链接地址字符内容转变成blob地址
link.href = URL.createObjectURL(blob)
link.download = fileName
document.body.appendChild(link)
link.click()
URL.revokeObjectURL(link.href) //清除引用
document.body.removeChild(link)
})
}

@ -38,15 +38,15 @@
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="补贴" prop="butie">
<el-input-number v-model="formData.butie" :min="0" :controls="false" />
<el-form-item label="补贴" prop="allowanceSalary">
<el-input-number v-model="formData.allowanceSalary" :min="0" :controls="false" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item label="满勤" prop="manqin">
<el-input-number v-model="formData.manqin" :min="0" :controls="false" />
<el-form-item label="满勤" prop="fullWorkSalary">
<el-input-number v-model="formData.fullWorkSalary" :min="0" :controls="false" />
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
@ -87,8 +87,8 @@
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="其他应扣" prop="qitayingkou">
<el-input-number v-model="formData.qitayingkou" :min="0" :controls="false" />
<el-form-item label="其他应扣" prop="extraDeduct">
<el-input-number v-model="formData.extraDeduct" :min="0" :controls="false" />
</el-form-item>
</el-col>
</el-row>
@ -129,7 +129,7 @@ const open = async (row) => {
try {
formData.value = await SalaryApi.getSalaryDetail(row.id)
if (!formData.value) {
resetForm()
resetForm(row.id)
}
} finally {
formLoading.value = false
@ -145,17 +145,19 @@ function getOptions() {
}
/** 重置表单 */
const resetForm = () => {
const resetForm = (id) => {
formData.value = {
employeeId: id,
baseSalary: 0,
planId: undefined,
rewardSalary: 0,
butie: 0,
manqin: 0,
allowanceSalary: 0,
fullWorkSalary: 0,
extraSalary: 0,
socialDeduct: 0,
accumulationFundDeduct: 0,
qitayingkou: 0
meritsSalary: 0,
extraDeduct: 0
}
formRef.value?.resetFields()
}
@ -171,12 +173,7 @@ const submitForm = async () => {
formLoading.value = true
try {
const data = formData.value
if (formType.value === 'create') {
await SalaryApi.createSalary(data)
message.success(t('common.createSuccess'))
} else {
await SalaryApi.updateSalary(data)
}
await SalaryApi.updateSalary(data)
message.success('配置成功')
dialogVisible.value = false
//

@ -5,6 +5,7 @@
<el-input
v-model="searchForm.name"
placeholder="请输入员工姓名"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item>

@ -3,58 +3,58 @@
<el-form :model="formData" ref="formRef" :rules="rules" label-width="80px">
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item label="年月" prop="yearmonth">
<el-date-picker v-model="formData.yearmonth" type="month" placeholder="选择年月" />
<el-form-item label="年月" prop="period">
<el-date-picker
v-model="formData.period"
type="month"
placeholder="选择年月"
format="YYYY-MM"
value-format="YYYY-MM"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12" :offset="0">
<el-form-item label="导入奖金">
<UploadFile
v-model="formData.files1"
:limit="1"
:fileType="['xls', 'xlsx']"
accept=".xls,.xlsx"
:isShowTip="false"
>
<template #tip>
<div>
<el-link
type="primary"
:underline="false"
href="https://ss-cloud.ahduima.com/1011/1808052748575576064.pdf"
target="_blank"
>
点击下载模板文件
</el-link>
</div>
</template>
</UploadFile>
<div>
<el-upload
ref="rewardFile"
action="#"
:limit="1"
accept=".xls,.xlsx"
:before-upload="fileBeforeUpload"
:http-request="rewardUpload"
>
<el-button type="primary">
<Icon icon="ep:upload" class="mr-5px" /> 点击上传
</el-button>
</el-upload>
<el-link type="primary" :underline="false" @click="downloadTemplateFile(1)">
点击下载模板文件
</el-link>
</div>
</el-form-item>
</el-col>
<el-col :span="12" :offset="0">
<el-form-item label="导入考勤" prop="files2">
<UploadFile
v-model="formData.files2"
:limit="1"
:fileType="['xls', 'xlsx']"
accept=".xls,.xlsx"
:isShowTip="false"
>
<template #tip>
<div>
<el-link
type="primary"
:underline="false"
href="https://ss-cloud.ahduima.com/1011/1808052748575576064.pdf"
target="_blank"
>
点击下载模板文件
</el-link>
</div>
</template>
</UploadFile>
<el-form-item label="导入考勤" prop="attendanceFile">
<div>
<el-upload
ref="attendanceFile"
action="#"
:limit="1"
accept=".xls,.xlsx"
:before-upload="fileBeforeUpload"
:http-request="attendanceUpload"
>
<el-button type="primary">
<Icon icon="ep:upload" class="mr-5px" /> 点击上传
</el-button>
</el-upload>
<el-link type="primary" :underline="false" @click="downloadTemplateFile(2)">
点击下载模板文件
</el-link>
</div>
</el-form-item>
</el-col>
</el-row>
@ -69,6 +69,10 @@
</template>
<script setup name="DialogCreateSalary">
import * as SalaryApi from '@/api/home/salary.js'
import { downloadFile } from '@/utils/download'
const message = useMessage() //
const dialogVisible = ref(false)
const formData = ref({})
@ -76,8 +80,7 @@ const formData = ref({})
const formLoading = ref(false)
const rules = {
yearmonth: { required: true, message: '年月不可为空', trigger: 'blur,change' },
files2: { required: true, message: '考勤文件不可为空', trigger: 'blur,change' }
period: { required: true, message: '年月不可为空', trigger: 'blur,change' }
}
function open() {
@ -86,16 +89,72 @@ function open() {
}
function resetForm() {
const month = `${new Date().getMonth() + 1}`
formData.value = {
yearmonth: new Date().getFullYear(),
files1: '',
files2: ''
period: `${new Date().getFullYear()}-${month.padStart(2, '0')}`
}
}
defineExpose({ open })
function handleSave() {}
const emit = defineEmits(['success'])
const formRef = ref()
async function handleSave() {
//
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
if (!fd.value.get('attendanceFile')) {
message.info('请导入考勤表!')
return
}
//
formLoading.value = true
try {
fd.value.append('period', formData.value.period)
await SalaryApi.createSalarySlip(fd.value)
message.success('生成成功')
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
async function downloadTemplateFile(type) {
let data
if (type == 1) {
data = await SalaryApi.getLinkReward()
downloadFile(data, '奖金模板.xls')
} else if (type == 2) {
data = await SalaryApi.getLinkAttendance()
downloadFile(data, '考勤模板.xls')
}
}
const fileBeforeUpload = (file) => {
let format = '.' + file.name.split('.')[1]
if (!['.xls', '.xlsx'].includes(format)) {
message.error(`请上传指定格式".xls,.xlsx"文件`)
return false
}
let isRightSize = file.size / 1024 / 1024 < 20
if (!isRightSize) {
message.error('文件大小超过 20MB')
}
return isRightSize
}
const fd = ref(new FormData())
function rewardUpload(data) {
fd.value.append('rewardSalaryFile', data.file)
}
function attendanceUpload(data) {
fd.value.append('attendanceFile', data.file)
}
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,89 @@
<template>
<Dialog v-model="dialogVisible" title="导入历史工资条" style="width: 600px">
<el-form :model="formData" ref="formRef" :rules="rules" label-width="80px">
<el-row :gutter="20">
<el-col :span="24" :offset="0">
<el-form-item label="年月" prop="period">
<el-date-picker
v-model="formData.period"
type="month"
placeholder="选择年月"
format="YYYY-MM"
value-format="YYYY-MM"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24" :offset="0">
<el-form-item label="导入工资条">
<UploadFile
v-model="formData.rewardSalaryFile"
:limit="1"
:fileType="['xls', 'xlsx']"
accept=".xls,.xlsx"
:isShowTip="false"
>
<template #tip>
<div>
<el-link type="primary" :underline="false" @click="downloadTemplateFile(3)">
点击下载模板文件
</el-link>
</div>
</template>
</UploadFile>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" :disabled="formLoading" @click="handleSave"> 确认生成 </el-button>
</span>
</template>
</Dialog>
</template>
<script setup name="DialogSalaryImport">
import * as SalaryApi from '@/api/home/salary.js'
import { downloadFile } from '@/utils/download'
const dialogVisible = ref(false)
const formData = ref({})
const formLoading = ref(false)
const rules = {
period: { required: true, message: '年月不可为空', trigger: 'blur,change' },
attendanceFile: { required: true, message: '考勤文件不可为空', trigger: 'blur,change' }
}
function open() {
dialogVisible.value = true
resetForm()
}
function resetForm() {
formData.value = {
period: `${new Date().getFullYear()}-${new Date().getMonth() + 1}`,
rewardSalaryFile: '',
attendanceFile: ''
}
}
defineExpose({ open })
function handleSave() {}
async function downloadTemplateFile(type) {
let data
if (type == 3) {
data = await SalaryApi.getLinkHistorySalary()
downloadFile(data, '工资条模板.xls')
}
}
</script>
<style lang="scss" scoped></style>

@ -1,17 +1,21 @@
<template>
<div>
<el-form :model="searchForm" ref="searchFormRef" inline>
<el-form :model="searchForm" ref="searchFormRef" inline @submit.prevent>
<el-form-item>
<el-input v-model="searchForm.employeeName" placeholder="员工姓名" />
<el-input
v-model="searchForm.name"
placeholder="员工姓名"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
<el-form-item>
<el-date-picker
v-model="searchForm.month"
v-model="searchForm.yearmonth"
type="month"
placeholder="年月"
format="YYYY-MM"
value-format="YYYY-MM"
style="width: 100%"
@change="handleSearch"
/>
</el-form-item>
<el-form-item>
@ -66,26 +70,26 @@
<template #default="{ row }">
<el-input-number
v-if="row.edit"
v-model="row.jbgz"
v-model="row.allowanceSalary"
:min="0"
:controls="false"
size="small"
style="width: 65px"
/>
<span v-else> {{ row.jbgz }}</span>
<span v-else> {{ row.allowanceSalary }}</span>
</template>
</el-table-column>
<el-table-column label="提成" min-width="90px">
<template #default="{ row }">
<el-input-number
v-if="row.edit"
v-model="row.jbgz"
v-model="row.percentageSalary"
:min="0"
:controls="false"
size="small"
style="width: 65px"
/>
<span v-else> {{ row.jbgz }}</span>
<span v-else> {{ row.percentageSalary }}</span>
</template>
</el-table-column>
<el-table-column label="奖金" min-width="90px">
@ -105,13 +109,13 @@
<template #default="{ row }">
<el-input-number
v-if="row.edit"
v-model="row.jbgz"
v-model="row.fullWorkSalary"
:min="0"
:controls="false"
size="small"
style="width: 65px"
/>
<span v-else> {{ row.jbgz }}</span>
<span v-else> {{ row.fullWorkSalary }}</span>
</template>
</el-table-column>
<el-table-column label="其他" min-width="90px">
@ -127,7 +131,7 @@
<span v-else> {{ row.extraSalary }}</span>
</template>
</el-table-column>
<el-table-column label="小计" min-width="90px" />
<el-table-column label="小计" prop="salaryTotal" min-width="90px" />
</el-table-column>
<el-table-column label="应扣工资" align="center">
<el-table-column label="绩效" min-width="90px">
@ -147,13 +151,13 @@
<template #default="{ row }">
<el-input-number
v-if="row.edit"
v-model="row.jbgz"
v-model="row.leaveDayDeduct"
:min="0"
:controls="false"
size="small"
style="width: 65px"
/>
<span v-else> {{ row.jbgz }}</span>
<span v-else> {{ row.leaveDayDeduct }}</span>
</template>
</el-table-column>
<el-table-column min-width="90px">
@ -163,39 +167,39 @@
<template #default="{ row }">
<el-input-number
v-if="row.edit"
v-model="row.jbgz"
v-model="row.absenceDeduct"
:min="0"
:controls="false"
size="small"
style="width: 65px"
/>
<span v-else> {{ row.jbgz }}</span>
<span v-else> {{ row.absenceDeduct }}</span>
</template>
</el-table-column>
<el-table-column label="旷工" min-width="90px">
<template #default="{ row }">
<el-input-number
v-if="row.edit"
v-model="row.jbgz"
v-model="row.skipWorkDeduct"
:min="0"
:controls="false"
size="small"
style="width: 65px"
/>
<span v-else> {{ row.jbgz }}</span>
<span v-else> {{ row.skipWorkDeduct }}</span>
</template>
</el-table-column>
<el-table-column label="违纪" min-width="90px">
<template #default="{ row }">
<el-input-number
v-if="row.edit"
v-model="row.jbgz"
v-model="row.breachDeduct"
:min="0"
:controls="false"
size="small"
style="width: 65px"
/>
<span v-else> {{ row.jbgz }}</span>
<span v-else> {{ row.breachDeduct }}</span>
</template>
</el-table-column>
<el-table-column label="公积金" min-width="90px">
@ -228,31 +232,31 @@
<template #default="{ row }">
<el-input-number
v-if="row.edit"
v-model="row.jbgz"
v-model="row.taxDeduct"
:min="0"
:controls="false"
size="small"
style="width: 65px"
/>
<span v-else> {{ row.jbgz }}</span>
<span v-else> {{ row.taxDeduct }}</span>
</template>
</el-table-column>
<el-table-column label="其他" min-width="90px">
<template #default="{ row }">
<el-input-number
v-if="row.edit"
v-model="row.jbgz"
v-model="row.extraDeduct"
:min="0"
:controls="false"
size="small"
style="width: 65px"
/>
<span v-else> {{ row.jbgz }}</span>
<span v-else> {{ row.extraDeduct }}</span>
</template>
</el-table-column>
<el-table-column label="小计" min-width="90px" />
<el-table-column label="小计" prop="deductTotal" min-width="90px" />
</el-table-column>
<el-table-column label="实发工资" fixed="right" width="90" />
<el-table-column label="实发工资" prop="gantSalary" fixed="right" width="90" />
<el-table-column label="操作" fixed="right" width="200">
<template #default="{ row }">
<el-button
@ -288,38 +292,66 @@
</template>
</el-table-column>
</el-table>
<Pagination
:total="total"
v-model:page="searchForm.pageNo"
v-model:limit="searchForm.pageSize"
@pagination="getList"
/>
<DialogCreateSalary ref="createSalaryRef" @success="getList" />
<DialogSalaryImport ref="importSalaryRef" @success="getList" />
</div>
</template>
<script setup name="SalarySlip">
import DialogCreateSalary from './Comp/DialogCreateSalary.vue'
const searchForm = ref({})
import DialogSalaryImport from './Comp/DialogSalaryImport.vue'
import { removeNullField } from '@/utils'
import * as SalaryApi from '@/api/home/salary.js'
const searchForm = ref({
name: undefined,
yearmonth: undefined,
pageNo: 1,
pageSize: 50
})
onMounted(() => {
handleSearch()
})
function handleSearch() {
searchForm.value.pageNO = 1
searchForm.value.pageNo = 1
getList()
}
const loading = ref(false)
const tableList = ref([])
const total = ref(0)
function getList() {
tableList.value = [
{
yearmonth: '2024-06',
id: 1,
children: [
{ id: 11, name: '张三' },
{ id: 12, name: '李四' }
]
async function getList() {
loading.value = true
try {
let params = {
...searchForm.value
}
if (params.yearmonth) {
params = {
...params,
year: new Date(params.yearmonth).getFullYear(),
month: new Date(params.yearmonth).getMonth() + 1
}
}
]
total.value = 0
const data = await SalaryApi.getSalaryPage(removeNullField(params))
tableList.value = data.list
total.value = data.total
} catch (err) {
console.log(err)
} finally {
loading.value = false
}
}
const createSalaryRef = ref()
@ -327,7 +359,10 @@ function craeteSalary() {
createSalaryRef.value.open()
}
function importHistorySalary() {}
const importSalaryRef = ref()
function importHistorySalary() {
importSalaryRef.value.open()
}
function spanMethod({ row, columnIndex }) {
if (row.children && row.children.length > 0) {

Loading…
Cancel
Save