Browse Source

1

yuhongzhe
于宏哲PHP 10 months ago
parent
commit
0ed8684db6
  1. 31
      admin/src/app/api/reimbursement.ts
  2. 33
      admin/src/app/api/salary.ts
  3. 39
      admin/src/app/lang/zh-cn/reimbursement.reimbursement.json
  4. 62
      admin/src/app/lang/zh-cn/salary.salary.json
  5. 16
      admin/src/app/views/campus_person_role/campus_person_role.vue
  6. 2
      admin/src/app/views/course/components/course-edit.vue
  7. 62
      admin/src/app/views/course/course.vue
  8. 445
      admin/src/app/views/reimbursement/components/reimbursement-edit.vue
  9. 465
      admin/src/app/views/reimbursement/reimbursement.vue
  10. 621
      admin/src/app/views/salary/components/salary-edit.vue
  11. 592
      admin/src/app/views/salary/salary.vue
  12. 12
      niucloud/app/adminapi/controller/reimbursement/Reimbursement.php
  13. 32
      niucloud/app/adminapi/controller/salary/Salary.php
  14. 6
      niucloud/app/adminapi/route/reimbursement.php
  15. 8
      niucloud/app/adminapi/route/salary.php
  16. 80
      niucloud/app/model/reimbursement/Reimbursement.php
  17. 140
      niucloud/app/model/salary/Salary.php
  18. 10
      niucloud/app/service/admin/reimbursement/ReimbursementService.php
  19. 20
      niucloud/app/service/admin/salary/SalaryService.php
  20. 6
      niucloud/app/validate/reimbursement/Reimbursement.php
  21. 10
      niucloud/app/validate/salary/Salary.php

31
admin/src/app/api/reimbursement.ts

@ -1,5 +1,13 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- reimbursement // USER_CODE_BEGIN -- reimbursement
/** /**
* *
@ -7,7 +15,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getReimbursementList(params: Record<string, any>) { export function getReimbursementList(params: Record<string, any>) {
return request.get(`reimbursement/reimbursement`, { params }) return request.get(`reimbursement/reimbursement`, {params})
} }
/** /**
@ -16,7 +24,7 @@ export function getReimbursementList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getReimbursementInfo(id: number) { export function getReimbursementInfo(id: number) {
return request.get(`reimbursement/reimbursement/${id}`) return request.get(`reimbursement/reimbursement/${id}`);
} }
/** /**
@ -25,10 +33,7 @@ export function getReimbursementInfo(id: number) {
* @returns * @returns
*/ */
export function addReimbursement(params: Record<string, any>) { export function addReimbursement(params: Record<string, any>) {
return request.post('reimbursement/reimbursement', params, { return request.post('reimbursement/reimbursement', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,10 +43,7 @@ export function addReimbursement(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editReimbursement(params: Record<string, any>) { export function editReimbursement(params: Record<string, any>) {
return request.put(`reimbursement/reimbursement/${params.id}`, params, { return request.put(`reimbursement/reimbursement/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -50,10 +52,11 @@ export function editReimbursement(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteReimbursement(id: number) { export function deleteReimbursement(id: number) {
return request.delete(`reimbursement/reimbursement/${id}`, { return request.delete(`reimbursement/reimbursement/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true, }
showSuccessMessage: true,
}) export function getWithPersonnelList(params: Record<string,any>){
return request.get('reimbursement/personnel_all', {params})
} }
// USER_CODE_END -- reimbursement // USER_CODE_END -- reimbursement

33
admin/src/app/api/salary.ts

@ -1,5 +1,13 @@
import request from '@/utils/request' import request from '@/utils/request'
// USER_CODE_BEGIN -- salary // USER_CODE_BEGIN -- salary
/** /**
* *
@ -7,7 +15,7 @@ import request from '@/utils/request'
* @returns * @returns
*/ */
export function getSalaryList(params: Record<string, any>) { export function getSalaryList(params: Record<string, any>) {
return request.get(`salary/salary`, { params }) return request.get(`salary/salary`, {params})
} }
/** /**
@ -16,7 +24,7 @@ export function getSalaryList(params: Record<string, any>) {
* @returns * @returns
*/ */
export function getSalaryInfo(id: number) { export function getSalaryInfo(id: number) {
return request.get(`salary/salary/${id}`) return request.get(`salary/salary/${id}`);
} }
/** /**
@ -25,10 +33,7 @@ export function getSalaryInfo(id: number) {
* @returns * @returns
*/ */
export function addSalary(params: Record<string, any>) { export function addSalary(params: Record<string, any>) {
return request.post('salary/salary', params, { return request.post('salary/salary', params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -38,10 +43,7 @@ export function addSalary(params: Record<string, any>) {
* @returns * @returns
*/ */
export function editSalary(params: Record<string, any>) { export function editSalary(params: Record<string, any>) {
return request.put(`salary/salary/${params.id}`, params, { return request.put(`salary/salary/${params.id}`, params, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true,
showSuccessMessage: true,
})
} }
/** /**
@ -50,10 +52,13 @@ export function editSalary(params: Record<string, any>) {
* @returns * @returns
*/ */
export function deleteSalary(id: number) { export function deleteSalary(id: number) {
return request.delete(`salary/salary/${id}`, { return request.delete(`salary/salary/${id}`, { showErrorMessage: true, showSuccessMessage: true })
showErrorMessage: true, }
showSuccessMessage: true,
}) export function getWithPersonnelList(params: Record<string,any>){
return request.get('salary/personnel_all', {params})
}export function getWithDepartmentsList(params: Record<string,any>){
return request.get('salary/departments_all', {params})
} }
// USER_CODE_END -- salary // USER_CODE_END -- salary

39
admin/src/app/lang/zh-cn/reimbursement.reimbursement.json

@ -1,21 +1,20 @@
{ {
"id": "报销编号", "applicantId":"申请人",
"idPlaceholder": "请输入报销编号", "applicantIdPlaceholder":"请输入申请人",
"applicantId": "申请人ID", "amount":"报销金额",
"applicantIdPlaceholder": "请输入申请人ID", "amountPlaceholder":"请输入报销金额",
"amount": "报销金额", "description":"报销描述",
"amountPlaceholder": "请输入报销金额", "descriptionPlaceholder":"请输入报销描述",
"description": "报销描述", "receiptUrl":"发票或收据",
"descriptionPlaceholder": "请输入报销描述", "receiptUrlPlaceholder":"请输入发票或收据",
"receiptUrl": "发票或收据URL", "status":"状态",
"receiptUrlPlaceholder": "请输入发票或收据URL", "statusPlaceholder":"请输入状态",
"status": "状态", "createdAt":"创建时间",
"statusPlaceholder": "请输入状态", "createdAtPlaceholder":"请输入创建时间",
"processId": "关联的审批流程ID", "updatedAt":"修改时间",
"processIdPlaceholder": "请输入关联的审批流程ID", "addReimbursement":"添加报销记录",
"addReimbursement": "添加报销记录", "updateReimbursement":"编辑报销记录",
"updateReimbursement": "编辑报销记录", "reimbursementDeleteTips":"确定要删除该数据吗?",
"reimbursementDeleteTips": "确定要删除该数据吗?", "startDate":"请选择开始时间",
"startDate": "请选择开始时间", "endDate":"请选择结束时间"
"endDate": "请选择结束时间" }
}

62
admin/src/app/lang/zh-cn/salary.salary.json

@ -1,33 +1,31 @@
{ {
"id": "工资编号", "staffId":"员工",
"idPlaceholder": "请输入工资编号", "staffIdPlaceholder":"全部",
"staffId": "员工ID", "departmentId":"部门",
"staffIdPlaceholder": "请输入员工ID", "departmentIdPlaceholder":"全部",
"baseSalary": "底薪", "baseSalary":"底薪",
"baseSalaryPlaceholder": "请输入底薪", "baseSalaryPlaceholder":"请输入底薪",
"performanceBonus": "绩效", "performanceBonus":"绩效",
"performanceBonusPlaceholder": "请输入绩效", "performanceBonusPlaceholder":"请输入绩效",
"deductions": "扣款", "deductions":"扣款",
"deductionsPlaceholder": "请输入扣款", "deductionsPlaceholder":"请输入扣款",
"otherSubsidies": "其他补贴", "otherSubsidies":"其他补贴",
"otherSubsidiesPlaceholder": "请输入其他补贴", "otherSubsidiesPlaceholder":"请输入其他补贴",
"netSalary": "实发工资", "netSalary":"实发工资",
"netSalaryPlaceholder": "请输入实发工资", "paymentStatus":"发放状态",
"paymentStatus": "发放状态", "paymentStatusPlaceholder":"请输入发放状态",
"paymentStatusPlaceholder": "请输入发放状态", "paymentMethod":"发放方式",
"paymentMethod": "发放方式", "paymentMethodPlaceholder":"请输入发放方式",
"paymentMethodPlaceholder": "请输入发放方式", "remarks":"备注",
"remarks": "备注", "remarksPlaceholder":"请输入备注",
"remarksPlaceholder": "请输入备注", "salaryMonth":"工资月份",
"salaryMonth": "工资月份", "salaryMonthPlaceholder":"请输入工资月份",
"salaryMonthPlaceholder": "请输入工资月份", "createdAt":"创建时间",
"departmentId": "部门ID", "createdAtPlaceholder":"请输入创建时间",
"departmentIdPlaceholder": "请输入部门ID", "updatedAt":"修改时间",
"processId": "关联的审批流程ID", "addSalary":"添加工资",
"processIdPlaceholder": "请输入关联的审批流程ID", "updateSalary":"编辑工资",
"addSalary": "添加工资", "salaryDeleteTips":"确定要删除该数据吗?",
"updateSalary": "编辑工资", "startDate":"请选择开始时间",
"salaryDeleteTips": "确定要删除该数据吗?", "endDate":"请选择结束时间"
"startDate": "请选择开始时间", }
"endDate": "请选择结束时间"
}

16
admin/src/app/views/campus_person_role/campus_person_role.vue

@ -65,7 +65,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('deptId')" prop="dept_id"> <!-- <el-form-item :label="t('deptId')" prop="dept_id">
<el-select <el-select
class="w-[280px]" class="w-[280px]"
v-model="campusPersonRoleTable.searchParam.dept_id" v-model="campusPersonRoleTable.searchParam.dept_id"
@ -79,7 +79,7 @@
:value="item['id']" :value="item['id']"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item> -->
<el-form-item> <el-form-item>
<el-button type="primary" @click="loadCampusPersonRoleList()">{{ <el-button type="primary" @click="loadCampusPersonRoleList()">{{
@ -186,7 +186,6 @@ const route = useRoute()
const pageName = route.meta.title const pageName = route.meta.title
// ?dept_id=1 // ?dept_id=1
const dept_id = pageName == '市场人员列表' ? 1 : 2;
let campusPersonRoleTable = reactive({ let campusPersonRoleTable = reactive({
page: 1, page: 1,
limit: 10, limit: 10,
@ -197,10 +196,19 @@ let campusPersonRoleTable = reactive({
campus_id: '', campus_id: '',
person_id: '', person_id: '',
role_id: '', role_id: '',
dept_id: dept_id, dept_id: '',
}, },
}) })
if(pageName == '市场人员列表'){
campusPersonRoleTable.searchParam.dept_id = 1;
}else if(pageName == '销售人员列表'){
campusPersonRoleTable.searchParam.dept_id = 2;
}else if(pageName == '教练管理'){
campusPersonRoleTable.searchParam.role_id = 5;
}
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
// //

2
admin/src/app/views/course/components/course-edit.vue

@ -33,7 +33,7 @@
v-for="(item, index) in courseTypeList" v-for="(item, index) in courseTypeList"
:key="index" :key="index"
:label="item.name" :label="item.name"
:value="item.value" :value="item.name"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>

62
admin/src/app/views/course/course.vue

@ -31,56 +31,12 @@
<el-option <el-option
v-for="item in courseTypeList" v-for="item in courseTypeList"
:key="item.value" :key="item.value"
:label="item.label" :label="item.name"
:value="item.value" :value="item.name"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('duration')" prop="duration">
<el-input
v-model="courseTable.searchParam.duration"
:placeholder="t('durationPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('sessionCount')" prop="session_count">
<el-input
v-model="courseTable.searchParam.session_count"
:placeholder="t('sessionCountPlaceholder')"
/>
</el-form-item>
<el-form-item
:label="t('singleSessionCount')"
prop="single_session_count"
>
<el-input
v-model="courseTable.searchParam.single_session_count"
:placeholder="t('singleSessionCountPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('price')" prop="price">
<el-input
v-model="courseTable.searchParam.price"
:placeholder="t('pricePlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('internalReminder')" prop="internal_reminder">
<el-input
v-model="courseTable.searchParam.internal_reminder"
:placeholder="t('internalReminderPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('customerReminder')" prop="customer_reminder">
<el-input
v-model="courseTable.searchParam.customer_reminder"
:placeholder="t('customerReminderPlaceholder')"
/>
</el-form-item>
<el-form-item :label="t('remarks')" prop="remarks">
<el-input
v-model="courseTable.searchParam.remarks"
:placeholder="t('remarksPlaceholder')"
/>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="loadCourseList()">{{ <el-button type="primary" @click="loadCourseList()">{{
@ -227,7 +183,17 @@ let courseTable = reactive({
remarks: '', remarks: '',
}, },
}) })
const courseTypeList = useDictionary('course_type') // const courseTypeList = useDictionary('course_type')
const courseTypeList = ref([])
const getcourseTypeList = async () => {
courseTypeList.value = await (
await useDictionary('course_type')
).data.dictionary
}
getcourseTypeList()
const searchFormRef = ref<FormInstance>() const searchFormRef = ref<FormInstance>()
// //

445
admin/src/app/views/reimbursement/components/reimbursement-edit.vue

@ -1,236 +1,209 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateReimbursement') : t('addReimbursement')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
v-model="showDialog" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
:title="formData.id ? t('updateReimbursement') : t('addReimbursement')" <el-form-item :label="t('applicantId')" prop="applicant_id">
width="50%" <el-select class="input-width" v-model="formData.applicant_id" clearable :placeholder="t('applicantIdPlaceholder')">
class="diy-dialog-wrap" <el-option label="请选择" value=""></el-option>
:destroy-on-close="true" <el-option
> v-for="(item, index) in applicantIdList"
<el-form :key="index"
:model="formData" :label="item['name']"
label-width="120px" :value="item['id']"
ref="formRef" />
:rules="formRules" </el-select>
class="page-form" </el-form-item>
v-loading="loading"
> <el-form-item :label="t('amount')" prop="amount">
<el-form-item :label="t('applicantId')" prop="applicant_id"> <el-input v-model="formData.amount" clearable :placeholder="t('amountPlaceholder')" class="input-width" />
<el-input </el-form-item>
v-model="formData.applicant_id"
clearable <el-form-item :label="t('description')" prop="description">
:placeholder="t('applicantIdPlaceholder')" <el-input v-model="formData.description" clearable :placeholder="t('descriptionPlaceholder')" class="input-width" />
class="input-width" </el-form-item>
/>
</el-form-item> <el-form-item :label="t('receiptUrl')">
<upload-file v-model="formData.receipt_url" />
<el-form-item :label="t('amount')" prop="amount"> </el-form-item>
<el-input
v-model="formData.amount" <el-form-item :label="t('status')" prop="status">
clearable <el-select class="input-width" v-model="formData.status" clearable :placeholder="t('statusPlaceholder')">
:placeholder="t('amountPlaceholder')" <el-option label="请选择" value=""></el-option>
class="input-width" <el-option
/> v-for="(item, index) in statusList"
</el-form-item> :key="index"
:label="item.name"
<el-form-item :label="t('description')" prop="description"> :value="item.value"
<el-input />
v-model="formData.description" </el-select>
clearable </el-form-item>
:placeholder="t('descriptionPlaceholder')"
class="input-width" </el-form>
/>
</el-form-item> <template #footer>
<span class="dialog-footer">
<el-form-item :label="t('receiptUrl')"> <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-input <el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
v-model="formData.receipt_url" t('confirm')
clearable }}</el-button>
:placeholder="t('receiptUrlPlaceholder')" </span>
class="input-width" </template>
/> </el-dialog>
</el-form-item> </template>
<el-form-item :label="t('status')" prop="status"> <script lang="ts" setup>
<el-input import { ref, reactive, computed, watch } from 'vue'
v-model="formData.status" import { useDictionary } from '@/app/api/dict'
clearable import { t } from '@/lang'
:placeholder="t('statusPlaceholder')" import type { FormInstance } from 'element-plus'
class="input-width" import { addReimbursement, editReimbursement, getReimbursementInfo, getWithPersonnelList } from '@/app/api/reimbursement'
/>
</el-form-item> let showDialog = ref(false)
const loading = ref(false)
<el-form-item :label="t('processId')" prop="process_id">
<el-input /**
v-model="formData.process_id" * 表单数据
clearable */
:placeholder="t('processIdPlaceholder')" const initialFormData = {
class="input-width" id: '',
/> applicant_id: '',
</el-form-item> amount: '',
</el-form> description: '',
receipt_url: '',
<template #footer> status: '',
<span class="dialog-footer"> }
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button> const formData: Record<string, any> = reactive({ ...initialFormData })
<el-button
type="primary" const formRef = ref<FormInstance>()
:loading="loading"
@click="confirm(formRef)" //
>{{ t('confirm') }}</el-button const formRules = computed(() => {
> return {
</span> applicant_id: [
</template> { required: true, message: t('applicantIdPlaceholder'), trigger: 'blur' },
</el-dialog>
</template> ]
,
<script lang="ts" setup> amount: [
import { ref, reactive, computed, watch } from 'vue' { required: true, message: t('amountPlaceholder'), trigger: 'blur' },
import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang' ]
import type { FormInstance } from 'element-plus' ,
import { description: [
addReimbursement, { required: true, message: t('descriptionPlaceholder'), trigger: 'blur' },
editReimbursement,
getReimbursementInfo, ]
} from '@/app/api/reimbursement' ,
receipt_url: [
let showDialog = ref(false) { required: true, message: t('receiptUrlPlaceholder'), trigger: 'blur' },
const loading = ref(false)
]
/** ,
* 表单数据 status: [
*/ { required: true, message: t('statusPlaceholder'), trigger: 'blur' },
const initialFormData = {
id: '', ]
applicant_id: '', ,
amount: '', }
description: '', })
receipt_url: '',
status: '', const emit = defineEmits(['complete'])
process_id: '',
} /**
const formData: Record<string, any> = reactive({ ...initialFormData }) * 确认
* @param formEl
const formRef = ref<FormInstance>() */
const confirm = async (formEl: FormInstance | undefined) => {
// if (loading.value || !formEl) return
const formRules = computed(() => { let save = formData.id ? editReimbursement : addReimbursement
return {
applicant_id: [ await formEl.validate(async (valid) => {
{ required: true, message: t('applicantIdPlaceholder'), trigger: 'blur' }, if (valid) {
], loading.value = true
amount: [
{ required: true, message: t('amountPlaceholder'), trigger: 'blur' }, let data = formData
],
description: [ save(data).then(res => {
{ required: true, message: t('descriptionPlaceholder'), trigger: 'blur' }, loading.value = false
], showDialog.value = false
receipt_url: [ emit('complete')
{ required: true, message: t('receiptUrlPlaceholder'), trigger: 'blur' }, }).catch(err => {
], loading.value = false
status: [ })
{ required: true, message: t('statusPlaceholder'), trigger: 'blur' }, }
], })
process_id: [ }
{ required: true, message: t('processIdPlaceholder'), trigger: 'blur' },
], //
} let statusList = ref([])
}) const statusDictList = async () => {
statusList.value = await (await useDictionary('sp_status')).data.dictionary
const emit = defineEmits(['complete']) }
statusDictList();
/** watch(() => statusList.value, () => { formData.status = statusList.value[0].value })
* 确认
* @param formEl
*/ const applicantIdList = ref([] as any[])
const confirm = async (formEl: FormInstance | undefined) => { const setApplicantIdList = async () => {
if (loading.value || !formEl) return applicantIdList.value = await (await getWithPersonnelList({})).data
let save = formData.id ? editReimbursement : addReimbursement }
setApplicantIdList()
await formEl.validate(async (valid) => { const setFormData = async (row: any = null) => {
if (valid) { Object.assign(formData, initialFormData)
loading.value = true loading.value = true
if(row){
let data = formData const data = await (await getReimbursementInfo(row.id)).data
if (data) Object.keys(formData).forEach((key: string) => {
save(data) if (data[key] != undefined) formData[key] = data[key]
.then((res) => { })
loading.value = false }
showDialog.value = false loading.value = false
emit('complete') }
})
.catch((err) => { //
loading.value = false const mobileVerify = (rule: any, value: any, callback: any) => {
}) if (value && !/^1[3-9]\d{9}$/.test(value)) {
} callback(new Error(t('generateMobile')))
}) } else {
} callback()
}
// }
const setFormData = async (row: any = null) => { //
Object.assign(formData, initialFormData) const idCardVerify = (rule: any, value: any, callback: any) => {
loading.value = true if (value && !/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(value)) {
if (row) { callback(new Error(t('generateIdCard')))
const data = await (await getReimbursementInfo(row.id)).data } else {
if (data) callback()
Object.keys(formData).forEach((key: string) => { }
if (data[key] != undefined) formData[key] = data[key] }
})
} //
loading.value = false const emailVerify = (rule: any, value: any, callback: any) => {
} if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
// } else {
const mobileVerify = (rule: any, value: any, callback: any) => { callback()
if (value && !/^1[3-9]\d{9}$/.test(value)) { }
callback(new Error(t('generateMobile'))) }
} else {
callback() //
} const numberVerify = (rule: any, value: any, callback: any) => {
} if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
// } else {
const idCardVerify = (rule: any, value: any, callback: any) => { callback()
if ( }
value && }
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(
value defineExpose({
) showDialog,
) { setFormData
callback(new Error(t('generateIdCard'))) })
} else { </script>
callback()
} <style lang="scss" scoped></style>
} <style lang="scss">
.diy-dialog-wrap .el-form-item__label{
// height: auto !important;
const emailVerify = (rule: any, value: any, callback: any) => { }
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) { </style>
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

465
admin/src/app/views/reimbursement/reimbursement.vue

@ -1,265 +1,200 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addReimbursement') }} <el-button type="primary" @click="addEvent">
</el-button> {{ t('addReimbursement') }}
</div> </el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="reimbursementTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('status')" prop="status">
:inline="true" <el-select class="w-[280px]" v-model="reimbursementTable.searchParam.status" clearable :placeholder="t('statusPlaceholder')">
:model="reimbursementTable.searchParam" <el-option label="全部" value=""></el-option>
ref="searchFormRef" <el-option
> v-for="(item, index) in statusList"
<el-form-item :label="t('applicantId')" prop="applicant_id"> :key="index"
<el-input :label="item.name"
v-model="reimbursementTable.searchParam.applicant_id" :value="item.value"
:placeholder="t('applicantIdPlaceholder')" />
/> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('amount')" prop="amount">
<el-input <el-form-item :label="t('createdAt')" prop="created_at">
v-model="reimbursementTable.searchParam.amount" <el-date-picker v-model="reimbursementTable.searchParam.created_at" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:placeholder="t('amountPlaceholder')" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('description')" prop="description"> <el-form-item>
<el-input <el-button type="primary" @click="loadReimbursementList()">{{ t('search') }}</el-button>
v-model="reimbursementTable.searchParam.description" <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
:placeholder="t('descriptionPlaceholder')" </el-form-item>
/> </el-form>
</el-form-item> </el-card>
<el-form-item :label="t('receiptUrl')" prop="receipt_url">
<el-input <div class="mt-[10px]">
v-model="reimbursementTable.searchParam.receipt_url" <el-table :data="reimbursementTable.data" size="large" v-loading="reimbursementTable.loading">
:placeholder="t('receiptUrlPlaceholder')" <template #empty>
/> <span>{{ !reimbursementTable.loading ? t('emptyData') : '' }}</span>
</el-form-item> </template>
<el-form-item :label="t('status')" prop="status"> <el-table-column prop="applicant_id_name" :label="t('applicantId')" min-width="120" :show-overflow-tooltip="true"/>
<el-input
v-model="reimbursementTable.searchParam.status" <el-table-column prop="amount" :label="t('amount')" min-width="120" :show-overflow-tooltip="true"/>
:placeholder="t('statusPlaceholder')"
/> <el-table-column prop="description" :label="t('description')" min-width="120" :show-overflow-tooltip="true"/>
</el-form-item>
<el-form-item :label="t('processId')" prop="process_id"> <el-table-column :label="t('status')" min-width="180" align="center" :show-overflow-tooltip="true">
<el-input <template #default="{ row }">
v-model="reimbursementTable.searchParam.process_id" <div v-for="(item, index) in statusList">
:placeholder="t('processIdPlaceholder')" <div v-if="item.value == row.status">{{ item.name }}</div>
/> </div>
</el-form-item> </template>
</el-table-column>
<el-form-item>
<el-button type="primary" @click="loadReimbursementList()">{{ <el-table-column prop="created_at" :label="t('createdAt')" min-width="120" :show-overflow-tooltip="true"/>
t('search')
}}</el-button> <el-table-column prop="updated_at" :label="t('updatedAt')" min-width="120" :show-overflow-tooltip="true"/>
<el-button @click="resetForm(searchFormRef)">{{
t('reset') <el-table-column :label="t('operation')" fixed="right" min-width="120">
}}</el-button> <template #default="{ row }">
</el-form-item> <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
</el-form> <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</el-card> </template>
</el-table-column>
<div class="mt-[10px]">
<el-table </el-table>
:data="reimbursementTable.data" <div class="mt-[16px] flex justify-end">
size="large" <el-pagination v-model:current-page="reimbursementTable.page" v-model:page-size="reimbursementTable.limit"
v-loading="reimbursementTable.loading" layout="total, sizes, prev, pager, next, jumper" :total="reimbursementTable.total"
> @size-change="loadReimbursementList()" @current-change="loadReimbursementList" />
<template #empty> </div>
<span>{{ !reimbursementTable.loading ? t('emptyData') : '' }}</span> </div>
</template>
<el-table-column <edit ref="editReimbursementDialog" @complete="loadReimbursementList" />
prop="applicant_id" </el-card>
:label="t('applicantId')" </div>
min-width="120" </template>
:show-overflow-tooltip="true"
/> <script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
<el-table-column import { t } from '@/lang'
prop="amount" import { useDictionary } from '@/app/api/dict'
:label="t('amount')" import { getReimbursementList, deleteReimbursement, getWithPersonnelList } from '@/app/api/reimbursement'
min-width="120" import { img } from '@/utils/common'
:show-overflow-tooltip="true" import { ElMessageBox,FormInstance } from 'element-plus'
/> import Edit from '@/app/views/reimbursement/components/reimbursement-edit.vue'
import { useRoute } from 'vue-router'
<el-table-column const route = useRoute()
prop="description" const pageName = route.meta.title;
:label="t('description')"
min-width="120" let reimbursementTable = reactive({
:show-overflow-tooltip="true" page: 1,
/> limit: 10,
total: 0,
<el-table-column loading: true,
prop="receipt_url" data: [],
:label="t('receiptUrl')" searchParam:{
min-width="120" "status":"",
:show-overflow-tooltip="true" "created_at":[]
/> }
})
<el-table-column
prop="status" const searchFormRef = ref<FormInstance>()
:label="t('status')"
min-width="120" //
:show-overflow-tooltip="true" const selectData = ref<any[]>([])
/>
//
<el-table-column const statusList = ref([] as any[])
prop="process_id" const statusDictList = async () => {
:label="t('processId')" statusList.value = await (await useDictionary('sp_status')).data.dictionary
min-width="120" }
:show-overflow-tooltip="true" statusDictList();
/>
/**
<el-table-column * 获取报销记录列表
:label="t('operation')" */
fixed="right" const loadReimbursementList = (page: number = 1) => {
min-width="120" reimbursementTable.loading = true
> reimbursementTable.page = page
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{ getReimbursementList({
t('edit') page: reimbursementTable.page,
}}</el-button> limit: reimbursementTable.limit,
<el-button type="primary" link @click="deleteEvent(row.id)">{{ ...reimbursementTable.searchParam
t('delete') }).then(res => {
}}</el-button> reimbursementTable.loading = false
</template> reimbursementTable.data = res.data.data
</el-table-column> reimbursementTable.total = res.data.total
</el-table> }).catch(() => {
<div class="mt-[16px] flex justify-end"> reimbursementTable.loading = false
<el-pagination })
v-model:current-page="reimbursementTable.page" }
v-model:page-size="reimbursementTable.limit" loadReimbursementList()
layout="total, sizes, prev, pager, next, jumper"
:total="reimbursementTable.total" const editReimbursementDialog: Record<string, any> | null = ref(null)
@size-change="loadReimbursementList()"
@current-change="loadReimbursementList" /**
/> * 添加报销记录
</div> */
</div> const addEvent = () => {
editReimbursementDialog.value.setFormData()
<edit ref="editReimbursementDialog" @complete="loadReimbursementList" /> editReimbursementDialog.value.showDialog = true
</el-card> }
</div>
</template> /**
* 编辑报销记录
<script lang="ts" setup> * @param data
import { reactive, ref, watch } from 'vue' */
import { t } from '@/lang' const editEvent = (data: any) => {
import { useDictionary } from '@/app/api/dict' editReimbursementDialog.value.setFormData(data)
import { editReimbursementDialog.value.showDialog = true
getReimbursementList, }
deleteReimbursement,
} from '@/app/api/reimbursement' /**
import { img } from '@/utils/common' * 删除报销记录
import { ElMessageBox, FormInstance } from 'element-plus' */
import Edit from '@/app/views/reimbursement/components/reimbursement-edit.vue' const deleteEvent = (id: number) => {
import { useRoute } from 'vue-router' ElMessageBox.confirm(t('reimbursementDeleteTips'), t('warning'),
const route = useRoute() {
const pageName = route.meta.title confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
let reimbursementTable = reactive({ type: 'warning',
page: 1, }
limit: 10, ).then(() => {
total: 0, deleteReimbursement(id).then(() => {
loading: true, loadReimbursementList()
data: [], }).catch(() => {
searchParam: { })
applicant_id: '', })
amount: '', }
description: '',
receipt_url: '',
status: '', const applicantIdList = ref([])
process_id: '', const setApplicantIdList = async () => {
}, applicantIdList.value = await (await getWithPersonnelList({})).data
}) }
setApplicantIdList()
const searchFormRef = ref<FormInstance>()
const resetForm = (formEl: FormInstance | undefined) => {
// if (!formEl) return
const selectData = ref<any[]>([]) formEl.resetFields()
loadReimbursementList()
// }
</script>
/**
* 获取报销记录列表 <style lang="scss" scoped>
*/ /* 多行超出隐藏 */
const loadReimbursementList = (page: number = 1) => { .multi-hidden {
reimbursementTable.loading = true word-break: break-all;
reimbursementTable.page = page text-overflow: ellipsis;
overflow: hidden;
getReimbursementList({ display: -webkit-box;
page: reimbursementTable.page, -webkit-line-clamp: 2;
limit: reimbursementTable.limit, -webkit-box-orient: vertical;
...reimbursementTable.searchParam, }
}) </style>
.then((res) => {
reimbursementTable.loading = false
reimbursementTable.data = res.data.data
reimbursementTable.total = res.data.total
})
.catch(() => {
reimbursementTable.loading = false
})
}
loadReimbursementList()
const editReimbursementDialog: Record<string, any> | null = ref(null)
/**
* 添加报销记录
*/
const addEvent = () => {
editReimbursementDialog.value.setFormData()
editReimbursementDialog.value.showDialog = true
}
/**
* 编辑报销记录
* @param data
*/
const editEvent = (data: any) => {
editReimbursementDialog.value.setFormData(data)
editReimbursementDialog.value.showDialog = true
}
/**
* 删除报销记录
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('reimbursementDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteReimbursement(id)
.then(() => {
loadReimbursementList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadReimbursementList()
}
</script>
<style lang="scss" scoped>
/* 多行超出隐藏 */
.multi-hidden {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>

621
admin/src/app/views/salary/components/salary-edit.vue

@ -1,330 +1,291 @@
<template> <template>
<el-dialog <el-dialog v-model="showDialog" :title="formData.id ? t('updateSalary') : t('addSalary')" width="50%" class="diy-dialog-wrap" :destroy-on-close="true">
v-model="showDialog" <el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
:title="formData.id ? t('updateSalary') : t('addSalary')" <el-form-item :label="t('staffId')" prop="staff_id">
width="50%" <el-select class="input-width" v-model="formData.staff_id" clearable :placeholder="t('staffIdPlaceholder')">
class="diy-dialog-wrap" <el-option label="请选择" value=""></el-option>
:destroy-on-close="true" <el-option
> v-for="(item, index) in staffIdList"
<el-form :key="index"
:model="formData" :label="item['name']"
label-width="120px" :value="item['id']"
ref="formRef" />
:rules="formRules" </el-select>
class="page-form" </el-form-item>
v-loading="loading"
> <el-form-item :label="t('departmentId')" prop="department_id">
<el-form-item :label="t('staffId')" prop="staff_id"> <el-select class="input-width" v-model="formData.department_id" clearable :placeholder="t('departmentIdPlaceholder')">
<el-input <el-option label="请选择" value=""></el-option>
v-model="formData.staff_id" <el-option
clearable v-for="(item, index) in departmentIdList"
:placeholder="t('staffIdPlaceholder')" :key="index"
class="input-width" :label="item['department_name']"
/> :value="item['id']"
</el-form-item> />
</el-select>
<el-form-item :label="t('baseSalary')" prop="base_salary"> </el-form-item>
<el-input
v-model="formData.base_salary" <el-form-item :label="t('baseSalary')" prop="base_salary">
clearable <el-input v-model="formData.base_salary" clearable :placeholder="t('baseSalaryPlaceholder')" class="input-width" />
:placeholder="t('baseSalaryPlaceholder')" </el-form-item>
class="input-width"
/> <el-form-item :label="t('performanceBonus')" prop="performance_bonus">
</el-form-item> <el-input v-model="formData.performance_bonus" clearable :placeholder="t('performanceBonusPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('performanceBonus')" prop="performance_bonus">
<el-input <el-form-item :label="t('deductions')" prop="deductions">
v-model="formData.performance_bonus" <el-input v-model="formData.deductions" clearable :placeholder="t('deductionsPlaceholder')" class="input-width" />
clearable </el-form-item>
:placeholder="t('performanceBonusPlaceholder')"
class="input-width" <el-form-item :label="t('otherSubsidies')" prop="other_subsidies">
/> <el-input v-model="formData.other_subsidies" clearable :placeholder="t('otherSubsidiesPlaceholder')" class="input-width" />
</el-form-item> </el-form-item>
<el-form-item :label="t('deductions')" prop="deductions"> <el-form-item :label="t('paymentStatus')" prop="payment_status">
<el-input <el-select class="input-width" v-model="formData.payment_status" clearable :placeholder="t('paymentStatusPlaceholder')">
v-model="formData.deductions" <el-option label="请选择" value=""></el-option>
clearable <el-option
:placeholder="t('deductionsPlaceholder')" v-for="(item, index) in payment_statusList"
class="input-width" :key="index"
/> :label="item.name"
</el-form-item> :value="item.value"
/>
<el-form-item :label="t('otherSubsidies')"> </el-select>
<el-input </el-form-item>
v-model="formData.other_subsidies"
clearable <el-form-item :label="t('paymentMethod')" prop="payment_method">
:placeholder="t('otherSubsidiesPlaceholder')" <el-select class="input-width" v-model="formData.payment_method" clearable :placeholder="t('paymentMethodPlaceholder')">
class="input-width" <el-option label="请选择" value=""></el-option>
/> <el-option
</el-form-item> v-for="(item, index) in payment_methodList"
:key="index"
<el-form-item :label="t('netSalary')"> :label="item.name"
<el-input :value="item.value"
v-model="formData.net_salary" />
clearable </el-select>
:placeholder="t('netSalaryPlaceholder')" </el-form-item>
class="input-width"
/> <el-form-item :label="t('remarks')" >
</el-form-item> <el-input v-model="formData.remarks" type="textarea" rows="4" clearable :placeholder="t('remarksPlaceholder')" class="input-width"/>
</el-form-item>
<el-form-item :label="t('paymentStatus')" prop="payment_status"> <el-form-item :label="t('salaryMonth')" prop="salary_month" class="input-width">
<el-input <el-date-picker
v-model="formData.payment_status" class="flex-1 !flex"
clearable v-model="formData.salary_month"
:placeholder="t('paymentStatusPlaceholder')" clearable
class="input-width" type="date"
/> value-format="YYYY-MM-DD"
</el-form-item> :placeholder="t('salaryMonthPlaceholder')">
</el-date-picker>
<el-form-item :label="t('paymentMethod')"> </el-form-item>
<el-input </el-form>
v-model="formData.payment_method"
clearable <template #footer>
:placeholder="t('paymentMethodPlaceholder')" <span class="dialog-footer">
class="input-width" <el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
/> <el-button type="primary" :loading="loading" @click="confirm(formRef)">{{
</el-form-item> t('confirm')
}}</el-button>
<el-form-item :label="t('remarks')"> </span>
<el-input </template>
v-model="formData.remarks" </el-dialog>
clearable </template>
:placeholder="t('remarksPlaceholder')"
class="input-width" <script lang="ts" setup>
/> import { ref, reactive, computed, watch } from 'vue'
</el-form-item> import { useDictionary } from '@/app/api/dict'
import { t } from '@/lang'
<el-form-item :label="t('salaryMonth')" prop="salary_month"> import type { FormInstance } from 'element-plus'
<el-input import { addSalary, editSalary, getSalaryInfo, getWithPersonnelList, getWithDepartmentsList } from '@/app/api/salary'
v-model="formData.salary_month"
clearable let showDialog = ref(false)
:placeholder="t('salaryMonthPlaceholder')" const loading = ref(false)
class="input-width"
/> /**
</el-form-item> * 表单数据
*/
<el-form-item :label="t('departmentId')"> const initialFormData = {
<el-input id: '',
v-model="formData.department_id" staff_id: '',
clearable department_id: '',
:placeholder="t('departmentIdPlaceholder')" base_salary: '',
class="input-width" performance_bonus: '',
/> deductions: '',
</el-form-item> other_subsidies: '',
payment_status: '',
<el-form-item :label="t('processId')"> payment_method: '',
<el-input remarks: '',
v-model="formData.process_id" salary_month: '',
clearable }
:placeholder="t('processIdPlaceholder')" const formData: Record<string, any> = reactive({ ...initialFormData })
class="input-width"
/> const formRef = ref<FormInstance>()
</el-form-item>
</el-form> //
const formRules = computed(() => {
<template #footer> return {
<span class="dialog-footer"> staff_id: [
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button> { required: true, message: t('staffIdPlaceholder'), trigger: 'blur' },
<el-button
type="primary" ]
:loading="loading" ,
@click="confirm(formRef)" department_id: [
>{{ t('confirm') }}</el-button { required: true, message: t('departmentIdPlaceholder'), trigger: 'blur' },
>
</span> ]
</template> ,
</el-dialog> base_salary: [
</template> { required: true, message: t('baseSalaryPlaceholder'), trigger: 'blur' },
<script lang="ts" setup> ]
import { ref, reactive, computed, watch } from 'vue' ,
import { useDictionary } from '@/app/api/dict' performance_bonus: [
import { t } from '@/lang' { required: true, message: t('performanceBonusPlaceholder'), trigger: 'blur' },
import type { FormInstance } from 'element-plus'
import { addSalary, editSalary, getSalaryInfo } from '@/app/api/salary' ]
,
let showDialog = ref(false) deductions: [
const loading = ref(false) { required: true, message: t('deductionsPlaceholder'), trigger: 'blur' },
/** ]
* 表单数据 ,
*/ other_subsidies: [
const initialFormData = { { required: true, message: t('otherSubsidiesPlaceholder'), trigger: 'blur' },
id: '',
staff_id: '', ]
base_salary: '', ,
performance_bonus: '', payment_status: [
deductions: '', { required: true, message: t('paymentStatusPlaceholder'), trigger: 'blur' },
other_subsidies: '',
net_salary: '', ]
payment_status: '', ,
payment_method: '', payment_method: [
remarks: '', { required: true, message: t('paymentMethodPlaceholder'), trigger: 'blur' },
salary_month: '',
department_id: '', ]
process_id: '', ,
} remarks: [
const formData: Record<string, any> = reactive({ ...initialFormData }) { required: true, message: t('remarksPlaceholder'), trigger: 'blur' },
const formRef = ref<FormInstance>() ]
,
// salary_month: [
const formRules = computed(() => { { required: true, message: t('salaryMonthPlaceholder'), trigger: 'blur' },
return {
staff_id: [ ]
{ required: true, message: t('staffIdPlaceholder'), trigger: 'blur' }, ,
], }
base_salary: [ })
{ required: true, message: t('baseSalaryPlaceholder'), trigger: 'blur' },
], const emit = defineEmits(['complete'])
performance_bonus: [
{ /**
required: true, * 确认
message: t('performanceBonusPlaceholder'), * @param formEl
trigger: 'blur', */
}, const confirm = async (formEl: FormInstance | undefined) => {
], if (loading.value || !formEl) return
deductions: [ let save = formData.id ? editSalary : addSalary
{ required: true, message: t('deductionsPlaceholder'), trigger: 'blur' },
], await formEl.validate(async (valid) => {
other_subsidies: [ if (valid) {
{ loading.value = true
required: true,
message: t('otherSubsidiesPlaceholder'), let data = formData
trigger: 'blur',
}, save(data).then(res => {
], loading.value = false
net_salary: [ showDialog.value = false
{ required: true, message: t('netSalaryPlaceholder'), trigger: 'blur' }, emit('complete')
], }).catch(err => {
payment_status: [ loading.value = false
{ })
required: true, }
message: t('paymentStatusPlaceholder'), })
trigger: 'blur', }
},
], //
payment_method: [ let payment_statusList = ref([])
{ const payment_statusDictList = async () => {
required: true, payment_statusList.value = await (await useDictionary('payment_status')).data.dictionary
message: t('paymentMethodPlaceholder'), }
trigger: 'blur', payment_statusDictList();
}, watch(() => payment_statusList.value, () => { formData.payment_status = payment_statusList.value[0].value })
], let payment_methodList = ref([])
remarks: [ const payment_methodDictList = async () => {
{ required: true, message: t('remarksPlaceholder'), trigger: 'blur' }, payment_methodList.value = await (await useDictionary('payment_method')).data.dictionary
], }
salary_month: [ payment_methodDictList();
{ required: true, message: t('salaryMonthPlaceholder'), trigger: 'blur' }, watch(() => payment_methodList.value, () => { formData.payment_method = payment_methodList.value[0].value })
],
department_id: [
{ const staffIdList = ref([] as any[])
required: true, const setStaffIdList = async () => {
message: t('departmentIdPlaceholder'), staffIdList.value = await (await getWithPersonnelList({})).data
trigger: 'blur', }
}, setStaffIdList()
], const departmentIdList = ref([] as any[])
process_id: [ const setDepartmentIdList = async () => {
{ required: true, message: t('processIdPlaceholder'), trigger: 'blur' }, departmentIdList.value = await (await getWithDepartmentsList({})).data
], }
} setDepartmentIdList()
}) const setFormData = async (row: any = null) => {
Object.assign(formData, initialFormData)
const emit = defineEmits(['complete']) loading.value = true
if(row){
/** const data = await (await getSalaryInfo(row.id)).data
* 确认 if (data) Object.keys(formData).forEach((key: string) => {
* @param formEl if (data[key] != undefined) formData[key] = data[key]
*/ })
const confirm = async (formEl: FormInstance | undefined) => { }
if (loading.value || !formEl) return loading.value = false
let save = formData.id ? editSalary : addSalary }
await formEl.validate(async (valid) => { //
if (valid) { const mobileVerify = (rule: any, value: any, callback: any) => {
loading.value = true if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(t('generateMobile')))
let data = formData } else {
callback()
save(data) }
.then((res) => { }
loading.value = false
showDialog.value = false //
emit('complete') const idCardVerify = (rule: any, value: any, callback: any) => {
}) if (value && !/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(value)) {
.catch((err) => { callback(new Error(t('generateIdCard')))
loading.value = false } else {
}) callback()
} }
}) }
}
//
// const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
const setFormData = async (row: any = null) => { callback(new Error(t('generateEmail')))
Object.assign(formData, initialFormData) } else {
loading.value = true callback()
if (row) { }
const data = await (await getSalaryInfo(row.id)).data }
if (data)
Object.keys(formData).forEach((key: string) => { //
if (data[key] != undefined) formData[key] = data[key] const numberVerify = (rule: any, value: any, callback: any) => {
}) if (!Number.isInteger(value)) {
} callback(new Error(t('generateNumber')))
loading.value = false } else {
} callback()
}
// }
const mobileVerify = (rule: any, value: any, callback: any) => {
if (value && !/^1[3-9]\d{9}$/.test(value)) { defineExpose({
callback(new Error(t('generateMobile'))) showDialog,
} else { setFormData
callback() })
} </script>
}
<style lang="scss" scoped></style>
// <style lang="scss">
const idCardVerify = (rule: any, value: any, callback: any) => { .diy-dialog-wrap .el-form-item__label{
if ( height: auto !important;
value && }
!/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test( </style>
value
)
) {
callback(new Error(t('generateIdCard')))
} else {
callback()
}
}
//
const emailVerify = (rule: any, value: any, callback: any) => {
if (value && !/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(value)) {
callback(new Error(t('generateEmail')))
} else {
callback()
}
}
//
const numberVerify = (rule: any, value: any, callback: any) => {
if (!Number.isInteger(value)) {
callback(new Error(t('generateNumber')))
} else {
callback()
}
}
defineExpose({
showDialog,
setFormData,
})
</script>
<style lang="scss" scoped></style>
<style lang="scss">
.diy-dialog-wrap .el-form-item__label {
height: auto !important;
}
</style>

592
admin/src/app/views/salary/salary.vue

@ -1,346 +1,246 @@
<template> <template>
<div class="main-container"> <div class="main-container">
<el-card class="box-card !border-none" shadow="never"> <el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-lg">{{ pageName }}</span> <div class="flex justify-between items-center">
<el-button type="primary" @click="addEvent"> <span class="text-lg">{{pageName}}</span>
{{ t('addSalary') }} <el-button type="primary" @click="addEvent">
</el-button> {{ t('addSalary') }}
</div> </el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap" <el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
shadow="never" <el-form :inline="true" :model="salaryTable.searchParam" ref="searchFormRef">
>
<el-form <el-form-item :label="t('staffId')" prop="staff_id">
:inline="true" <el-select class="w-[280px]" v-model="salaryTable.searchParam.staff_id" clearable :placeholder="t('staffIdPlaceholder')">
:model="salaryTable.searchParam" <el-option
ref="searchFormRef" v-for="(item, index) in staffIdList"
> :key="index"
<el-form-item :label="t('staffId')" prop="staff_id"> :label="item['name']"
<el-input :value="item['id']"
v-model="salaryTable.searchParam.staff_id" />
:placeholder="t('staffIdPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('baseSalary')" prop="base_salary">
<el-input <el-form-item :label="t('departmentId')" prop="department_id">
v-model="salaryTable.searchParam.base_salary" <el-select class="w-[280px]" v-model="salaryTable.searchParam.department_id" clearable :placeholder="t('departmentIdPlaceholder')">
:placeholder="t('baseSalaryPlaceholder')" <el-option
/> v-for="(item, index) in departmentIdList"
</el-form-item> :key="index"
<el-form-item :label="t('performanceBonus')" prop="performance_bonus"> :label="item['department_name']"
<el-input :value="item['id']"
v-model="salaryTable.searchParam.performance_bonus" />
:placeholder="t('performanceBonusPlaceholder')" </el-select>
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('deductions')" prop="deductions">
<el-input <el-form-item :label="t('paymentStatus')" prop="payment_status">
v-model="salaryTable.searchParam.deductions" <el-select class="w-[280px]" v-model="salaryTable.searchParam.payment_status" clearable :placeholder="t('paymentStatusPlaceholder')">
:placeholder="t('deductionsPlaceholder')" <el-option label="全部" value=""></el-option>
/> <el-option
</el-form-item> v-for="(item, index) in payment_statusList"
<el-form-item :label="t('otherSubsidies')" prop="other_subsidies"> :key="index"
<el-input :label="item.name"
v-model="salaryTable.searchParam.other_subsidies" :value="item.value"
:placeholder="t('otherSubsidiesPlaceholder')" />
/> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('netSalary')" prop="net_salary">
<el-input <el-form-item :label="t('createdAt')" prop="created_at">
v-model="salaryTable.searchParam.net_salary" <el-date-picker v-model="salaryTable.searchParam.created_at" type="datetimerange" format="YYYY-MM-DD hh:mm:ss"
:placeholder="t('netSalaryPlaceholder')" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
/> </el-form-item>
</el-form-item>
<el-form-item :label="t('paymentStatus')" prop="payment_status"> <el-form-item>
<el-input <el-button type="primary" @click="loadSalaryList()">{{ t('search') }}</el-button>
v-model="salaryTable.searchParam.payment_status" <el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
:placeholder="t('paymentStatusPlaceholder')" </el-form-item>
/> </el-form>
</el-form-item> </el-card>
<el-form-item :label="t('paymentMethod')" prop="payment_method">
<el-input <div class="mt-[10px]">
v-model="salaryTable.searchParam.payment_method" <el-table :data="salaryTable.data" size="large" v-loading="salaryTable.loading">
:placeholder="t('paymentMethodPlaceholder')" <template #empty>
/> <span>{{ !salaryTable.loading ? t('emptyData') : '' }}</span>
</el-form-item> </template>
<el-form-item :label="t('remarks')" prop="remarks"> <el-table-column prop="staff_id_name" :label="t('staffId')" min-width="120" :show-overflow-tooltip="true"/>
<el-input
v-model="salaryTable.searchParam.remarks" <el-table-column prop="department_id_name" :label="t('departmentId')" min-width="120" :show-overflow-tooltip="true"/>
:placeholder="t('remarksPlaceholder')"
/> <el-table-column prop="net_salary" :label="t('netSalary')" min-width="120" :show-overflow-tooltip="true"/>
</el-form-item>
<el-form-item :label="t('salaryMonth')" prop="salary_month"> <el-table-column :label="t('paymentStatus')" min-width="180" align="center" :show-overflow-tooltip="true">
<el-input <template #default="{ row }">
v-model="salaryTable.searchParam.salary_month" <div v-for="(item, index) in payment_statusList">
:placeholder="t('salaryMonthPlaceholder')" <div v-if="item.value == row.payment_status">{{ item.name }}</div>
/> </div>
</el-form-item> </template>
<el-form-item :label="t('departmentId')" prop="department_id"> </el-table-column>
<el-input
v-model="salaryTable.searchParam.department_id" <el-table-column :label="t('paymentMethod')" min-width="180" align="center" :show-overflow-tooltip="true">
:placeholder="t('departmentIdPlaceholder')" <template #default="{ row }">
/> <div v-for="(item, index) in payment_methodList">
</el-form-item> <div v-if="item.value == row.payment_method">{{ item.name }}</div>
<el-form-item :label="t('processId')" prop="process_id"> </div>
<el-input </template>
v-model="salaryTable.searchParam.process_id" </el-table-column>
:placeholder="t('processIdPlaceholder')"
/> <el-table-column prop="salary_month" :label="t('salaryMonth')" min-width="120" :show-overflow-tooltip="true"/>
</el-form-item>
<el-table-column prop="created_at" :label="t('createdAt')" min-width="120" :show-overflow-tooltip="true"/>
<el-form-item>
<el-button type="primary" @click="loadSalaryList()">{{ <el-table-column prop="updated_at" :label="t('updatedAt')" min-width="120" :show-overflow-tooltip="true"/>
t('search')
}}</el-button> <el-table-column :label="t('operation')" fixed="right" min-width="120">
<el-button @click="resetForm(searchFormRef)">{{ <template #default="{ row }">
t('reset') <el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
}}</el-button> <el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</el-form-item> </template>
</el-form> </el-table-column>
</el-card>
</el-table>
<div class="mt-[10px]"> <div class="mt-[16px] flex justify-end">
<el-table <el-pagination v-model:current-page="salaryTable.page" v-model:page-size="salaryTable.limit"
:data="salaryTable.data" layout="total, sizes, prev, pager, next, jumper" :total="salaryTable.total"
size="large" @size-change="loadSalaryList()" @current-change="loadSalaryList" />
v-loading="salaryTable.loading" </div>
> </div>
<template #empty>
<span>{{ !salaryTable.loading ? t('emptyData') : '' }}</span> <edit ref="editSalaryDialog" @complete="loadSalaryList" />
</template> </el-card>
<el-table-column </div>
prop="staff_id" </template>
:label="t('staffId')"
min-width="120" <script lang="ts" setup>
:show-overflow-tooltip="true" import { reactive, ref, watch } from 'vue'
/> import { t } from '@/lang'
import { useDictionary } from '@/app/api/dict'
<el-table-column import { getSalaryList, deleteSalary, getWithPersonnelList, getWithDepartmentsList } from '@/app/api/salary'
prop="base_salary" import { img } from '@/utils/common'
:label="t('baseSalary')" import { ElMessageBox,FormInstance } from 'element-plus'
min-width="120" import Edit from '@/app/views/salary/components/salary-edit.vue'
:show-overflow-tooltip="true" import { useRoute } from 'vue-router'
/> const route = useRoute()
const pageName = route.meta.title;
<el-table-column
prop="performance_bonus" let salaryTable = reactive({
:label="t('performanceBonus')" page: 1,
min-width="120" limit: 10,
:show-overflow-tooltip="true" total: 0,
/> loading: true,
data: [],
<el-table-column searchParam:{
prop="deductions" "staff_id":"",
:label="t('deductions')" "department_id":"",
min-width="120" "payment_status":"",
:show-overflow-tooltip="true" "created_at":[]
/> }
})
<el-table-column
prop="other_subsidies" const searchFormRef = ref<FormInstance>()
:label="t('otherSubsidies')"
min-width="120" //
:show-overflow-tooltip="true" const selectData = ref<any[]>([])
/>
//
<el-table-column const payment_statusList = ref([] as any[])
prop="net_salary" const payment_statusDictList = async () => {
:label="t('netSalary')" payment_statusList.value = await (await useDictionary('payment_status')).data.dictionary
min-width="120" }
:show-overflow-tooltip="true" payment_statusDictList();
/> const payment_methodList = ref([] as any[])
const payment_methodDictList = async () => {
<el-table-column payment_methodList.value = await (await useDictionary('payment_method')).data.dictionary
prop="payment_status" }
:label="t('paymentStatus')" payment_methodDictList();
min-width="120"
:show-overflow-tooltip="true" /**
/> * 获取工资列表
*/
<el-table-column const loadSalaryList = (page: number = 1) => {
prop="payment_method" salaryTable.loading = true
:label="t('paymentMethod')" salaryTable.page = page
min-width="120"
:show-overflow-tooltip="true" getSalaryList({
/> page: salaryTable.page,
limit: salaryTable.limit,
<el-table-column ...salaryTable.searchParam
prop="remarks" }).then(res => {
:label="t('remarks')" salaryTable.loading = false
min-width="120" salaryTable.data = res.data.data
:show-overflow-tooltip="true" salaryTable.total = res.data.total
/> }).catch(() => {
salaryTable.loading = false
<el-table-column })
prop="salary_month" }
:label="t('salaryMonth')" loadSalaryList()
min-width="120"
:show-overflow-tooltip="true" const editSalaryDialog: Record<string, any> | null = ref(null)
/>
/**
<el-table-column * 添加工资
prop="department_id" */
:label="t('departmentId')" const addEvent = () => {
min-width="120" editSalaryDialog.value.setFormData()
:show-overflow-tooltip="true" editSalaryDialog.value.showDialog = true
/> }
<el-table-column /**
prop="process_id" * 编辑工资
:label="t('processId')" * @param data
min-width="120" */
:show-overflow-tooltip="true" const editEvent = (data: any) => {
/> editSalaryDialog.value.setFormData(data)
editSalaryDialog.value.showDialog = true
<el-table-column }
:label="t('operation')"
fixed="right" /**
min-width="120" * 删除工资
> */
<template #default="{ row }"> const deleteEvent = (id: number) => {
<el-button type="primary" link @click="editEvent(row)">{{ ElMessageBox.confirm(t('salaryDeleteTips'), t('warning'),
t('edit') {
}}</el-button> confirmButtonText: t('confirm'),
<el-button type="primary" link @click="deleteEvent(row.id)">{{ cancelButtonText: t('cancel'),
t('delete') type: 'warning',
}}</el-button> }
</template> ).then(() => {
</el-table-column> deleteSalary(id).then(() => {
</el-table> loadSalaryList()
<div class="mt-[16px] flex justify-end"> }).catch(() => {
<el-pagination })
v-model:current-page="salaryTable.page" })
v-model:page-size="salaryTable.limit" }
layout="total, sizes, prev, pager, next, jumper"
:total="salaryTable.total"
@size-change="loadSalaryList()" const staffIdList = ref([])
@current-change="loadSalaryList" const setStaffIdList = async () => {
/> staffIdList.value = await (await getWithPersonnelList({})).data
</div> }
</div> setStaffIdList()
const departmentIdList = ref([])
<edit ref="editSalaryDialog" @complete="loadSalaryList" /> const setDepartmentIdList = async () => {
</el-card> departmentIdList.value = await (await getWithDepartmentsList({})).data
</div> }
</template> setDepartmentIdList()
<script lang="ts" setup> const resetForm = (formEl: FormInstance | undefined) => {
import { reactive, ref, watch } from 'vue' if (!formEl) return
import { t } from '@/lang' formEl.resetFields()
import { useDictionary } from '@/app/api/dict' loadSalaryList()
import { getSalaryList, deleteSalary } from '@/app/api/salary' }
import { img } from '@/utils/common' </script>
import { ElMessageBox, FormInstance } from 'element-plus'
import Edit from '@/app/views/salary/components/salary-edit.vue' <style lang="scss" scoped>
import { useRoute } from 'vue-router' /* 多行超出隐藏 */
const route = useRoute() .multi-hidden {
const pageName = route.meta.title word-break: break-all;
text-overflow: ellipsis;
let salaryTable = reactive({ overflow: hidden;
page: 1, display: -webkit-box;
limit: 10, -webkit-line-clamp: 2;
total: 0, -webkit-box-orient: vertical;
loading: true, }
data: [], </style>
searchParam: {
staff_id: '',
base_salary: '',
performance_bonus: '',
deductions: '',
other_subsidies: '',
net_salary: '',
payment_status: '',
payment_method: '',
remarks: '',
salary_month: '',
department_id: '',
process_id: '',
},
})
const searchFormRef = ref<FormInstance>()
//
const selectData = ref<any[]>([])
//
/**
* 获取工资列表
*/
const loadSalaryList = (page: number = 1) => {
salaryTable.loading = true
salaryTable.page = page
getSalaryList({
page: salaryTable.page,
limit: salaryTable.limit,
...salaryTable.searchParam,
})
.then((res) => {
salaryTable.loading = false
salaryTable.data = res.data.data
salaryTable.total = res.data.total
})
.catch(() => {
salaryTable.loading = false
})
}
loadSalaryList()
const editSalaryDialog: Record<string, any> | null = ref(null)
/**
* 添加工资
*/
const addEvent = () => {
editSalaryDialog.value.setFormData()
editSalaryDialog.value.showDialog = true
}
/**
* 编辑工资
* @param data
*/
const editEvent = (data: any) => {
editSalaryDialog.value.setFormData(data)
editSalaryDialog.value.showDialog = true
}
/**
* 删除工资
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('salaryDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteSalary(id)
.then(() => {
loadSalaryList()
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadSalaryList()
}
</script>
<style lang="scss" scoped>
/* 多行超出隐藏 */
.multi-hidden {
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>

12
niucloud/app/adminapi/controller/reimbursement/Reimbursement.php

@ -28,12 +28,8 @@ class Reimbursement extends BaseAdminController
*/ */
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["applicant_id",""],
["amount",""],
["description",""],
["receipt_url",""],
["status",""], ["status",""],
["process_id",""] ["created_at",["",""]]
]); ]);
return success((new ReimbursementService())->getPage($data)); return success((new ReimbursementService())->getPage($data));
} }
@ -58,7 +54,6 @@ class Reimbursement extends BaseAdminController
["description",""], ["description",""],
["receipt_url",""], ["receipt_url",""],
["status",""], ["status",""],
["process_id",0],
]); ]);
$this->validate($data, 'app\validate\reimbursement\Reimbursement.add'); $this->validate($data, 'app\validate\reimbursement\Reimbursement.add');
@ -78,7 +73,6 @@ class Reimbursement extends BaseAdminController
["description",""], ["description",""],
["receipt_url",""], ["receipt_url",""],
["status",""], ["status",""],
["process_id",0],
]); ]);
$this->validate($data, 'app\validate\reimbursement\Reimbursement.edit'); $this->validate($data, 'app\validate\reimbursement\Reimbursement.edit');
@ -97,4 +91,8 @@ class Reimbursement extends BaseAdminController
} }
public function getPersonnelAll(){
return success(( new ReimbursementService())->getPersonnelAll());
}
} }

32
niucloud/app/adminapi/controller/salary/Salary.php

@ -29,17 +29,9 @@ class Salary extends BaseAdminController
public function lists(){ public function lists(){
$data = $this->request->params([ $data = $this->request->params([
["staff_id",""], ["staff_id",""],
["base_salary",""],
["performance_bonus",""],
["deductions",""],
["other_subsidies",""],
["net_salary",""],
["payment_status",""],
["payment_method",""],
["remarks",""],
["salary_month",""],
["department_id",""], ["department_id",""],
["process_id",""] ["payment_status",""],
["created_at",["",""]]
]); ]);
return success((new SalaryService())->getPage($data)); return success((new SalaryService())->getPage($data));
} }
@ -60,17 +52,15 @@ class Salary extends BaseAdminController
public function add(){ public function add(){
$data = $this->request->params([ $data = $this->request->params([
["staff_id",0], ["staff_id",0],
["department_id",0],
["base_salary",0.00], ["base_salary",0.00],
["performance_bonus",0.00], ["performance_bonus",0.00],
["deductions",0.00], ["deductions",0.00],
["other_subsidies",0.00], ["other_subsidies",0.00],
["net_salary",0.00],
["payment_status",""], ["payment_status",""],
["payment_method",""], ["payment_method",""],
["remarks",""], ["remarks",""],
["salary_month","2025-05-16 17:53:32"], ["salary_month","2025-05-22 18:20:42"],
["department_id",0],
["process_id",0],
]); ]);
$this->validate($data, 'app\validate\salary\Salary.add'); $this->validate($data, 'app\validate\salary\Salary.add');
@ -86,17 +76,15 @@ class Salary extends BaseAdminController
public function edit(int $id){ public function edit(int $id){
$data = $this->request->params([ $data = $this->request->params([
["staff_id",0], ["staff_id",0],
["department_id",0],
["base_salary",0.00], ["base_salary",0.00],
["performance_bonus",0.00], ["performance_bonus",0.00],
["deductions",0.00], ["deductions",0.00],
["other_subsidies",0.00], ["other_subsidies",0.00],
["net_salary",0.00],
["payment_status",""], ["payment_status",""],
["payment_method",""], ["payment_method",""],
["remarks",""], ["remarks",""],
["salary_month","2025-05-16 17:53:32"], ["salary_month","2025-05-22 18:20:42"],
["department_id",0],
["process_id",0],
]); ]);
$this->validate($data, 'app\validate\salary\Salary.edit'); $this->validate($data, 'app\validate\salary\Salary.edit');
@ -115,4 +103,12 @@ class Salary extends BaseAdminController
} }
public function getPersonnelAll(){
return success(( new SalaryService())->getPersonnelAll());
}
public function getDepartmentsAll(){
return success(( new SalaryService())->getDepartmentsAll());
}
} }

6
niucloud/app/adminapi/route/reimbursement.php

@ -14,6 +14,10 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- reimbursement // USER_CODE_BEGIN -- reimbursement
Route::group('reimbursement', function () { Route::group('reimbursement', function () {
@ -29,6 +33,8 @@ Route::group('reimbursement', function () {
//删除报销记录 //删除报销记录
Route::delete('reimbursement/:id', 'reimbursement.Reimbursement/del'); Route::delete('reimbursement/:id', 'reimbursement.Reimbursement/del');
Route::get('personnel_all','reimbursement.Reimbursement/getPersonnelAll');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

8
niucloud/app/adminapi/route/salary.php

@ -14,6 +14,10 @@ use think\facade\Route;
use app\adminapi\middleware\AdminCheckRole; use app\adminapi\middleware\AdminCheckRole;
use app\adminapi\middleware\AdminCheckToken; use app\adminapi\middleware\AdminCheckToken;
use app\adminapi\middleware\AdminLog; use app\adminapi\middleware\AdminLog;
// USER_CODE_BEGIN -- salary // USER_CODE_BEGIN -- salary
Route::group('salary', function () { Route::group('salary', function () {
@ -29,6 +33,10 @@ Route::group('salary', function () {
//删除工资 //删除工资
Route::delete('salary/:id', 'salary.Salary/del'); Route::delete('salary/:id', 'salary.Salary/del');
Route::get('personnel_all','salary.Salary/getPersonnelAll');
Route::get('departments_all','salary.Salary/getDepartmentsAll');
})->middleware([ })->middleware([
AdminCheckToken::class, AdminCheckToken::class,
AdminCheckRole::class, AdminCheckRole::class,

80
niucloud/app/model/reimbursement/Reimbursement.php

@ -16,6 +16,8 @@ use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
use app\model\personnel\Personnel;
/** /**
* 报销记录模型 * 报销记录模型
* Class Reimbursement * Class Reimbursement
@ -42,66 +44,6 @@ class Reimbursement extends BaseModel
/**
* 搜索器:报销记录报销编号
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:报销记录申请人ID
* @param $value
* @param $data
*/
public function searchApplicantIdAttr($query, $value, $data)
{
if ($value) {
$query->where("applicant_id", $value);
}
}
/**
* 搜索器:报销记录报销金额
* @param $value
* @param $data
*/
public function searchAmountAttr($query, $value, $data)
{
if ($value) {
$query->where("amount", $value);
}
}
/**
* 搜索器:报销记录报销描述
* @param $value
* @param $data
*/
public function searchDescriptionAttr($query, $value, $data)
{
if ($value) {
$query->where("description", $value);
}
}
/**
* 搜索器:报销记录发票或收据URL
* @param $value
* @param $data
*/
public function searchReceiptUrlAttr($query, $value, $data)
{
if ($value) {
$query->where("receipt_url", $value);
}
}
/** /**
* 搜索器:报销记录状态 * 搜索器:报销记录状态
* @param $value * @param $value
@ -115,14 +57,20 @@ class Reimbursement extends BaseModel
} }
/** /**
* 搜索器:报销记录关联的审批流程ID * 搜索器:报销记录创建时间
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchProcessIdAttr($query, $value, $data) public function searchCreatedAtAttr($query, $value, $data)
{ {
if ($value) { $start = empty($value[0]) ? 0 : strtotime($value[0]);
$query->where("process_id", $value); $end = empty($value[1]) ? 0 : strtotime($value[1]);
if ($start > 0 && $end > 0) {
$query->where([["created_at", "between", [$start, $end]]]);
} else if ($start > 0 && $end == 0) {
$query->where([["created_at", ">=", $start]]);
} else if ($start == 0 && $end > 0) {
$query->where([["created_at", "<=", $end]]);
} }
} }
@ -131,4 +79,8 @@ class Reimbursement extends BaseModel
public function personnel(){
return $this->hasOne(Personnel::class, 'id', 'applicant_id')->joinType('left')->withField('name,id')->bind(['applicant_id_name'=>'name']);
}
} }

140
niucloud/app/model/salary/Salary.php

@ -16,6 +16,10 @@ use think\model\concern\SoftDelete;
use think\model\relation\HasMany; use think\model\relation\HasMany;
use think\model\relation\HasOne; use think\model\relation\HasOne;
use app\model\personnel\Personnel;
use app\model\departments\Departments;
/** /**
* 工资模型 * 工资模型
* Class Salary * Class Salary
@ -43,19 +47,7 @@ class Salary extends BaseModel
/** /**
* 搜索器:工资工资编号 * 搜索器:工资员工
* @param $value
* @param $data
*/
public function searchIdAttr($query, $value, $data)
{
if ($value) {
$query->where("id", $value);
}
}
/**
* 搜索器:工资员工ID
* @param $value * @param $value
* @param $data * @param $data
*/ */
@ -67,62 +59,14 @@ class Salary extends BaseModel
} }
/** /**
* 搜索器:工资底薪 * 搜索器:工资部门
* @param $value
* @param $data
*/
public function searchBaseSalaryAttr($query, $value, $data)
{
if ($value) {
$query->where("base_salary", $value);
}
}
/**
* 搜索器:工资绩效
* @param $value
* @param $data
*/
public function searchPerformanceBonusAttr($query, $value, $data)
{
if ($value) {
$query->where("performance_bonus", $value);
}
}
/**
* 搜索器:工资扣款
* @param $value
* @param $data
*/
public function searchDeductionsAttr($query, $value, $data)
{
if ($value) {
$query->where("deductions", $value);
}
}
/**
* 搜索器:工资其他补贴
* @param $value
* @param $data
*/
public function searchOtherSubsidiesAttr($query, $value, $data)
{
if ($value) {
$query->where("other_subsidies", $value);
}
}
/**
* 搜索器:工资实发工资
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchNetSalaryAttr($query, $value, $data) public function searchDepartmentIdAttr($query, $value, $data)
{ {
if ($value) { if ($value) {
$query->where("net_salary", $value); $query->where("department_id", $value);
} }
} }
@ -139,68 +83,34 @@ class Salary extends BaseModel
} }
/** /**
* 搜索器:工资发放方式 * 搜索器:工资创建时间
* @param $value * @param $value
* @param $data * @param $data
*/ */
public function searchPaymentMethodAttr($query, $value, $data) public function searchCreatedAtAttr($query, $value, $data)
{ {
if ($value) { $start = empty($value[0]) ? 0 : strtotime($value[0]);
$query->where("payment_method", $value); $end = empty($value[1]) ? 0 : strtotime($value[1]);
if ($start > 0 && $end > 0) {
$query->where([["created_at", "between", [$start, $end]]]);
} else if ($start > 0 && $end == 0) {
$query->where([["created_at", ">=", $start]]);
} else if ($start == 0 && $end > 0) {
$query->where([["created_at", "<=", $end]]);
} }
} }
/**
* 搜索器:工资备注
* @param $value
* @param $data
*/
public function searchRemarksAttr($query, $value, $data)
{
if ($value) {
$query->where("remarks", $value);
}
}
/**
* 搜索器:工资工资月份
* @param $value
* @param $data
*/
public function searchSalaryMonthAttr($query, $value, $data)
{
if ($value) {
$query->where("salary_month", $value);
}
}
/**
* 搜索器:工资部门ID
* @param $value
* @param $data
*/
public function searchDepartmentIdAttr($query, $value, $data)
{
if ($value) {
$query->where("department_id", $value);
}
}
/** public function personnel(){
* 搜索器:工资关联的审批流程ID return $this->hasOne(Personnel::class, 'id', 'staff_id')->joinType('left')->withField('name,id')->bind(['staff_id_name'=>'name']);
* @param $value
* @param $data
*/
public function searchProcessIdAttr($query, $value, $data)
{
if ($value) {
$query->where("process_id", $value);
}
} }
public function departments(){
return $this->hasOne(Departments::class, 'id', 'department_id')->joinType('left')->withField('department_name,id')->bind(['department_id_name'=>'department_name']);
}
} }

10
niucloud/app/service/admin/reimbursement/ReimbursementService.php

@ -12,6 +12,7 @@
namespace app\service\admin\reimbursement; namespace app\service\admin\reimbursement;
use app\model\reimbursement\Reimbursement; use app\model\reimbursement\Reimbursement;
use app\model\personnel\Personnel;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -39,7 +40,7 @@ class ReimbursementService extends BaseAdminService
$field = 'id,applicant_id,amount,description,receipt_url,status,process_id,created_at,updated_at'; $field = 'id,applicant_id,amount,description,receipt_url,status,process_id,created_at,updated_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","applicant_id","amount","description","receipt_url","status","process_id"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["status","created_at"], $where)->with(['personnel'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -53,7 +54,7 @@ class ReimbursementService extends BaseAdminService
{ {
$field = 'id,applicant_id,amount,description,receipt_url,status,process_id,created_at,updated_at'; $field = 'id,applicant_id,amount,description,receipt_url,status,process_id,created_at,updated_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['personnel'])->findOrEmpty()->toArray();
return $info; return $info;
} }
@ -95,5 +96,10 @@ class ReimbursementService extends BaseAdminService
} }
public function getPersonnelAll(){
$personnelModel = new Personnel();
return $personnelModel->select()->toArray();
}
} }

20
niucloud/app/service/admin/salary/SalaryService.php

@ -12,6 +12,8 @@
namespace app\service\admin\salary; namespace app\service\admin\salary;
use app\model\salary\Salary; use app\model\salary\Salary;
use app\model\personnel\Personnel;
use app\model\departments\Departments;
use core\base\BaseAdminService; use core\base\BaseAdminService;
@ -36,10 +38,10 @@ class SalaryService extends BaseAdminService
*/ */
public function getPage(array $where = []) public function getPage(array $where = [])
{ {
$field = 'id,staff_id,base_salary,performance_bonus,deductions,other_subsidies,net_salary,payment_status,payment_method,remarks,salary_month,department_id,process_id,created_at,updated_at'; $field = 'id,staff_id,department_id,base_salary,performance_bonus,deductions,other_subsidies,net_salary,payment_status,payment_method,remarks,salary_month,process_id,created_at,updated_at';
$order = 'id desc'; $order = 'id desc';
$search_model = $this->model->withSearch(["id","staff_id","base_salary","performance_bonus","deductions","other_subsidies","net_salary","payment_status","payment_method","remarks","salary_month","department_id","process_id"], $where)->field($field)->order($order); $search_model = $this->model->withSearch(["staff_id","department_id","payment_status","created_at"], $where)->with(['personnel','departments'])->field($field)->order($order);
$list = $this->pageQuery($search_model); $list = $this->pageQuery($search_model);
return $list; return $list;
} }
@ -51,9 +53,9 @@ class SalaryService extends BaseAdminService
*/ */
public function getInfo(int $id) public function getInfo(int $id)
{ {
$field = 'id,staff_id,base_salary,performance_bonus,deductions,other_subsidies,net_salary,payment_status,payment_method,remarks,salary_month,department_id,process_id,created_at,updated_at'; $field = 'id,staff_id,department_id,base_salary,performance_bonus,deductions,other_subsidies,net_salary,payment_status,payment_method,remarks,salary_month,process_id,created_at,updated_at';
$info = $this->model->field($field)->where([['id', "=", $id]])->findOrEmpty()->toArray(); $info = $this->model->field($field)->where([['id', "=", $id]])->with(['personnel','departments'])->findOrEmpty()->toArray();
return $info; return $info;
} }
@ -95,5 +97,15 @@ class SalaryService extends BaseAdminService
} }
public function getPersonnelAll(){
$personnelModel = new Personnel();
return $personnelModel->select()->toArray();
}
public function getDepartmentsAll(){
$departmentsModel = new Departments();
return $departmentsModel->select()->toArray();
}
} }

6
niucloud/app/validate/reimbursement/Reimbursement.php

@ -24,7 +24,6 @@ class Reimbursement extends BaseValidate
'amount' => 'require', 'amount' => 'require',
'description' => 'require', 'description' => 'require',
'status' => 'require', 'status' => 'require',
'process_id' => 'require',
]; ];
protected $message = [ protected $message = [
@ -32,12 +31,11 @@ class Reimbursement extends BaseValidate
'amount.require' => ['common_validate.require', ['amount']], 'amount.require' => ['common_validate.require', ['amount']],
'description.require' => ['common_validate.require', ['description']], 'description.require' => ['common_validate.require', ['description']],
'status.require' => ['common_validate.require', ['status']], 'status.require' => ['common_validate.require', ['status']],
'process_id.require' => ['common_validate.require', ['process_id']],
]; ];
protected $scene = [ protected $scene = [
"add" => ['applicant_id', 'amount', 'description', 'receipt_url', 'status', 'process_id'], "add" => ['applicant_id', 'amount', 'description', 'receipt_url', 'status'],
"edit" => ['applicant_id', 'amount', 'description', 'receipt_url', 'status', 'process_id'] "edit" => ['applicant_id', 'amount', 'description', 'receipt_url', 'status']
]; ];
} }

10
niucloud/app/validate/salary/Salary.php

@ -21,25 +21,31 @@ class Salary extends BaseValidate
protected $rule = [ protected $rule = [
'staff_id' => 'require', 'staff_id' => 'require',
'department_id' => 'require',
'base_salary' => 'require', 'base_salary' => 'require',
'performance_bonus' => 'require', 'performance_bonus' => 'require',
'deductions' => 'require', 'deductions' => 'require',
'other_subsidies' => 'require',
'payment_status' => 'require', 'payment_status' => 'require',
'payment_method' => 'require',
'salary_month' => 'require', 'salary_month' => 'require',
]; ];
protected $message = [ protected $message = [
'staff_id.require' => ['common_validate.require', ['staff_id']], 'staff_id.require' => ['common_validate.require', ['staff_id']],
'department_id.require' => ['common_validate.require', ['department_id']],
'base_salary.require' => ['common_validate.require', ['base_salary']], 'base_salary.require' => ['common_validate.require', ['base_salary']],
'performance_bonus.require' => ['common_validate.require', ['performance_bonus']], 'performance_bonus.require' => ['common_validate.require', ['performance_bonus']],
'deductions.require' => ['common_validate.require', ['deductions']], 'deductions.require' => ['common_validate.require', ['deductions']],
'other_subsidies.require' => ['common_validate.require', ['other_subsidies']],
'payment_status.require' => ['common_validate.require', ['payment_status']], 'payment_status.require' => ['common_validate.require', ['payment_status']],
'payment_method.require' => ['common_validate.require', ['payment_method']],
'salary_month.require' => ['common_validate.require', ['salary_month']], 'salary_month.require' => ['common_validate.require', ['salary_month']],
]; ];
protected $scene = [ protected $scene = [
"add" => ['staff_id', 'base_salary', 'performance_bonus', 'deductions', 'other_subsidies', 'net_salary', 'payment_status', 'payment_method', 'remarks', 'salary_month', 'department_id', 'process_id'], "add" => ['staff_id', 'department_id', 'base_salary', 'performance_bonus', 'deductions', 'other_subsidies', 'payment_status', 'payment_method', 'remarks', 'salary_month'],
"edit" => ['staff_id', 'base_salary', 'performance_bonus', 'deductions', 'other_subsidies', 'net_salary', 'payment_status', 'payment_method', 'remarks', 'salary_month', 'department_id', 'process_id'] "edit" => ['staff_id', 'department_id', 'base_salary', 'performance_bonus', 'deductions', 'other_subsidies', 'payment_status', 'payment_method', 'remarks', 'salary_month']
]; ];
} }

Loading…
Cancel
Save