银川
1.员工管理页面开发、联调
2.人事审批页面开发联调
3.考勤管理页面开发联调
4.薪酬管理页面开发联调
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmAttendanceExceptionApi { |
| | | /** èå¤å¼å¸¸ç³è¯· */ |
| | | export interface AttendanceException { |
| | | id?: number; |
| | | userId?: number; |
| | | userName?: string; |
| | | deptName?: string; |
| | | date?: string; |
| | | exceptionType?: number; |
| | | exceptionTypeName?: string; |
| | | reason?: string; |
| | | fileUrl?: string; |
| | | status?: number; |
| | | processInstanceId?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢èå¤å¼å¸¸ç³è¯·å页å表 */ |
| | | export function getAttendanceExceptionPage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmAttendanceExceptionApi.AttendanceException>>( |
| | | '/hrm/attendance/exception/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢èå¤å¼å¸¸ç³è¯·è¯¦æ
*/ |
| | | export function getAttendanceException(id: number) { |
| | | return requestClient.get<HrmAttendanceExceptionApi.AttendanceException>( |
| | | `/hrm/attendance/exception/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢èå¤å¼å¸¸ç³è¯· */ |
| | | export function createAttendanceException( |
| | | data: HrmAttendanceExceptionApi.AttendanceException, |
| | | ) { |
| | | return requestClient.post('/hrm/attendance/exception/create', data); |
| | | } |
| | | |
| | | /** æäº¤èå¤å¼å¸¸ç³è¯· */ |
| | | export function submitAttendanceException(id: number) { |
| | | return requestClient.post(`/hrm/attendance/exception/submit?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmAttendanceRecordApi { |
| | | /** èå¤è®°å½ */ |
| | | export interface AttendanceRecord { |
| | | id?: number; |
| | | userId?: number; |
| | | userName?: string; |
| | | deptId?: number; |
| | | deptName?: string; |
| | | date?: string; |
| | | clockInTime?: string; |
| | | clockOutTime?: string; |
| | | clockInType?: number; |
| | | clockInTypeName?: string; |
| | | clockOutType?: number; |
| | | clockOutTypeName?: string; |
| | | workHours?: number; |
| | | overtimeHours?: number; |
| | | location?: string; |
| | | dataSource?: number; |
| | | dataSourceName?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** èå¤ç»è®¡ */ |
| | | export interface AttendanceStatistics { |
| | | userId?: number; |
| | | userName?: string; |
| | | deptName?: string; |
| | | totalDays?: number; |
| | | normalDays?: number; |
| | | lateDays?: number; |
| | | earlyDays?: number; |
| | | absentDays?: number; |
| | | overtimeHours?: number; |
| | | totalWorkHours?: number; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢èå¤è®°å½å页å表 */ |
| | | export function getAttendanceRecordPage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmAttendanceRecordApi.AttendanceRecord>>( |
| | | '/hrm/attendance/record/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢èå¤ç»è®¡ï¼ææï¼ */ |
| | | export function getAttendanceStatistics(params: { |
| | | userId?: number; |
| | | deptId?: number; |
| | | month: string; |
| | | }) { |
| | | return requestClient.get<HrmAttendanceRecordApi.AttendanceStatistics[]>( |
| | | '/hrm/attendance/record/statistics', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** 导å
¥èå¤è®°å½ */ |
| | | export function importAttendanceRecord(file: File, updateSupport: boolean) { |
| | | return requestClient.upload('/hrm/attendance/record/import', { |
| | | file, |
| | | updateSupport, |
| | | }); |
| | | } |
| | | |
| | | /** ä¸è½½èå¤å¯¼å
¥æ¨¡æ¿ */ |
| | | export function importAttendanceTemplate() { |
| | | return requestClient.download('/hrm/attendance/record/get-import-template'); |
| | | } |
| | | |
| | | /** 导åºèå¤è®°å½ */ |
| | | export function exportAttendanceRecord(params: any) { |
| | | return requestClient.download('/hrm/attendance/record/export-excel', { params }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { HrmEmployeeApi } from '../index'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | /** æ¥è¯¢æè²ç»åå表 */ |
| | | export function getEducationListByEmployeeId(employeeId: number) { |
| | | return requestClient.get<HrmEmployeeApi.Education[]>( |
| | | `/hrm/employee/education/list?employeeId=${employeeId}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢æè²ç»å */ |
| | | export function createEducation(data: HrmEmployeeApi.Education) { |
| | | return requestClient.post('/hrm/employee/education/create', data); |
| | | } |
| | | |
| | | /** æ´æ°æè²ç»å */ |
| | | export function updateEducation(data: HrmEmployeeApi.Education) { |
| | | return requestClient.put('/hrm/employee/education/update', data); |
| | | } |
| | | |
| | | /** å 餿è²ç»å */ |
| | | export function deleteEducation(id: number) { |
| | | return requestClient.delete(`/hrm/employee/education/delete?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { HrmEmployeeApi } from '../index'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | /** æ¥è¯¢ç´§æ¥è系人å表 */ |
| | | export function getEmergencyContactListByEmployeeId(employeeId: number) { |
| | | return requestClient.get<HrmEmployeeApi.EmergencyContact[]>( |
| | | `/hrm/employee/emergency-contact/list?employeeId=${employeeId}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢ç´§æ¥è系人 */ |
| | | export function createEmergencyContact(data: HrmEmployeeApi.EmergencyContact) { |
| | | return requestClient.post('/hrm/employee/emergency-contact/create', data); |
| | | } |
| | | |
| | | /** æ´æ°ç´§æ¥è系人 */ |
| | | export function updateEmergencyContact(data: HrmEmployeeApi.EmergencyContact) { |
| | | return requestClient.put('/hrm/employee/emergency-contact/update', data); |
| | | } |
| | | |
| | | /** å é¤ç´§æ¥è系人 */ |
| | | export function deleteEmergencyContact(id: number) { |
| | | return requestClient.delete(`/hrm/employee/emergency-contact/delete?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmEmployeeApi { |
| | | /** åå·¥ä¿¡æ¯ */ |
| | | export interface Employee { |
| | | id?: number; |
| | | userId?: number; |
| | | deptId?: number; |
| | | deptName?: string; |
| | | postId?: number; |
| | | postName?: string; |
| | | employeeNo?: string; |
| | | name?: string; |
| | | gender?: number; |
| | | birthday?: string; |
| | | phone?: string; |
| | | email?: string; |
| | | idCard?: string; |
| | | hireDate?: string; |
| | | regularDate?: string; |
| | | leaveDate?: string; |
| | | employeeStatus?: number; |
| | | annualLeaveBalance?: number; |
| | | sickLeaveBalance?: number; |
| | | baseSalary?: number; |
| | | roleIds?: number[]; |
| | | roleNames?: string[]; |
| | | bankName?: string; |
| | | bankAccount?: string; |
| | | address?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | /** æè²ç»åå表 */ |
| | | educations?: Education[]; |
| | | /** å·¥ä½ç»åå表 */ |
| | | workHistories?: WorkHistory[]; |
| | | /** ç´§æ¥è系人å表 */ |
| | | emergencyContacts?: EmergencyContact[]; |
| | | } |
| | | |
| | | /** æè²ç»å */ |
| | | export interface Education { |
| | | id?: number; |
| | | employeeId?: number; |
| | | schoolName?: string; |
| | | major?: string; |
| | | degree?: number; |
| | | startDate?: string; |
| | | endDate?: string; |
| | | certificateNo?: string; |
| | | } |
| | | |
| | | /** å·¥ä½ç»å */ |
| | | export interface WorkHistory { |
| | | id?: number; |
| | | employeeId?: number; |
| | | companyName?: string; |
| | | position?: string; |
| | | startDate?: string; |
| | | endDate?: string; |
| | | workContent?: string; |
| | | leaveReason?: string; |
| | | } |
| | | |
| | | /** ç´§æ¥è系人 */ |
| | | export interface EmergencyContact { |
| | | id?: number; |
| | | employeeId?: number; |
| | | contactName?: string; |
| | | relationship?: string; |
| | | phone?: string; |
| | | address?: string; |
| | | } |
| | | |
| | | /** å工精ç®ä¿¡æ¯ */ |
| | | export interface EmployeeSimple { |
| | | id: number; |
| | | employeeNo: string; |
| | | name: string; |
| | | deptName?: string; |
| | | postName?: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢åå·¥å页å表 */ |
| | | export function getEmployeePage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmEmployeeApi.Employee>>( |
| | | '/hrm/employee/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢å工详æ
*/ |
| | | export function getEmployee(id: number) { |
| | | return requestClient.get<HrmEmployeeApi.Employee>( |
| | | `/hrm/employee/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢å工精ç®å表 */ |
| | | export function getEmployeeSimpleList() { |
| | | return requestClient.get<HrmEmployeeApi.EmployeeSimple[]>( |
| | | '/hrm/employee/simple-list', |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢åå·¥ */ |
| | | export function createEmployee(data: HrmEmployeeApi.Employee) { |
| | | return requestClient.post('/hrm/employee/create', data); |
| | | } |
| | | |
| | | /** æ´æ°åå·¥ */ |
| | | export function updateEmployee(data: HrmEmployeeApi.Employee) { |
| | | return requestClient.put('/hrm/employee/update', data); |
| | | } |
| | | |
| | | /** å é¤åå·¥ */ |
| | | export function deleteEmployee(id: number) { |
| | | return requestClient.delete(`/hrm/employee/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºåå·¥ */ |
| | | export function exportEmployee(params: any) { |
| | | return requestClient.download('/hrm/employee/export-excel', { params }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { HrmEmployeeApi } from '../index'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | /** æ¥è¯¢å·¥ä½ç»åå表 */ |
| | | export function getWorkHistoryListByEmployeeId(employeeId: number) { |
| | | return requestClient.get<HrmEmployeeApi.WorkHistory[]>( |
| | | `/hrm/employee/work-history/list?employeeId=${employeeId}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢å·¥ä½ç»å */ |
| | | export function createWorkHistory(data: HrmEmployeeApi.WorkHistory) { |
| | | return requestClient.post('/hrm/employee/work-history/create', data); |
| | | } |
| | | |
| | | /** æ´æ°å·¥ä½ç»å */ |
| | | export function updateWorkHistory(data: HrmEmployeeApi.WorkHistory) { |
| | | return requestClient.put('/hrm/employee/work-history/update', data); |
| | | } |
| | | |
| | | /** å é¤å·¥ä½ç»å */ |
| | | export function deleteWorkHistory(id: number) { |
| | | return requestClient.delete(`/hrm/employee/work-history/delete?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmLeaveApi { |
| | | /** 请åç³è¯· */ |
| | | export interface Leave { |
| | | id?: number; |
| | | employeeId?: number; |
| | | employeeName?: string; |
| | | deptName?: string; |
| | | leaveType?: number; |
| | | startTime?: string; |
| | | endTime?: string; |
| | | duration?: number; |
| | | reason?: string; |
| | | fileUrl?: string; |
| | | status?: number; |
| | | processInstanceId?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢è¯·åç³è¯·å页å表 */ |
| | | export function getLeavePage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmLeaveApi.Leave>>( |
| | | '/hrm/leave/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢è¯·åç³è¯·è¯¦æ
*/ |
| | | export function getLeave(id: number) { |
| | | return requestClient.get<HrmLeaveApi.Leave>(`/hrm/leave/get?id=${id}`); |
| | | } |
| | | |
| | | /** æ°å¢è¯·åç³è¯·ï¼èç¨¿ï¼ */ |
| | | export function createLeave(data: HrmLeaveApi.Leave) { |
| | | return requestClient.post('/hrm/leave/create', data); |
| | | } |
| | | |
| | | /** æ´æ°è¯·åç³è¯· */ |
| | | export function updateLeave(data: HrmLeaveApi.Leave) { |
| | | return requestClient.put('/hrm/leave/update', data); |
| | | } |
| | | |
| | | /** å é¤è¯·åç³è¯· */ |
| | | export function deleteLeave(id: number) { |
| | | return requestClient.delete(`/hrm/leave/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æäº¤è¯·åç³è¯·ï¼å起审æ¹ï¼ */ |
| | | export function submitLeave(id: number) { |
| | | return requestClient.post(`/hrm/leave/submit?id=${id}`); |
| | | } |
| | | |
| | | /** æ¤é请åç³è¯· */ |
| | | export function cancelLeave(id: number) { |
| | | return requestClient.post(`/hrm/leave/cancel?id=${id}`); |
| | | } |
| | | |
| | | /** è·åå¯ç¨æµç¨å®ä¹å表 */ |
| | | export function getLeaveApproveProcessDefinitionList() { |
| | | return requestClient.get<any[]>('/hrm/leave/approve-process-definition-list'); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmResignationApi { |
| | | /** 离èç³è¯· */ |
| | | export interface Resignation { |
| | | id?: number; |
| | | employeeId?: number; |
| | | employeeName?: string; |
| | | deptName?: string; |
| | | postName?: string; |
| | | resignationType?: number; |
| | | expectedDate?: string; |
| | | reason?: string; |
| | | handoverUserId?: number; |
| | | handoverUserName?: string; |
| | | handoverStatus?: number; |
| | | fileUrl?: string; |
| | | status?: number; |
| | | processInstanceId?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢ç¦»èç³è¯·å页å表 */ |
| | | export function getResignationPage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmResignationApi.Resignation>>( |
| | | '/hrm/resignation/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢ç¦»èç³è¯·è¯¦æ
*/ |
| | | export function getResignation(id: number) { |
| | | return requestClient.get<HrmResignationApi.Resignation>( |
| | | `/hrm/resignation/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢ç¦»èç³è¯·ï¼èç¨¿ï¼ */ |
| | | export function createResignation(data: HrmResignationApi.Resignation) { |
| | | return requestClient.post('/hrm/resignation/create', data); |
| | | } |
| | | |
| | | /** æ´æ°ç¦»èç³è¯· */ |
| | | export function updateResignation(data: HrmResignationApi.Resignation) { |
| | | return requestClient.put('/hrm/resignation/update', data); |
| | | } |
| | | |
| | | /** å é¤ç¦»èç³è¯· */ |
| | | export function deleteResignation(id: number) { |
| | | return requestClient.delete(`/hrm/resignation/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æäº¤ç¦»èç³è¯·ï¼å起审æ¹ï¼ */ |
| | | export function submitResignation(id: number) { |
| | | return requestClient.post(`/hrm/resignation/submit?id=${id}`); |
| | | } |
| | | |
| | | /** æ¤é离èç³è¯· */ |
| | | export function cancelResignation(id: number) { |
| | | return requestClient.post(`/hrm/resignation/cancel?id=${id}`); |
| | | } |
| | | |
| | | /** æ´æ°äº¤æ¥ç¶æ */ |
| | | export function updateHandoverStatus(id: number, handoverStatus: number) { |
| | | return requestClient.put('/hrm/resignation/handover', { id, handoverStatus }); |
| | | } |
| | | |
| | | /** è·åå¯ç¨æµç¨å®ä¹å表 */ |
| | | export function getResignationApproveProcessDefinitionList() { |
| | | return requestClient.get<any[]>('/hrm/resignation/approve-process-definition-list'); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmSalaryCalculationApi { |
| | | /** èªé
¬æ ¸ç® */ |
| | | export interface SalaryCalculation { |
| | | id?: number; |
| | | no?: string; |
| | | period?: string; |
| | | userId?: number; |
| | | userName?: string; |
| | | deptId?: number; |
| | | deptName?: string; |
| | | baseSalary?: number; |
| | | performanceSalary?: number; |
| | | overtimePay?: number; |
| | | mealAllowance?: number; |
| | | transportAllowance?: number; |
| | | otherAllowance?: number; |
| | | socialSecurityDeduction?: number; |
| | | housingFundDeduction?: number; |
| | | taxDeduction?: number; |
| | | otherDeduction?: number; |
| | | actualSalary?: number; |
| | | workDays?: number; |
| | | overtimeHours?: number; |
| | | status?: number; |
| | | statusName?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** èªé
¬æ ¸ç®æ¥è¯¢åæ° */ |
| | | export interface SalaryCalculationPageParam extends PageParam { |
| | | period?: string; |
| | | userId?: number; |
| | | deptId?: number; |
| | | status?: number; |
| | | } |
| | | |
| | | /** èªé
¬æ ¸ç®é¢è§åæ° */ |
| | | export interface SalaryPreviewParam { |
| | | period: string; |
| | | deptId?: number; |
| | | } |
| | | |
| | | /** èªé
¬æ ¸ç®ä¿ååæ° */ |
| | | export interface SalarySaveParam { |
| | | period: string; |
| | | list: SalaryCalculation[]; |
| | | } |
| | | |
| | | /** èªé
¬æ ¸ç®æ§è¡åæ° */ |
| | | export interface SalaryCalculateParam { |
| | | period: string; |
| | | deptId?: number; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢èªé
¬æ ¸ç®å页å表 */ |
| | | export function getSalaryCalculationPage( |
| | | params: HrmSalaryCalculationApi.SalaryCalculationPageParam, |
| | | ) { |
| | | return requestClient.get<PageResult<HrmSalaryCalculationApi.SalaryCalculation>>( |
| | | '/hrm/salary/calculation/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢èªé
¬æ ¸ç®è¯¦æ
*/ |
| | | export function getSalaryCalculation(id: number) { |
| | | return requestClient.get<HrmSalaryCalculationApi.SalaryCalculation>( |
| | | `/hrm/salary/calculation/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ§è¡èªé
¬æ ¸ç®ï¼èªå¨è®¡ç®ï¼ */ |
| | | export function calculateSalary(params: HrmSalaryCalculationApi.SalaryCalculateParam) { |
| | | return requestClient.post('/hrm/salary/calculation/calculate', null, { params }); |
| | | } |
| | | |
| | | /** é¢è§èªé
¬æ ¸ç®æ°æ®ï¼æ ¹æ®å¨æåé¨é¨è·ååå·¥å·¥èµæç»ï¼ */ |
| | | export function previewSalaryCalculation(params: HrmSalaryCalculationApi.SalaryPreviewParam) { |
| | | return requestClient.get<HrmSalaryCalculationApi.SalaryCalculation[]>( |
| | | '/hrm/salary/calculation/preview', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¹éä¿åèªé
¬æ ¸ç®è®°å½ */ |
| | | export function saveSalaryCalculation(data: HrmSalaryCalculationApi.SalarySaveParam) { |
| | | return requestClient.post('/hrm/salary/calculation/save', data); |
| | | } |
| | | |
| | | /** æ´æ°èªé
¬æ ¸ç®è®°å½ */ |
| | | export function updateSalaryCalculation(data: HrmSalaryCalculationApi.SalaryCalculation) { |
| | | return requestClient.put('/hrm/salary/calculation/update', data); |
| | | } |
| | | |
| | | /** 确认èªé
¬æ ¸ç® */ |
| | | export function confirmSalary(ids: number[]) { |
| | | return requestClient.post('/hrm/salary/calculation/confirm', { ids }); |
| | | } |
| | | |
| | | /** 导åºèªé
¬æ ¸ç®è¡¨ */ |
| | | export function exportSalaryCalculation(params: any) { |
| | | return requestClient.download('/hrm/salary/calculation/export-excel', { params }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmEmployeeSocialSecurityApi { |
| | | /** å工社ä¿å
¬ç§¯éæ¡£æ¡ */ |
| | | export interface EmployeeSocialSecurity { |
| | | id?: number; |
| | | userId?: number; |
| | | userName?: string; |
| | | deptName?: string; |
| | | socialSecuritySchemeId?: number; |
| | | schemeName?: string; |
| | | socialSecurityBase?: number; |
| | | housingFundBase?: number; |
| | | effectiveDate?: string; |
| | | status?: number; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** å工社ä¿å
¬ç§¯éæ¡£æ¡æ¥è¯¢åæ° */ |
| | | export interface EmployeeSocialSecurityPageParam extends PageParam { |
| | | userName?: string; |
| | | deptId?: number; |
| | | } |
| | | } |
| | | |
| | | /** è·åå工社ä¿å
¬ç§¯éæ¡£æ¡å页å表 */ |
| | | export function getEmployeeSocialSecurityPage( |
| | | params: HrmEmployeeSocialSecurityApi.EmployeeSocialSecurityPageParam, |
| | | ) { |
| | | return requestClient.get<PageResult<HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity>>( |
| | | '/hrm/salary/employee-social-security/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** è·åå工社ä¿å
¬ç§¯éæ¡£æ¡è¯¦æ
*/ |
| | | export function getEmployeeSocialSecurity(id: number) { |
| | | return requestClient.get<HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity>( |
| | | `/hrm/salary/employee-social-security/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åå工社ä¿å
¬ç§¯éæ¡£æ¡åå²å表 */ |
| | | export function getEmployeeSocialSecurityListByUser(userId: number) { |
| | | return requestClient.get<HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity[]>( |
| | | `/hrm/salary/employee-social-security/list-by-user?userId=${userId}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·ååå·¥ææ°çæç社ä¿å
¬ç§¯éæ¡£æ¡ */ |
| | | export function getLatestEmployeeSocialSecurity(userId: number) { |
| | | return requestClient.get<HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity>( |
| | | `/hrm/salary/employee-social-security/get-latest?userId=${userId}`, |
| | | ); |
| | | } |
| | | |
| | | /** å建å工社ä¿å
¬ç§¯éæ¡£æ¡ */ |
| | | export function createEmployeeSocialSecurity( |
| | | data: HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity, |
| | | ) { |
| | | return requestClient.post('/hrm/salary/employee-social-security/create', data); |
| | | } |
| | | |
| | | /** æ´æ°å工社ä¿å
¬ç§¯éæ¡£æ¡ */ |
| | | export function updateEmployeeSocialSecurity( |
| | | data: HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity, |
| | | ) { |
| | | return requestClient.put('/hrm/salary/employee-social-security/update', data); |
| | | } |
| | | |
| | | /** å é¤å工社ä¿å
¬ç§¯éæ¡£æ¡ */ |
| | | export function deleteEmployeeSocialSecurity(id: number) { |
| | | return requestClient.delete(`/hrm/salary/employee-social-security/delete?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmSalaryPaymentApi { |
| | | /** èªèµåæ¾è®°å½ */ |
| | | export interface SalaryPayment { |
| | | id?: number; |
| | | no?: string; |
| | | period?: string; |
| | | userId?: number; |
| | | userName?: string; |
| | | deptId?: number; |
| | | deptName?: string; |
| | | actualSalary?: number; |
| | | paymentMethod?: number; |
| | | paymentTime?: string; |
| | | paymentNo?: string; |
| | | status?: number; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** èªèµåæ¾æ¥è¯¢åæ° */ |
| | | export interface SalaryPaymentPageParam extends PageParam { |
| | | period?: string; |
| | | userId?: number; |
| | | deptId?: number; |
| | | status?: number; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢èªèµåæ¾å页å表 */ |
| | | export function getSalaryPaymentPage( |
| | | params: HrmSalaryPaymentApi.SalaryPaymentPageParam, |
| | | ) { |
| | | return requestClient.get<PageResult<HrmSalaryPaymentApi.SalaryPayment>>( |
| | | '/hrm/salary/payment/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢èªèµåæ¾è¯¦æ
*/ |
| | | export function getSalaryPayment(id: number) { |
| | | return requestClient.get<HrmSalaryPaymentApi.SalaryPayment>( |
| | | `/hrm/salary/payment/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ§è¡èªèµåæ¾ */ |
| | | export function paySalary(ids: number[]) { |
| | | return requestClient.post('/hrm/salary/payment/pay', { ids }); |
| | | } |
| | | |
| | | /** æ¤éèªèµåæ¾ */ |
| | | export function revokeSalaryPayment(id: number) { |
| | | return requestClient.post(`/hrm/salary/payment/revoke?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºèªèµåæ¾è¡¨ */ |
| | | export function exportSalaryPayment(params: any) { |
| | | return requestClient.download('/hrm/salary/payment/export-excel', { params }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmSocialSecuritySchemeApi { |
| | | /** 社ä¿å
¬ç§¯éæ¹æ¡ */ |
| | | export interface SocialSecurityScheme { |
| | | id?: number; |
| | | name?: string; |
| | | effectiveDate?: string; |
| | | pensionCompanyRatio?: number; |
| | | pensionPersonalRatio?: number; |
| | | medicalCompanyRatio?: number; |
| | | medicalPersonalRatio?: number; |
| | | unemploymentCompanyRatio?: number; |
| | | unemploymentPersonalRatio?: number; |
| | | workInjuryRatio?: number; |
| | | maternityRatio?: number; |
| | | housingFundCompanyRatio?: number; |
| | | housingFundPersonalRatio?: number; |
| | | status?: number; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** 社ä¿å
¬ç§¯éæ¹æ¡æ¥è¯¢åæ° */ |
| | | export interface SocialSecuritySchemeQueryParam { |
| | | name?: string; |
| | | status?: number; |
| | | } |
| | | } |
| | | |
| | | /** è·å社ä¿å
¬ç§¯éæ¹æ¡å表 */ |
| | | export function getSocialSecuritySchemeList(params?: HrmSocialSecuritySchemeApi.SocialSecuritySchemeQueryParam) { |
| | | return requestClient.get<HrmSocialSecuritySchemeApi.SocialSecurityScheme[]>( |
| | | '/hrm/salary/social-security-scheme/list', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** è·å社ä¿å
¬ç§¯éæ¹æ¡è¯¦æ
*/ |
| | | export function getSocialSecurityScheme(id: number) { |
| | | return requestClient.get<HrmSocialSecuritySchemeApi.SocialSecurityScheme>( |
| | | `/hrm/salary/social-security-scheme/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** å建社ä¿å
¬ç§¯éæ¹æ¡ */ |
| | | export function createSocialSecurityScheme( |
| | | data: HrmSocialSecuritySchemeApi.SocialSecurityScheme, |
| | | ) { |
| | | return requestClient.post('/hrm/salary/social-security-scheme/create', data); |
| | | } |
| | | |
| | | /** æ´æ°ç¤¾ä¿å
¬ç§¯éæ¹æ¡ */ |
| | | export function updateSocialSecurityScheme( |
| | | data: HrmSocialSecuritySchemeApi.SocialSecurityScheme, |
| | | ) { |
| | | return requestClient.put('/hrm/salary/social-security-scheme/update', data); |
| | | } |
| | | |
| | | /** å é¤ç¤¾ä¿å
¬ç§¯éæ¹æ¡ */ |
| | | export function deleteSocialSecurityScheme(id: number) { |
| | | return requestClient.delete(`/hrm/salary/social-security-scheme/delete?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmTaxRateConfigApi { |
| | | /** 个ç¨ç¨çé
ç½® */ |
| | | export interface TaxRateConfig { |
| | | id?: number; |
| | | name?: string; |
| | | effectiveDate?: string; |
| | | level?: number; |
| | | minAmount?: number; |
| | | maxAmount?: number; |
| | | taxRate?: number; |
| | | quickDeduction?: number; |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** è·å个ç¨ç¨çé
ç½®å表 */ |
| | | export function getTaxRateConfigList() { |
| | | return requestClient.get<HrmTaxRateConfigApi.TaxRateConfig[]>( |
| | | '/hrm/salary/tax-rate-config/list', |
| | | ); |
| | | } |
| | | |
| | | /** è·å个ç¨ç¨çé
置详æ
*/ |
| | | export function getTaxRateConfig(id: number) { |
| | | return requestClient.get<HrmTaxRateConfigApi.TaxRateConfig>( |
| | | `/hrm/salary/tax-rate-config/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åçæç个ç¨ç¨çé
ç½®å表 */ |
| | | export function getEffectiveTaxRateConfigList() { |
| | | return requestClient.get<HrmTaxRateConfigApi.TaxRateConfig[]>( |
| | | '/hrm/salary/tax-rate-config/get-effective', |
| | | ); |
| | | } |
| | | |
| | | /** æ¹éå建个ç¨ç¨çé
ç½® */ |
| | | export function createTaxRateConfigList( |
| | | data: HrmTaxRateConfigApi.TaxRateConfig[], |
| | | ) { |
| | | return requestClient.post('/hrm/salary/tax-rate-config/create-list', data); |
| | | } |
| | | |
| | | /** æ´æ°ä¸ªç¨ç¨çé
ç½® */ |
| | | export function updateTaxRateConfig(data: HrmTaxRateConfigApi.TaxRateConfig) { |
| | | return requestClient.put('/hrm/salary/tax-rate-config/update', data); |
| | | } |
| | | |
| | | /** å é¤ä¸ªç¨ç¨çé
ç½® */ |
| | | export function deleteTaxRateConfig(id: number) { |
| | | return requestClient.delete(`/hrm/salary/tax-rate-config/delete?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmTransferApi { |
| | | /** è°å²ç³è¯· */ |
| | | export interface Transfer { |
| | | id?: number; |
| | | employeeId?: number; |
| | | employeeName?: string; |
| | | currentDeptId?: number; |
| | | currentDeptName?: string; |
| | | currentPostId?: number; |
| | | currentPostName?: string; |
| | | targetDeptId?: number; |
| | | targetDeptName?: string; |
| | | targetPostId?: number; |
| | | targetPostName?: string; |
| | | transferType?: number; |
| | | effectiveDate?: string; |
| | | reason?: string; |
| | | fileUrl?: string; |
| | | status?: number; |
| | | processInstanceId?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢è°å²ç³è¯·å页å表 */ |
| | | export function getTransferPage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmTransferApi.Transfer>>( |
| | | '/hrm/transfer/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢è°å²ç³è¯·è¯¦æ
*/ |
| | | export function getTransfer(id: number) { |
| | | return requestClient.get<HrmTransferApi.Transfer>(`/hrm/transfer/get?id=${id}`); |
| | | } |
| | | |
| | | /** æ°å¢è°å²ç³è¯·ï¼èç¨¿ï¼ */ |
| | | export function createTransfer(data: HrmTransferApi.Transfer) { |
| | | return requestClient.post('/hrm/transfer/create', data); |
| | | } |
| | | |
| | | /** æ´æ°è°å²ç³è¯· */ |
| | | export function updateTransfer(data: HrmTransferApi.Transfer) { |
| | | return requestClient.put('/hrm/transfer/update', data); |
| | | } |
| | | |
| | | /** å é¤è°å²ç³è¯· */ |
| | | export function deleteTransfer(id: number) { |
| | | return requestClient.delete(`/hrm/transfer/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æäº¤è°å²ç³è¯·ï¼å起审æ¹ï¼ */ |
| | | export function submitTransfer(id: number) { |
| | | return requestClient.post(`/hrm/transfer/submit?id=${id}`); |
| | | } |
| | | |
| | | /** æ¤éè°å²ç³è¯· */ |
| | | export function cancelTransfer(id: number) { |
| | | return requestClient.post(`/hrm/transfer/cancel?id=${id}`); |
| | | } |
| | | |
| | | /** è·åå¯ç¨æµç¨å®ä¹å表 */ |
| | | export function getTransferApproveProcessDefinitionList() { |
| | | return requestClient.get<any[]>('/hrm/transfer/approve-process-definition-list'); |
| | | } |
| | |
| | | STOCK_BIZ_TYPE: 'stock_biz_type', // åºåä¸å¡ç±»å |
| | | } as const; |
| | | |
| | | /** ========== HRM - 人åèµæºæ¨¡å ========== */ |
| | | const HRM_DICT = { |
| | | HRM_DEGREE: 'hrm_degree', // HRM å¦å |
| | | HRM_EMPLOYEE_STATUS: 'hrm_employee_status', // HRM åå·¥ç¶æ |
| | | HRM_GENDER: 'hrm_gender', // HRM æ§å« |
| | | HRM_AUDIT_STATUS: 'hrm_audit_status', // HRM 审æ¹ç¶æ |
| | | HRM_LEAVE_TYPE: 'hrm_leave_type', // HRM 请åç±»å |
| | | HRM_TRANSFER_TYPE: 'hrm_transfer_type', // HRM è°å²ç±»å |
| | | HRM_RESIGNATION_TYPE: 'hrm_resignation_type', // HRM 离èç±»å |
| | | HRM_CLOCK_TYPE: 'hrm_clock_type', // HRM æå¡ç±»å |
| | | HRM_SALARY_STATUS: 'hrm_salary_status', // HRM èªèµç¶æ |
| | | HRM_HANDOVER_STATUS: 'hrm_handover_status', // HRM 交æ¥ç¶æ |
| | | HRM_PAYMENT_METHOD: 'hrm_payment_method', // HRM åæ¾æ¹å¼ |
| | | } as const; |
| | | |
| | | /** åå
¸ç±»åæä¸¾ - ç»ä¸å¯¼åº */ |
| | | const DICT_TYPE = { |
| | | ...AI_DICT, |
| | | ...BPM_DICT, |
| | | ...CRM_DICT, |
| | | ...ERP_DICT, |
| | | ...HRM_DICT, |
| | | ...INFRA_DICT, |
| | | ...IM_DICT, |
| | | ...IOT_DICT, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmAttendanceExceptionApi } from '#/api/hrm/attendance/exception'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail'; |
| | | |
| | | /** æ°å¢èå¤å¼å¸¸ç³è¯·ç表å */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'date', |
| | | label: 'è夿¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©è夿¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'exceptionType', |
| | | label: 'å¼å¸¸ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å¼å¸¸ç±»å', |
| | | options: [ |
| | | { label: 'æ¼æå¡', value: 1 }, |
| | | { label: 'è¿å°', value: 2 }, |
| | | { label: 'æ©é', value: 3 }, |
| | | { label: 'å
¶ä»', value: 4 }, |
| | | ], |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'reason', |
| | | label: 'å¼å¸¸åå ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¼å¸¸åå ', |
| | | rows: 3, |
| | | maxlength: 500, |
| | | showCount: true, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'date', |
| | | label: 'è夿¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | allowClear: true, |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '审æ¹ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_AUDIT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å®¡æ¹ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmAttendanceExceptionApi.AttendanceException>['columns'] { |
| | | return [ |
| | | { field: 'userName', title: 'ç³è¯·äºº', minWidth: 100 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { field: 'date', title: 'è夿¥æ', width: 110 }, |
| | | { field: 'exceptionTypeName', title: 'å¼å¸¸ç±»å', width: 100 }, |
| | | { field: 'reason', title: 'å¼å¸¸åå ', minWidth: 200 }, |
| | | { |
| | | field: 'status', |
| | | title: '审æ¹ç¶æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_AUDIT_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 150, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmAttendanceExceptionApi } from '#/api/hrm/attendance/exception'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getAttendanceExceptionPage, submitAttendanceException } from '#/api/hrm/attendance/exception'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** å建èå¤å¼å¸¸ç³è¯· */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥çèå¤å¼å¸¸ç³è¯· */ |
| | | function handleDetail(row: HrmAttendanceExceptionApi.AttendanceException) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | /** æäº¤èå¤å¼å¸¸ç³è¯· */ |
| | | async function handleSubmit(row: HrmAttendanceExceptionApi.AttendanceException) { |
| | | await submitAttendanceException(row.id!); |
| | | message.success('æäº¤æå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getAttendanceExceptionPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmAttendanceExceptionApi.AttendanceException>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="èå¤å¼å¸¸ç³è¯·å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['èå¤å¼å¸¸ç³è¯·']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:attendance-exception:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ¥ç', |
| | | type: 'link', |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤', |
| | | type: 'link', |
| | | auth: ['hrm:attendance-exception:submit'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { HrmAttendanceExceptionApi } from '#/api/hrm/attendance/exception'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createAttendanceException, getAttendanceException } from '#/api/hrm/attendance/exception'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const isDetail = computed(() => formType.value === 'detail'); |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return 'æ¥çèå¤å¼å¸¸ç³è¯·'; |
| | | } |
| | | return 'æ°å¢èå¤å¼å¸¸ç³è¯·'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as HrmAttendanceExceptionApi.AttendanceException; |
| | | try { |
| | | await createAttendanceException(data); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setState({ schema: useFormSchema(data.formType) }); |
| | | formApi.setDisabled(data.formType === 'detail'); |
| | | modalApi.setState({ showConfirmButton: data.formType !== 'detail' }); |
| | | if (!data?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await formApi.setValues(await getAttendanceException(data.id)); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[600px]"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmAttendanceRecordApi } from '#/api/hrm/attendance/record'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'userName', |
| | | label: 'åå·¥å§å', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åå·¥å§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'date', |
| | | label: 'è夿¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | allowClear: true, |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'deptId', |
| | | label: 'é¨é¨', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const data = await getDeptList(); |
| | | return handleTree(data); |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | childrenField: 'children', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©é¨é¨', |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'clockInType', |
| | | label: 'ä¸çæå¡ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_CLOCK_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©ä¸çæå¡ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'clockOutType', |
| | | label: 'ä¸çæå¡ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_CLOCK_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©ä¸çæå¡ç±»å', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmAttendanceRecordApi.AttendanceRecord>['columns'] { |
| | | return [ |
| | | { field: 'userName', title: 'åå·¥å§å', minWidth: 100 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { field: 'date', title: 'è夿¥æ', width: 110 }, |
| | | { field: 'clockInTime', title: 'ä¸çæå¡æ¶é´', width: 160 }, |
| | | { |
| | | field: 'clockInType', |
| | | title: 'ä¸çæå¡ç±»å', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_CLOCK_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'clockOutTime', title: 'ä¸çæå¡æ¶é´', width: 160 }, |
| | | { |
| | | field: 'clockOutType', |
| | | title: 'ä¸çæå¡ç±»å', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_CLOCK_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'workHours', title: '工使¶é¿(h)', width: 100 }, |
| | | { field: 'overtimeHours', title: 'å çæ¶é¿(h)', width: 100 }, |
| | | { field: 'location', title: 'æå¡å°ç¹', minWidth: 150 }, |
| | | { |
| | | field: 'dataSource', |
| | | title: 'æ°æ®æ¥æº', |
| | | width: 100, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 120, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmAttendanceRecordApi } from '#/api/hrm/attendance/record'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart } from '#/packages/utils/src'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { exportAttendanceRecord, getAttendanceRecordPage } from '#/api/hrm/attendance/record'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import ImportForm from './modules/import-form.vue'; |
| | | |
| | | const [ImportModal, importModalApi] = useVbenModal({ |
| | | connectedComponent: ImportForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** 导å
¥èå¤è®°å½ */ |
| | | function handleImport() { |
| | | importModalApi.open(); |
| | | } |
| | | |
| | | /** 导åºèå¤è®°å½ */ |
| | | async function handleExport() { |
| | | const data = await exportAttendanceRecord(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'èå¤è®°å½.xls', source: data }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getAttendanceRecordPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmAttendanceRecordApi.AttendanceRecord>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <ImportModal @success="handleRefresh" /> |
| | | <Grid table-title="èå¤è®°å½å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: '导å
¥', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['hrm:attendance-record:import'], |
| | | onClick: handleImport, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['hrm:attendance-record:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FileType } from 'ant-design-vue/es/upload/interface'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart } from '#/packages/utils/src'; |
| | | |
| | | import { Button, message, Switch, Upload } from 'ant-design-vue'; |
| | | |
| | | import { importAttendanceRecord, importAttendanceTemplate } from '#/api/hrm/attendance/record'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const fileList = ref<FileType[]>([]); |
| | | const updateSupport = ref(false); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (fileList.value.length === 0) { |
| | | message.warning('è¯·éæ©è¦å¯¼å
¥çæä»¶'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await importAttendanceRecord(fileList.value[0] as File, updateSupport.value); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('导å
¥æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | function beforeUpload(file: FileType) { |
| | | fileList.value = [file]; |
| | | return false; |
| | | } |
| | | |
| | | function handleRemove() { |
| | | fileList.value = []; |
| | | } |
| | | |
| | | async function handleDownloadTemplate() { |
| | | const data = await importAttendanceTemplate(); |
| | | downloadFileFromBlobPart({ fileName: 'èå¤å¯¼å
¥æ¨¡æ¿.xls', source: data }); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="导å
¥èå¤è®°å½" class="w-[500px]"> |
| | | <div class="mx-4"> |
| | | <Upload |
| | | :max-count="1" |
| | | accept=".xls,.xlsx" |
| | | :file-list="fileList" |
| | | :before-upload="beforeUpload" |
| | | @remove="handleRemove" |
| | | > |
| | | <Button type="primary">éæ© Excel æä»¶</Button> |
| | | </Upload> |
| | | <div class="mt-4"> |
| | | <span class="mr-2">æ¯å¦è¦çå·²åå¨çæ°æ®ï¼</span> |
| | | <Switch v-model:checked="updateSupport" /> |
| | | </div> |
| | | <div class="mt-2 text-gray-500"> |
| | | ä»
å
许导å
¥ xlsãxlsx æ ¼å¼æä»¶ |
| | | </div> |
| | | </div> |
| | | <template #prepend-footer> |
| | | <Button @click="handleDownloadTemplate">ä¸è½½å¯¼å
¥æ¨¡æ¿</Button> |
| | | </template> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | export { default as HrmEmployeeSelect } from './select.vue'; |
| | | export { default as HrmEmployeeSelectDialog } from './select-dialog.vue'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import { nextTick, ref } from 'vue'; |
| | | |
| | | import { message, Modal } from 'ant-design-vue'; |
| | | |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getEmployeePage } from '#/api/hrm/employee'; |
| | | |
| | | import { |
| | | useEmployeeSelectGridColumns, |
| | | useEmployeeSelectGridFormSchema, |
| | | } from '../data'; |
| | | |
| | | defineOptions({ name: 'HrmEmployeeSelectDialog' }); |
| | | |
| | | const emit = defineEmits<{ |
| | | selected: [rows: HrmEmployeeApi.Employee[]]; |
| | | }>(); |
| | | |
| | | const open = ref(false); |
| | | const multiple = ref(false); |
| | | const selectedRows = ref<HrmEmployeeApi.Employee[]>([]); |
| | | const preSelectedIds = ref<number[]>([]); |
| | | const filterDeptId = ref<number>(); |
| | | |
| | | /** è·åå½åè¡¨æ ¼æ°æ® */ |
| | | function getTableRows() { |
| | | return gridApi.grid.getTableData().fullData as HrmEmployeeApi.Employee[]; |
| | | } |
| | | |
| | | /** è·åå¤éè®°å½ */ |
| | | function getMultipleSelectedRows() { |
| | | const selectedMap = new Map<number, HrmEmployeeApi.Employee>(); |
| | | const records = [ |
| | | ...(gridApi.grid.getCheckboxReserveRecords?.() ?? []), |
| | | ...(gridApi.grid.getCheckboxRecords?.() ?? []), |
| | | ] as HrmEmployeeApi.Employee[]; |
| | | records.forEach((row) => { |
| | | const rowId = row.id; |
| | | if (rowId !== undefined && rowId !== null) { |
| | | selectedMap.set(rowId, row); |
| | | } |
| | | }); |
| | | return [...selectedMap.values()]; |
| | | } |
| | | |
| | | /** å¤çå¾éåå */ |
| | | function handleCheckboxSelectChange() { |
| | | selectedRows.value = getMultipleSelectedRows(); |
| | | } |
| | | |
| | | /** å¤çåéåå */ |
| | | function handleRadioChange(row: HrmEmployeeApi.Employee) { |
| | | selectedRows.value = [row]; |
| | | } |
| | | |
| | | /** å¤é模å¼ä¸åæ¢è¡å¾é */ |
| | | async function toggleMultipleRow(row: HrmEmployeeApi.Employee) { |
| | | const selected = gridApi.grid.isCheckedByCheckboxRow(row); |
| | | await gridApi.grid.setCheckboxRow(row, !selected); |
| | | selectedRows.value = getMultipleSelectedRows(); |
| | | } |
| | | |
| | | /** å¤çè¡åå» */ |
| | | async function handleCellDblclick({ row }: { row: HrmEmployeeApi.Employee }) { |
| | | if (multiple.value) { |
| | | await toggleMultipleRow(row); |
| | | return; |
| | | } |
| | | selectedRows.value = [row]; |
| | | await gridApi.grid.setRadioRow(row); |
| | | handleConfirm(); |
| | | } |
| | | |
| | | /** åæ¾é¢éåå·¥ */ |
| | | async function applyPreSelection() { |
| | | if (preSelectedIds.value.length === 0) { |
| | | return; |
| | | } |
| | | const rows = getTableRows(); |
| | | for (const row of rows) { |
| | | if (row.id === undefined || row.id === null || !preSelectedIds.value.includes(row.id)) { |
| | | continue; |
| | | } |
| | | if (multiple.value) { |
| | | await gridApi.grid.setCheckboxRow(row, true); |
| | | } else { |
| | | await gridApi.grid.setRadioRow(row); |
| | | selectedRows.value = [row]; |
| | | return; |
| | | } |
| | | } |
| | | if (multiple.value) { |
| | | selectedRows.value = getMultipleSelectedRows(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useEmployeeSelectGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useEmployeeSelectGridColumns(false), |
| | | height: 400, |
| | | keepSource: true, |
| | | checkboxConfig: { |
| | | highlight: true, |
| | | range: true, |
| | | reserve: true, |
| | | }, |
| | | radioConfig: { |
| | | highlight: true, |
| | | trigger: 'row', |
| | | }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getEmployeePage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | deptId: filterDeptId.value, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmEmployeeApi.Employee>, |
| | | gridEvents: { |
| | | cellDblclick: handleCellDblclick, |
| | | checkboxAll: handleCheckboxSelectChange, |
| | | checkboxChange: handleCheckboxSelectChange, |
| | | radioChange: ({ row }: { row: HrmEmployeeApi.Employee }) => { |
| | | handleRadioChange(row); |
| | | }, |
| | | }, |
| | | }); |
| | | |
| | | /** éç½®æ¥è¯¢åéæ©ç¶æ */ |
| | | async function resetQueryState() { |
| | | selectedRows.value = []; |
| | | filterDeptId.value = undefined; |
| | | await gridApi.grid.clearCheckboxRow(); |
| | | await gridApi.grid.clearCheckboxReserve(); |
| | | await gridApi.grid.clearRadioRow(); |
| | | await gridApi.formApi.resetForm(); |
| | | } |
| | | |
| | | /** æå¼åå·¥éæ©å¼¹çª */ |
| | | async function openModal( |
| | | selectedIds?: number[], |
| | | options?: { multiple?: boolean; deptId?: number }, |
| | | ) { |
| | | open.value = true; |
| | | multiple.value = options?.multiple ?? false; |
| | | filterDeptId.value = options?.deptId; |
| | | preSelectedIds.value = selectedIds || []; |
| | | await nextTick(); |
| | | gridApi.setGridOptions({ |
| | | columns: useEmployeeSelectGridColumns(multiple.value), |
| | | }); |
| | | await resetQueryState(); |
| | | await gridApi.query(); |
| | | await nextTick(); |
| | | await applyPreSelection(); |
| | | } |
| | | |
| | | /** å
³éåå·¥éæ©å¼¹çª */ |
| | | async function closeModal() { |
| | | open.value = false; |
| | | await resetQueryState(); |
| | | } |
| | | |
| | | /** ç¡®è®¤éæ©åå·¥ */ |
| | | function handleConfirm() { |
| | | const rows = multiple.value ? getMultipleSelectedRows() : selectedRows.value; |
| | | if (rows.length === 0) { |
| | | message.warning(multiple.value ? '请è³å°éæ©ä¸æ¡æ°æ®' : 'è¯·éæ©ä¸æ¡æ°æ®'); |
| | | return; |
| | | } |
| | | emit('selected', multiple.value ? rows : [rows[0]!]); |
| | | open.value = false; |
| | | } |
| | | |
| | | defineExpose({ open: openModal }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal |
| | | v-model:open="open" |
| | | title="å工鿩" |
| | | width="800px" |
| | | :destroy-on-close="true" |
| | | @ok="handleConfirm" |
| | | @cancel="closeModal" |
| | | > |
| | | <Grid table-title="åå·¥å表" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import { computed, ref, useAttrs, watch } from 'vue'; |
| | | |
| | | import { IconifyIcon } from '#/packages/icons/src'; |
| | | |
| | | import { Input, Tooltip } from 'ant-design-vue'; |
| | | |
| | | import { getEmployee } from '#/api/hrm/employee'; |
| | | |
| | | import HrmEmployeeSelectDialog from './select-dialog.vue'; |
| | | |
| | | defineOptions({ name: 'HrmEmployeeSelect', inheritAttrs: false }); |
| | | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | allowClear?: boolean; |
| | | disabled?: boolean; |
| | | modelValue?: number; |
| | | placeholder?: string; |
| | | deptId?: number; |
| | | }>(), |
| | | { |
| | | allowClear: true, |
| | | disabled: false, |
| | | modelValue: undefined, |
| | | placeholder: 'è¯·éæ©åå·¥', |
| | | deptId: undefined, |
| | | }, |
| | | ); |
| | | |
| | | const emit = defineEmits<{ |
| | | change: [item: HrmEmployeeApi.Employee | undefined]; |
| | | 'update:modelValue': [value: number | undefined]; |
| | | }>(); |
| | | |
| | | const attrs = useAttrs(); |
| | | const dialogRef = ref<InstanceType<typeof HrmEmployeeSelectDialog>>(); |
| | | const hovering = ref(false); |
| | | const selectedItem = ref<HrmEmployeeApi.Employee>(); |
| | | |
| | | const displayLabel = computed(() => selectedItem.value?.name ?? ''); |
| | | const showClear = computed( |
| | | () => |
| | | props.allowClear && |
| | | !props.disabled && |
| | | hovering.value && |
| | | props.modelValue !== null, |
| | | ); |
| | | |
| | | /** æ ¹æ®åå·¥IDåæ¾éæ©å¨ */ |
| | | async function resolveItemById(id: number | undefined) { |
| | | if (!id) { |
| | | selectedItem.value = undefined; |
| | | return; |
| | | } |
| | | if (selectedItem.value?.id === id) { |
| | | return; |
| | | } |
| | | selectedItem.value = await getEmployee(id); |
| | | } |
| | | |
| | | watch( |
| | | () => props.modelValue, |
| | | (value) => { |
| | | resolveItemById(value); |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | |
| | | /** æ¸
空已éåå·¥ */ |
| | | function clearSelected() { |
| | | selectedItem.value = undefined; |
| | | emit('update:modelValue', undefined); |
| | | emit('change', undefined); |
| | | } |
| | | |
| | | /** æå¼åå·¥éæ©å¼¹çª */ |
| | | function handleClick(event: MouseEvent) { |
| | | if (props.disabled) { |
| | | return; |
| | | } |
| | | const target = event.target as HTMLElement; |
| | | if (showClear.value && target.closest('.ant-input-suffix')) { |
| | | event.stopPropagation(); |
| | | clearSelected(); |
| | | return; |
| | | } |
| | | const selectedIds = (props.modelValue === null ? [] : [props.modelValue]) as number[]; |
| | | dialogRef.value?.open(selectedIds, { multiple: false, deptId: props.deptId }); |
| | | } |
| | | |
| | | /** åå¡«éä¸çåå·¥ */ |
| | | function handleSelected(rows: HrmEmployeeApi.Employee[]) { |
| | | const item = rows[0]; |
| | | if (!item) { |
| | | return; |
| | | } |
| | | selectedItem.value = item; |
| | | emit('update:modelValue', item.id); |
| | | emit('change', item); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div |
| | | v-bind="attrs" |
| | | class="w-full" |
| | | :class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'" |
| | | @click="handleClick" |
| | | @mouseenter="hovering = true" |
| | | @mouseleave="hovering = false" |
| | | > |
| | | <Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false"> |
| | | <template #title> |
| | | <div v-if="selectedItem" class="leading-6"> |
| | | <div>åå·¥ç¼å·ï¼{{ selectedItem.employeeNo || '-' }}</div> |
| | | <div>åå·¥å§åï¼{{ selectedItem.name || '-' }}</div> |
| | | <div v-if="selectedItem.deptName">é¨é¨ï¼{{ selectedItem.deptName }}</div> |
| | | <div v-if="selectedItem.postName">å²ä½ï¼{{ selectedItem.postName }}</div> |
| | | </div> |
| | | </template> |
| | | <Input |
| | | :disabled="disabled" |
| | | :placeholder="placeholder" |
| | | :value="displayLabel" |
| | | readonly |
| | | > |
| | | <template #suffix> |
| | | <IconifyIcon |
| | | class="size-4" |
| | | :icon="showClear ? 'lucide:circle-x' : 'lucide:search'" |
| | | /> |
| | | </template> |
| | | </Input> |
| | | </Tooltip> |
| | | </div> |
| | | <HrmEmployeeSelectDialog ref="dialogRef" @selected="handleSelected" /> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import { h } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { getSimplePostList } from '#/api/system/post'; |
| | | import { getSimpleRoleList } from '#/api/system/role'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹åå·¥ç表å */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'userId', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'employeeNo', |
| | | label: 'åå·¥ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åå·¥ç¼å·', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'åå·¥å§å', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åå·¥å§å', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'gender', |
| | | label: 'æ§å«', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | buttonStyle: 'solid', |
| | | optionType: 'button', |
| | | options: getDictOptions(DICT_TYPE.HRM_GENDER, 'number'), |
| | | }, |
| | | rules: z.number().default(1), |
| | | }, |
| | | { |
| | | fieldName: 'phone', |
| | | label: 'ææºå·(ç»å½è´¦å·)', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ææºå·ç ï¼å°ä½ä¸ºç³»ç»ç»å½è´¦å·', |
| | | }, |
| | | rules: 'required', |
| | | help: 'ææºå·å°ä½ä¸ºç³»ç»ç»å½è´¦å·ï¼é»è®¤å¯ç 䏿æºå·ç¸å', |
| | | }, |
| | | { |
| | | fieldName: 'deptId', |
| | | label: 'å½å±é¨é¨', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const data = await getDeptList(); |
| | | return handleTree(data); |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | childrenField: 'children', |
| | | placeholder: 'è¯·éæ©å½å±é¨é¨', |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'postId', |
| | | label: 'å²ä½', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getSimplePostList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©å²ä½', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'roleIds', |
| | | label: 'ç¨æ·è§è²', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getSimpleRoleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | mode: 'multiple', |
| | | placeholder: 'è¯·éæ©è§è²', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'hireDate', |
| | | label: 'å
¥èæ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å
¥èæ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'regularDate', |
| | | label: 'è½¬æ£æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©è½¬æ£æ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'employeeStatus', |
| | | label: 'åå·¥ç¶æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | buttonStyle: 'solid', |
| | | optionType: 'button', |
| | | options: getDictOptions(DICT_TYPE.HRM_EMPLOYEE_STATUS, 'number'), |
| | | }, |
| | | rules: z.number().default(1), |
| | | }, |
| | | { |
| | | fieldName: 'birthday', |
| | | label: 'åºçæ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©åºçæ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'email', |
| | | label: 'é®ç®±', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥é®ç®±', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'idCard', |
| | | label: '身份è¯å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥èº«ä»½è¯å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'annualLeaveBalance', |
| | | label: 'å¹´åä½é¢', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¹´åä½é¢', |
| | | min: 0, |
| | | precision: 1, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'sickLeaveBalance', |
| | | label: 'ç
åä½é¢', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç
åä½é¢', |
| | | min: 0, |
| | | precision: 1, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'baseSalary', |
| | | label: 'åºæ¬å·¥èµ', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åºæ¬å·¥èµ', |
| | | min: 0, |
| | | precision: 2, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'bankName', |
| | | label: '弿·é¶è¡', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¼æ·é¶è¡', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'bankAccount', |
| | | label: 'é¶è¡è´¦å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥é¶è¡è´¦å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'address', |
| | | label: 'å±
ä½å°å', |
| | | component: 'Input', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å±
ä½å°å', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'employeeNo', |
| | | label: 'åå·¥ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åå·¥ç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'åå·¥å§å', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åå·¥å§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'deptId', |
| | | label: 'å½å±é¨é¨', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const data = await getDeptList(); |
| | | return handleTree(data); |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | childrenField: 'children', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©å½å±é¨é¨', |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'employeeStatus', |
| | | label: 'åå·¥ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_EMPLOYEE_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©åå·¥ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmEmployeeApi.Employee>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'employeeNo', |
| | | title: 'åå·¥ç¼å·', |
| | | minWidth: 120, |
| | | slots: { |
| | | default: 'employeeNo', |
| | | }, |
| | | }, |
| | | { field: 'name', title: 'åå·¥å§å', minWidth: 100 }, |
| | | { |
| | | field: 'gender', |
| | | title: 'æ§å«', |
| | | width: 80, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_GENDER }, |
| | | }, |
| | | }, |
| | | { field: 'phone', title: 'ææºå·ç ', minWidth: 120 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { field: 'postName', title: 'å²ä½', minWidth: 120 }, |
| | | { |
| | | field: 'employeeStatus', |
| | | title: 'åå·¥ç¶æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_EMPLOYEE_STATUS }, |
| | | }, |
| | | }, |
| | | { field: 'hireDate', title: 'å
¥èæ¥æ', width: 110 }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 180, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å工鿩弹çªçæç´¢è¡¨å */ |
| | | export function useEmployeeSelectGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'employeeNo', |
| | | label: 'åå·¥ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åå·¥ç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'åå·¥å§å', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åå·¥å§å', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å工鿩弹çªçå */ |
| | | export function useEmployeeSelectGridColumns(multiple: boolean): VxeTableGridOptions<HrmEmployeeApi.Employee>['columns'] { |
| | | return [ |
| | | { |
| | | type: multiple ? 'checkbox' : 'radio', |
| | | width: 50, |
| | | }, |
| | | { field: 'employeeNo', title: 'åå·¥ç¼å·', minWidth: 120 }, |
| | | { field: 'name', title: 'åå·¥å§å', minWidth: 100 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { field: 'postName', title: 'å²ä½', minWidth: 120 }, |
| | | { field: 'phone', title: 'ææºå·ç ', minWidth: 120 }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart } from '#/packages/utils/src'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteEmployee, exportEmployee, getEmployeePage } from '#/api/hrm/employee'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** å建åå·¥ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥çåå·¥ */ |
| | | function handleDetail(row: HrmEmployeeApi.Employee) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾åå·¥ */ |
| | | function handleEdit(row: HrmEmployeeApi.Employee) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å é¤åå·¥ */ |
| | | async function handleDelete(row: HrmEmployeeApi.Employee) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.name]), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteEmployee(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.name])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 导åºåå·¥ */ |
| | | async function handleExport() { |
| | | const data = await exportEmployee(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'åå·¥å表.xls', source: data }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getEmployeePage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmEmployeeApi.Employee>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="åå·¥å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['åå·¥']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:employee:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['hrm:employee:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #employeeNo="{ row }"> |
| | | <Button type="link" @click="handleDetail(row)">{{ row.employeeNo }}</Button> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:employee:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:employee:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.name]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import { computed } from 'vue'; |
| | | |
| | | import { Descriptions, DescriptionsItem, Tabs, TabPane, Tag } from 'ant-design-vue'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictLabel } from '#/packages/effects/hooks/src'; |
| | | |
| | | import EducationList from './education-list.vue'; |
| | | import WorkHistoryList from './work-history-list.vue'; |
| | | import EmergencyContactList from './emergency-contact-list.vue'; |
| | | import SocialSecurityList from './social-security-list.vue'; |
| | | |
| | | const props = defineProps<{ |
| | | employee: HrmEmployeeApi.Employee; |
| | | }>(); |
| | | |
| | | const genderLabel = computed(() => |
| | | getDictLabel(DICT_TYPE.HRM_GENDER, props.employee.gender), |
| | | ); |
| | | |
| | | const employeeStatusLabel = computed(() => |
| | | getDictLabel(DICT_TYPE.HRM_EMPLOYEE_STATUS, props.employee.employeeStatus), |
| | | ); |
| | | |
| | | const employeeStatusColor = computed(() => { |
| | | const status = props.employee.employeeStatus; |
| | | if (status === 1) return 'success'; |
| | | if (status === 2) return 'processing'; |
| | | if (status === 3) return 'default'; |
| | | return 'default'; |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="employee-detail"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <Descriptions title="åºæ¬ä¿¡æ¯" bordered :column="2" class="mb-4"> |
| | | <DescriptionsItem label="åå·¥ç¼å·">{{ employee.employeeNo }}</DescriptionsItem> |
| | | <DescriptionsItem label="åå·¥å§å">{{ employee.name }}</DescriptionsItem> |
| | | <DescriptionsItem label="æ§å«">{{ genderLabel }}</DescriptionsItem> |
| | | <DescriptionsItem label="åºçæ¥æ">{{ employee.birthday || '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="ææºå·ç ">{{ employee.phone }}</DescriptionsItem> |
| | | <DescriptionsItem label="é®ç®±">{{ employee.email || '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="身份è¯å·">{{ employee.idCard || '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="åå·¥ç¶æ"> |
| | | <Tag :color="employeeStatusColor">{{ employeeStatusLabel }}</Tag> |
| | | </DescriptionsItem> |
| | | </Descriptions> |
| | | |
| | | <!-- å·¥ä½ä¿¡æ¯ --> |
| | | <Descriptions title="å·¥ä½ä¿¡æ¯" bordered :column="2" class="mb-4"> |
| | | <DescriptionsItem label="å½å±é¨é¨">{{ employee.deptName || '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="å²ä½">{{ employee.postName || '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="å
¥èæ¥æ">{{ employee.hireDate }}</DescriptionsItem> |
| | | <DescriptionsItem label="è½¬æ£æ¥æ">{{ employee.regularDate || '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="ç¦»èæ¥æ">{{ employee.leaveDate || '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="åºæ¬å·¥èµ">{{ employee.baseSalary ? `Â¥${employee.baseSalary.toFixed(2)}` : '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="ç¨æ·è§è²" :span="2"> |
| | | {{ Array.isArray(employee.roleNames) ? employee.roleNames.join('ã') : employee.roleNames || '-' }} |
| | | </DescriptionsItem> |
| | | <DescriptionsItem label="å¹´åä½é¢">{{ employee.annualLeaveBalance ?? 0 }} 天</DescriptionsItem> |
| | | <DescriptionsItem label="ç
åä½é¢">{{ employee.sickLeaveBalance ?? 0 }} 天</DescriptionsItem> |
| | | </Descriptions> |
| | | |
| | | <!-- è´¢å¡ä¿¡æ¯ --> |
| | | <Descriptions title="è´¢å¡ä¿¡æ¯" bordered :column="2" class="mb-4"> |
| | | <DescriptionsItem label="弿·é¶è¡">{{ employee.bankName || '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="é¶è¡è´¦å·">{{ employee.bankAccount || '-' }}</DescriptionsItem> |
| | | <DescriptionsItem label="å±
ä½å°å" :span="2">{{ employee.address || '-' }}</DescriptionsItem> |
| | | </Descriptions> |
| | | |
| | | <!-- 夿³¨ --> |
| | | <Descriptions title="å
¶ä»ä¿¡æ¯" bordered :column="1" class="mb-4"> |
| | | <DescriptionsItem label="夿³¨">{{ employee.remark || '-' }}</DescriptionsItem> |
| | | </Descriptions> |
| | | |
| | | <!-- åè¡¨ä¿¡æ¯ --> |
| | | <Tabs v-if="employee.id" class="mt-4"> |
| | | <Tabs.TabPane key="education" tab="æè²ç»å"> |
| | | <EducationList form-type="detail" :employee-id="employee.id" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="workHistory" tab="å·¥ä½ç»å"> |
| | | <WorkHistoryList form-type="detail" :employee-id="employee.id" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="emergencyContact" tab="ç´§æ¥è系人"> |
| | | <EmergencyContactList form-type="detail" :employee-id="employee.id" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="socialSecurity" tab="社ä¿å
¬ç§¯éæ¡£æ¡"> |
| | | <SocialSecurityList form-type="detail" :employee-id="employee.id" :user-id="employee.userId" /> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .employee-detail { |
| | | padding: 0 16px; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import type { DefaultOptionType } from 'ant-design-vue/es/select'; |
| | | |
| | | import { computed, nextTick, watch } from 'vue'; |
| | | |
| | | import { IconifyIcon } from '#/packages/icons/src'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { Button, DatePicker, Input, Select } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getEducationListByEmployeeId } from '#/api/hrm/employee/education'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | const props = defineProps<{ |
| | | employeeId?: number; |
| | | formType: FormType; |
| | | }>(); |
| | | |
| | | const isReadOnly = computed(() => props.formType === 'detail'); |
| | | |
| | | const degreeOptions = getDictOptions(DICT_TYPE.HRM_DEGREE, 'number') as DefaultOptionType[]; |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: [ |
| | | { field: 'schoolName', title: '妿 ¡åç§°', minWidth: 150, slots: { default: 'schoolName' } }, |
| | | { field: 'major', title: 'ä¸ä¸', minWidth: 120, slots: { default: 'major' } }, |
| | | { |
| | | field: 'degree', |
| | | title: 'å¦å', |
| | | minWidth: 100, |
| | | slots: { default: 'degree' }, |
| | | }, |
| | | { |
| | | field: 'startDate', |
| | | title: 'å
¥å¦æ¶é´', |
| | | minWidth: 120, |
| | | slots: { default: 'startDate' }, |
| | | }, |
| | | { |
| | | field: 'endDate', |
| | | title: 'æ¯ä¸æ¶é´', |
| | | minWidth: 120, |
| | | slots: { default: 'endDate' }, |
| | | }, |
| | | { field: 'certificateNo', title: 'è¯ä¹¦ç¼å·', minWidth: 140, slots: { default: 'certificateNo' } }, |
| | | { |
| | | title: 'æä½', |
| | | width: 80, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | visible: !isReadOnly.value, |
| | | }, |
| | | ], |
| | | border: true, |
| | | showOverflow: true, |
| | | autoResize: true, |
| | | keepSource: true, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | pagerConfig: { |
| | | enabled: false, |
| | | }, |
| | | toolbarConfig: { |
| | | enabled: false, |
| | | }, |
| | | } as VxeTableGridOptions<HrmEmployeeApi.Education>, |
| | | }); |
| | | |
| | | /** æ·»å æè²ç»å */ |
| | | async function handleAdd() { |
| | | await gridApi.grid.insertAt({} as HrmEmployeeApi.Education, -1); |
| | | } |
| | | |
| | | /** å 餿è²ç»å */ |
| | | async function handleDelete(row: HrmEmployeeApi.Education) { |
| | | await gridApi.grid.remove(row); |
| | | } |
| | | |
| | | /** æä¾è·åè¡¨æ ¼æ°æ®çæ¹æ³ä¾ç¶ç»ä»¶è°ç¨ */ |
| | | defineExpose({ |
| | | getData: (): HrmEmployeeApi.Education[] => { |
| | | const data = gridApi.grid.getData() as HrmEmployeeApi.Education[]; |
| | | const removeRecords = gridApi.grid.getRemoveRecords() as HrmEmployeeApi.Education[]; |
| | | const insertRecords = gridApi.grid.getInsertRecords() as HrmEmployeeApi.Education[]; |
| | | return [ |
| | | ...data.filter((row) => !removeRecords.some((removed) => removed.id === row.id)), |
| | | ...insertRecords.map((row: any) => ({ ...row, id: undefined })), |
| | | ]; |
| | | }, |
| | | }); |
| | | |
| | | /** çå¬åå·¥IDååï¼å è½½åè¡¨æ°æ® */ |
| | | watch( |
| | | () => props.employeeId, |
| | | async (val) => { |
| | | if (!val) { |
| | | return; |
| | | } |
| | | await nextTick(); |
| | | await gridApi.grid.loadData(await getEducationListByEmployeeId(props.employeeId!)); |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | </script> |
| | | |
| | | <template> |
| | | <Grid class="w-full"> |
| | | <template #schoolName="{ row }"> |
| | | <Input v-model:value="row.schoolName" :disabled="isReadOnly" placeholder="请è¾å
¥å¦æ ¡åç§°" /> |
| | | </template> |
| | | <template #major="{ row }"> |
| | | <Input v-model:value="row.major" :disabled="isReadOnly" placeholder="请è¾å
¥ä¸ä¸" /> |
| | | </template> |
| | | <template #degree="{ row }"> |
| | | <Select |
| | | v-model:value="row.degree" |
| | | :disabled="isReadOnly" |
| | | :options="degreeOptions" |
| | | placeholder="è¯·éæ©å¦å" |
| | | allowClear |
| | | /> |
| | | </template> |
| | | <template #startDate="{ row }"> |
| | | <DatePicker |
| | | v-model:value="row.startDate" |
| | | :disabled="isReadOnly" |
| | | placeholder="è¯·éæ©å
¥å¦æ¶é´" |
| | | valueFormat="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | </template> |
| | | <template #endDate="{ row }"> |
| | | <DatePicker |
| | | v-model:value="row.endDate" |
| | | :disabled="isReadOnly" |
| | | placeholder="è¯·éæ©æ¯ä¸æ¶é´" |
| | | valueFormat="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | </template> |
| | | <template #certificateNo="{ row }"> |
| | | <Input v-model:value="row.certificateNo" :disabled="isReadOnly" placeholder="请è¾å
¥è¯ä¹¦ç¼å·" /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.delete'), |
| | | danger: true, |
| | | type: 'link', |
| | | icon: ACTION_ICON.DELETE, |
| | | onClick: handleDelete.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | <div v-if="!isReadOnly" class="-mt-4 flex justify-center"> |
| | | <Button type="primary" ghost @click="handleAdd"> |
| | | <IconifyIcon icon="lucide:plus" /> |
| | | æ·»å æè²ç»å |
| | | </Button> |
| | | </div> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import { computed, nextTick, watch } from 'vue'; |
| | | |
| | | import { IconifyIcon } from '#/packages/icons/src'; |
| | | |
| | | import { Button, Input } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getEmergencyContactListByEmployeeId } from '#/api/hrm/employee/emergency-contact'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | const props = defineProps<{ |
| | | employeeId?: number; |
| | | formType: FormType; |
| | | }>(); |
| | | |
| | | const isReadOnly = computed(() => props.formType === 'detail'); |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: [ |
| | | { field: 'contactName', title: 'è系人å§å', minWidth: 120, slots: { default: 'contactName' } }, |
| | | { field: 'relationship', title: 'å
³ç³»', minWidth: 100, slots: { default: 'relationship' } }, |
| | | { field: 'phone', title: 'èç³»çµè¯', minWidth: 130, slots: { default: 'phone' } }, |
| | | { field: 'address', title: 'å°å', minWidth: 200, slots: { default: 'address' } }, |
| | | { |
| | | title: 'æä½', |
| | | width: 80, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | visible: !isReadOnly.value, |
| | | }, |
| | | ], |
| | | border: true, |
| | | showOverflow: true, |
| | | autoResize: true, |
| | | keepSource: true, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | pagerConfig: { |
| | | enabled: false, |
| | | }, |
| | | toolbarConfig: { |
| | | enabled: false, |
| | | }, |
| | | } as VxeTableGridOptions<HrmEmployeeApi.EmergencyContact>, |
| | | }); |
| | | |
| | | /** æ·»å ç´§æ¥è系人 */ |
| | | async function handleAdd() { |
| | | await gridApi.grid.insertAt({} as HrmEmployeeApi.EmergencyContact, -1); |
| | | } |
| | | |
| | | /** å é¤ç´§æ¥è系人 */ |
| | | async function handleDelete(row: HrmEmployeeApi.EmergencyContact) { |
| | | await gridApi.grid.remove(row); |
| | | } |
| | | |
| | | /** æä¾è·åè¡¨æ ¼æ°æ®çæ¹æ³ä¾ç¶ç»ä»¶è°ç¨ */ |
| | | defineExpose({ |
| | | getData: (): HrmEmployeeApi.EmergencyContact[] => { |
| | | const data = gridApi.grid.getData() as HrmEmployeeApi.EmergencyContact[]; |
| | | const removeRecords = gridApi.grid.getRemoveRecords() as HrmEmployeeApi.EmergencyContact[]; |
| | | const insertRecords = gridApi.grid.getInsertRecords() as HrmEmployeeApi.EmergencyContact[]; |
| | | return [ |
| | | ...data.filter((row) => !removeRecords.some((removed) => removed.id === row.id)), |
| | | ...insertRecords.map((row: any) => ({ ...row, id: undefined })), |
| | | ]; |
| | | }, |
| | | }); |
| | | |
| | | /** çå¬åå·¥IDååï¼å è½½åè¡¨æ°æ® */ |
| | | watch( |
| | | () => props.employeeId, |
| | | async (val) => { |
| | | if (!val) { |
| | | return; |
| | | } |
| | | await nextTick(); |
| | | await gridApi.grid.loadData(await getEmergencyContactListByEmployeeId(props.employeeId!)); |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | </script> |
| | | |
| | | <template> |
| | | <Grid class="w-full"> |
| | | <template #contactName="{ row }"> |
| | | <Input v-model:value="row.contactName" :disabled="isReadOnly" placeholder="请è¾å
¥è系人å§å" /> |
| | | </template> |
| | | <template #relationship="{ row }"> |
| | | <Input v-model:value="row.relationship" :disabled="isReadOnly" placeholder="请è¾å
¥å
³ç³»" /> |
| | | </template> |
| | | <template #phone="{ row }"> |
| | | <Input v-model:value="row.phone" :disabled="isReadOnly" placeholder="请è¾å
¥èç³»çµè¯" /> |
| | | </template> |
| | | <template #address="{ row }"> |
| | | <Input v-model:value="row.address" :disabled="isReadOnly" placeholder="请è¾å
¥å°å" /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.delete'), |
| | | danger: true, |
| | | type: 'link', |
| | | icon: ACTION_ICON.DELETE, |
| | | onClick: handleDelete.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | <div v-if="!isReadOnly" class="-mt-4 flex justify-center"> |
| | | <Button type="primary" ghost @click="handleAdd"> |
| | | <IconifyIcon icon="lucide:plus" /> |
| | | æ·»å ç´§æ¥è系人 |
| | | </Button> |
| | | </div> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, Tabs, Input } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createEmployee, getEmployee, updateEmployee } from '#/api/hrm/employee'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | import EducationList from './education-list.vue'; |
| | | import WorkHistoryList from './work-history-list.vue'; |
| | | import EmergencyContactList from './emergency-contact-list.vue'; |
| | | import SocialSecurityList from './social-security-list.vue'; |
| | | import EmployeeDetail from './detail.vue'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | const employeeId = ref<number>(); |
| | | const employeeData = ref<HrmEmployeeApi.Employee>(); |
| | | const subTabsName = ref('education'); |
| | | const remark = ref<string>(); |
| | | |
| | | // å表ç»ä»¶ ref |
| | | const educationListRef = ref<InstanceType<typeof EducationList>>(); |
| | | const workHistoryListRef = ref<InstanceType<typeof WorkHistoryList>>(); |
| | | const emergencyContactListRef = ref<InstanceType<typeof EmergencyContactList>>(); |
| | | const socialSecurityListRef = ref<InstanceType<typeof SocialSecurityList>>(); |
| | | |
| | | const isDetail = computed(() => formType.value === 'detail'); |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return 'æ¥çåå·¥'; |
| | | } |
| | | return formType.value === 'update' ? 'ä¿®æ¹åå·¥' : 'æ°å¢åå·¥'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 150, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = (await formApi.getValues()) as HrmEmployeeApi.Employee; |
| | | data.remark = remark.value; |
| | | // è·ååè¡¨æ°æ® |
| | | data.educations = educationListRef.value?.getData(); |
| | | data.workHistories = workHistoryListRef.value?.getData(); |
| | | data.emergencyContacts = emergencyContactListRef.value?.getData(); |
| | | try { |
| | | if (data.id) { |
| | | await updateEmployee(data); |
| | | } else { |
| | | await createEmployee(data); |
| | | } |
| | | // å
³éå¹¶æç¤º |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | employeeId.value = undefined; |
| | | employeeData.value = undefined; |
| | | remark.value = undefined; |
| | | return; |
| | | } |
| | | // å è½½æ°æ® |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | if (data.formType !== 'detail') { |
| | | formApi.setState({ schema: useFormSchema(data.formType) }); |
| | | formApi.setDisabled(false); |
| | | modalApi.setState({ showConfirmButton: true }); |
| | | } else { |
| | | modalApi.setState({ showConfirmButton: false }); |
| | | } |
| | | if (!data?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const formData = await getEmployee(data.id); |
| | | employeeId.value = formData.id; |
| | | employeeData.value = formData; |
| | | remark.value = formData.remark; |
| | | if (data.formType !== 'detail') { |
| | | await formApi.setValues(formData); |
| | | } |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[1100px]"> |
| | | <!-- 详æ
模å¼ï¼ä½¿ç¨ä¸é¨ç详æ
å±ç¤ºç»ä»¶ --> |
| | | <EmployeeDetail v-if="isDetail && employeeData" :employee="employeeData" /> |
| | | <!-- æ°å¢/ç¼è¾æ¨¡å¼ï¼ä½¿ç¨è¡¨å --> |
| | | <template v-else> |
| | | <Form class="mx-4" /> |
| | | <Tabs |
| | | v-if="!isDetail || employeeId" |
| | | v-model:active-key="subTabsName" |
| | | class="mx-4 mt-4" |
| | | > |
| | | <Tabs.TabPane key="education" tab="æè²ç»å"> |
| | | <EducationList |
| | | ref="educationListRef" |
| | | :form-type="formType" |
| | | :employee-id="employeeId" |
| | | /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="workHistory" tab="å·¥ä½ç»å"> |
| | | <WorkHistoryList |
| | | ref="workHistoryListRef" |
| | | :form-type="formType" |
| | | :employee-id="employeeId" |
| | | /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="emergencyContact" tab="ç´§æ¥è系人"> |
| | | <EmergencyContactList |
| | | ref="emergencyContactListRef" |
| | | :form-type="formType" |
| | | :employee-id="employeeId" |
| | | /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="socialSecurity" tab="社ä¿å
¬ç§¯éæ¡£æ¡" v-if="employeeId"> |
| | | <SocialSecurityList |
| | | ref="socialSecurityListRef" |
| | | :form-type="formType" |
| | | :employee-id="employeeId" |
| | | :user-id="employeeData?.userId" |
| | | /> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | <div class="mx-4 mt-4"> |
| | | <div class="mb-2 font-medium">夿³¨</div> |
| | | <Input.TextArea |
| | | v-model:value="remark" |
| | | placeholder="请è¾å
¥å¤æ³¨" |
| | | :rows="3" |
| | | /> |
| | | </div> |
| | | </template> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmEmployeeSocialSecurityApi } from '#/api/hrm/salary/employee-social-security'; |
| | | import type { HrmSocialSecuritySchemeApi } from '#/api/hrm/salary/social-security-scheme'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, Form, FormItem, Select, InputNumber, DatePicker, RadioGroup, Textarea } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | createEmployeeSocialSecurity, |
| | | getEmployeeSocialSecurity, |
| | | updateEmployeeSocialSecurity, |
| | | } from '#/api/hrm/salary/employee-social-security'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<'create' | 'update'>('create'); |
| | | const userId = ref<number>(); |
| | | const schemeList = ref<HrmSocialSecuritySchemeApi.SocialSecurityScheme[]>([]); |
| | | |
| | | const formState = ref<HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity>({ |
| | | userId: undefined, |
| | | socialSecuritySchemeId: undefined, |
| | | socialSecurityBase: 0, |
| | | housingFundBase: 0, |
| | | effectiveDate: undefined, |
| | | status: 0, |
| | | remark: '', |
| | | }); |
| | | |
| | | const getTitle = computed(() => |
| | | formType.value === 'update' ? 'ä¿®æ¹ç¤¾ä¿å
¬ç§¯éæ¡£æ¡' : 'æ°å¢ç¤¾ä¿å
¬ç§¯éæ¡£æ¡', |
| | | ); |
| | | |
| | | const rules = { |
| | | socialSecuritySchemeId: [{ required: true, message: 'è¯·éæ©ç¤¾ä¿æ¹æ¡' }], |
| | | socialSecurityBase: [{ required: true, message: '请è¾å
¥ç¤¾ä¿åºæ°' }], |
| | | housingFundBase: [{ required: true, message: '请è¾å
¥å
¬ç§¯éåºæ°' }], |
| | | effectiveDate: [{ required: true, message: 'è¯·éæ©çææ¥æ' }], |
| | | }; |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | try { |
| | | const data = { ...formState.value, userId: userId.value }; |
| | | if (formState.value.id) { |
| | | await updateEmployeeSocialSecurity(data); |
| | | } else { |
| | | await createEmployeeSocialSecurity(data); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ |
| | | formType: 'create' | 'update'; |
| | | id?: number; |
| | | userId?: number; |
| | | schemeList?: HrmSocialSecuritySchemeApi.SocialSecurityScheme[]; |
| | | }>(); |
| | | formType.value = data.formType; |
| | | userId.value = data.userId; |
| | | schemeList.value = data.schemeList || []; |
| | | |
| | | // é置表å |
| | | formState.value = { |
| | | userId: data.userId, |
| | | socialSecuritySchemeId: undefined, |
| | | socialSecurityBase: 0, |
| | | housingFundBase: 0, |
| | | effectiveDate: undefined, |
| | | status: 0, |
| | | remark: '', |
| | | }; |
| | | |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | | try { |
| | | const detail = await getEmployeeSocialSecurity(data.id); |
| | | formState.value = detail; |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[700px]"> |
| | | <Form |
| | | :model="formState" |
| | | :rules="rules" |
| | | :label-col="{ style: { width: '100px' } }" |
| | | layout="horizontal" |
| | | > |
| | | <FormItem label="ç¤¾ä¿æ¹æ¡" name="socialSecuritySchemeId"> |
| | | <Select |
| | | v-model:value="formState.socialSecuritySchemeId" |
| | | :options="schemeList.map(s => ({ label: s.name, value: s.id }))" |
| | | placeholder="è¯·éæ©ç¤¾ä¿å
¬ç§¯éæ¹æ¡" |
| | | /> |
| | | </FormItem> |
| | | |
| | | <FormItem label="社ä¿åºæ°" name="socialSecurityBase"> |
| | | <InputNumber |
| | | v-model:value="formState.socialSecurityBase" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请è¾å
¥ç¤¾ä¿åºæ°" |
| | | /> |
| | | </FormItem> |
| | | |
| | | <FormItem label="å
¬ç§¯éåºæ°" name="housingFundBase"> |
| | | <InputNumber |
| | | v-model:value="formState.housingFundBase" |
| | | :min="0" |
| | | :precision="2" |
| | | style="width: 100%" |
| | | placeholder="请è¾å
¥å
¬ç§¯éåºæ°" |
| | | /> |
| | | </FormItem> |
| | | |
| | | <FormItem label="çææ¥æ" name="effectiveDate"> |
| | | <DatePicker |
| | | v-model:value="formState.effectiveDate" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | placeholder="è¯·éæ©çææ¥æ" |
| | | /> |
| | | </FormItem> |
| | | |
| | | <FormItem label="ç¶æ" name="status"> |
| | | <RadioGroup |
| | | v-model:value="formState.status" |
| | | :options="[ |
| | | { label: 'å¯ç¨', value: 0 }, |
| | | { label: 'ç¦ç¨', value: 1 }, |
| | | ]" |
| | | /> |
| | | </FormItem> |
| | | |
| | | <FormItem label="夿³¨"> |
| | | <Textarea |
| | | v-model:value="formState.remark" |
| | | :rows="2" |
| | | placeholder="请è¾å
¥å¤æ³¨" |
| | | /> |
| | | </FormItem> |
| | | </Form> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from './form.vue'; |
| | | import type { HrmEmployeeSocialSecurityApi } from '#/api/hrm/salary/employee-social-security'; |
| | | |
| | | import { ref, computed, watch } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, Table, Tag, Button } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction } from '#/adapter/vxe-table'; |
| | | import { |
| | | createEmployeeSocialSecurity, |
| | | deleteEmployeeSocialSecurity, |
| | | getEmployeeSocialSecurityListByUser, |
| | | updateEmployeeSocialSecurity, |
| | | } from '#/api/hrm/salary/employee-social-security'; |
| | | import { getSocialSecuritySchemeList } from '#/api/hrm/salary/social-security-scheme'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import SocialSecurityForm from './social-security-form.vue'; |
| | | |
| | | const props = defineProps<{ |
| | | employeeId?: number; |
| | | userId?: number; |
| | | formType?: FormType; |
| | | }>(); |
| | | |
| | | const emit = defineEmits(['refresh']); |
| | | |
| | | const list = ref<HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity[]>([]); |
| | | const schemeList = ref<any[]>([]); |
| | | const loading = ref(false); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: SocialSecurityForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°å表 */ |
| | | async function handleRefresh() { |
| | | if (!props.userId) return; |
| | | loading.value = true; |
| | | try { |
| | | list.value = await getEmployeeSocialSecurityListByUser(props.userId); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | emit('refresh'); |
| | | } |
| | | |
| | | /** æ°å¢æ¡£æ¡ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ |
| | | formType: 'create', |
| | | userId: props.userId, |
| | | schemeList: schemeList.value, |
| | | }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾æ¡£æ¡ */ |
| | | function handleEdit(row: HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity) { |
| | | formModalApi.setData({ |
| | | formType: 'update', |
| | | id: row.id, |
| | | userId: props.userId, |
| | | schemeList: schemeList.value, |
| | | }).open(); |
| | | } |
| | | |
| | | /** å 餿¡£æ¡ */ |
| | | async function handleDelete(row: HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity) { |
| | | await deleteEmployeeSocialSecurity(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', ['社ä¿å
¬ç§¯éæ¡£æ¡'])); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** è·åæ¹æ¡å表 */ |
| | | async function getSchemeList() { |
| | | schemeList.value = await getSocialSecuritySchemeList({ status: 0 }); |
| | | } |
| | | |
| | | /** çå¬ userId åå */ |
| | | watch( |
| | | () => props.userId, |
| | | (val) => { |
| | | if (val) { |
| | | handleRefresh(); |
| | | getSchemeList(); |
| | | } |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | |
| | | const columns = [ |
| | | { title: 'ç¤¾ä¿æ¹æ¡', dataIndex: 'schemeName', width: 180 }, |
| | | { title: '社ä¿åºæ°', dataIndex: 'socialSecurityBase', width: 100 }, |
| | | { title: 'å
¬ç§¯éåºæ°', dataIndex: 'housingFundBase', width: 100 }, |
| | | { title: 'çææ¥æ', dataIndex: 'effectiveDate', width: 110 }, |
| | | { |
| | | title: 'ç¶æ', |
| | | dataIndex: 'status', |
| | | width: 80, |
| | | key: 'status', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | key: 'action', |
| | | width: 150, |
| | | fixed: 'right', |
| | | }, |
| | | ]; |
| | | |
| | | const isDetail = computed(() => props.formType === 'detail'); |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="social-security-list"> |
| | | <FormModal @success="handleRefresh" /> |
| | | |
| | | <!-- å·¥å
·æ --> |
| | | <div v-if="!isDetail" class="mb-3"> |
| | | <Button type="primary" @click="handleCreate"> |
| | | æ°å¢ç¤¾ä¿å
¬ç§¯éæ¡£æ¡ |
| | | </Button> |
| | | </div> |
| | | |
| | | <!-- è¡¨æ ¼ --> |
| | | <Table |
| | | :columns="columns" |
| | | :data-source="list" |
| | | :loading="loading" |
| | | :pagination="false" |
| | | size="small" |
| | | row-key="id" |
| | | > |
| | | <template #bodyCell="{ column, record }"> |
| | | <template v-if="column.key === 'status'"> |
| | | <Tag :color="record.status === 0 ? 'success' : 'default'"> |
| | | {{ record.status === 0 ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </Tag> |
| | | </template> |
| | | <template v-else-if="column.key === 'action' && !isDetail"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | onClick: handleEdit.bind(null, record), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', ['社ä¿å
¬ç§¯éæ¡£æ¡']), |
| | | confirm: handleDelete.bind(null, record), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </template> |
| | | </Table> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .social-security-list { |
| | | padding: 0; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeApi } from '#/api/hrm/employee'; |
| | | |
| | | import { computed, nextTick, watch } from 'vue'; |
| | | |
| | | import { IconifyIcon } from '#/packages/icons/src'; |
| | | |
| | | import { Button, DatePicker, Input } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getWorkHistoryListByEmployeeId } from '#/api/hrm/employee/work-history'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | const props = defineProps<{ |
| | | employeeId?: number; |
| | | formType: FormType; |
| | | }>(); |
| | | |
| | | const isReadOnly = computed(() => props.formType === 'detail'); |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: [ |
| | | { field: 'companyName', title: 'å
¬å¸åç§°', minWidth: 150, slots: { default: 'companyName' } }, |
| | | { field: 'position', title: 'èä½', minWidth: 120, slots: { default: 'position' } }, |
| | | { |
| | | field: 'startDate', |
| | | title: 'å¼å§æ¶é´', |
| | | minWidth: 120, |
| | | slots: { default: 'startDate' }, |
| | | }, |
| | | { |
| | | field: 'endDate', |
| | | title: 'ç»ææ¶é´', |
| | | minWidth: 120, |
| | | slots: { default: 'endDate' }, |
| | | }, |
| | | { field: 'workContent', title: 'å·¥ä½å
容', minWidth: 200, slots: { default: 'workContent' } }, |
| | | { field: 'leaveReason', title: '离èåå ', minWidth: 150, slots: { default: 'leaveReason' } }, |
| | | { |
| | | title: 'æä½', |
| | | width: 80, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | visible: !isReadOnly.value, |
| | | }, |
| | | ], |
| | | border: true, |
| | | showOverflow: true, |
| | | autoResize: true, |
| | | keepSource: true, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | pagerConfig: { |
| | | enabled: false, |
| | | }, |
| | | toolbarConfig: { |
| | | enabled: false, |
| | | }, |
| | | } as VxeTableGridOptions<HrmEmployeeApi.WorkHistory>, |
| | | }); |
| | | |
| | | /** æ·»å å·¥ä½ç»å */ |
| | | async function handleAdd() { |
| | | await gridApi.grid.insertAt({} as HrmEmployeeApi.WorkHistory, -1); |
| | | } |
| | | |
| | | /** å é¤å·¥ä½ç»å */ |
| | | async function handleDelete(row: HrmEmployeeApi.WorkHistory) { |
| | | await gridApi.grid.remove(row); |
| | | } |
| | | |
| | | /** æä¾è·åè¡¨æ ¼æ°æ®çæ¹æ³ä¾ç¶ç»ä»¶è°ç¨ */ |
| | | defineExpose({ |
| | | getData: (): HrmEmployeeApi.WorkHistory[] => { |
| | | const data = gridApi.grid.getData() as HrmEmployeeApi.WorkHistory[]; |
| | | const removeRecords = gridApi.grid.getRemoveRecords() as HrmEmployeeApi.WorkHistory[]; |
| | | const insertRecords = gridApi.grid.getInsertRecords() as HrmEmployeeApi.WorkHistory[]; |
| | | return [ |
| | | ...data.filter((row) => !removeRecords.some((removed) => removed.id === row.id)), |
| | | ...insertRecords.map((row: any) => ({ ...row, id: undefined })), |
| | | ]; |
| | | }, |
| | | }); |
| | | |
| | | /** çå¬åå·¥IDååï¼å è½½åè¡¨æ°æ® */ |
| | | watch( |
| | | () => props.employeeId, |
| | | async (val) => { |
| | | if (!val) { |
| | | return; |
| | | } |
| | | await nextTick(); |
| | | await gridApi.grid.loadData(await getWorkHistoryListByEmployeeId(props.employeeId!)); |
| | | }, |
| | | { immediate: true }, |
| | | ); |
| | | </script> |
| | | |
| | | <template> |
| | | <Grid class="w-full"> |
| | | <template #companyName="{ row }"> |
| | | <Input v-model:value="row.companyName" :disabled="isReadOnly" placeholder="请è¾å
¥å
¬å¸åç§°" /> |
| | | </template> |
| | | <template #position="{ row }"> |
| | | <Input v-model:value="row.position" :disabled="isReadOnly" placeholder="请è¾å
¥èä½" /> |
| | | </template> |
| | | <template #startDate="{ row }"> |
| | | <DatePicker |
| | | v-model:value="row.startDate" |
| | | :disabled="isReadOnly" |
| | | placeholder="è¯·éæ©å¼å§æ¶é´" |
| | | valueFormat="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | </template> |
| | | <template #endDate="{ row }"> |
| | | <DatePicker |
| | | v-model:value="row.endDate" |
| | | :disabled="isReadOnly" |
| | | placeholder="è¯·éæ©ç»ææ¶é´" |
| | | valueFormat="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | </template> |
| | | <template #workContent="{ row }"> |
| | | <Input v-model:value="row.workContent" :disabled="isReadOnly" placeholder="请è¾å
¥å·¥ä½å
容" /> |
| | | </template> |
| | | <template #leaveReason="{ row }"> |
| | | <Input v-model:value="row.leaveReason" :disabled="isReadOnly" placeholder="请è¾å
¥ç¦»èåå " /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.delete'), |
| | | danger: true, |
| | | type: 'link', |
| | | icon: ACTION_ICON.DELETE, |
| | | onClick: handleDelete.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | <div v-if="!isReadOnly" class="-mt-4 flex justify-center"> |
| | | <Button type="primary" ghost @click="handleAdd"> |
| | | <IconifyIcon icon="lucide:plus" /> |
| | | æ·»å å·¥ä½ç»å |
| | | </Button> |
| | | </div> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmLeaveApi } from '#/api/hrm/leave'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹è¯·åç³è¯·ç表å */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'leaveType', |
| | | label: '请åç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©è¯·åç±»å', |
| | | options: getDictOptions(DICT_TYPE.HRM_LEAVE_TYPE, 'number'), |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'startTime', |
| | | label: 'å¼å§æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å¼å§æ¶é´', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'endTime', |
| | | label: 'ç»ææ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ç»ææ¶é´', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'duration', |
| | | label: 'è¯·åæ¶é¿', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è¯·åæ¶é¿', |
| | | min: 0.5, |
| | | precision: 2, |
| | | step: 0.5, |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | suffix: '天', |
| | | }, |
| | | { |
| | | fieldName: 'reason', |
| | | label: '请ååå ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è¯·ååå ', |
| | | rows: 3, |
| | | maxlength: 500, |
| | | showCount: true, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'fileUrl', |
| | | label: 'éä»¶', |
| | | component: 'Input', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请ä¸ä¼ éä»¶', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'leaveType', |
| | | label: '请åç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_LEAVE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©è¯·åç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '审æ¹ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_AUDIT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å®¡æ¹ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmLeaveApi.Leave>['columns'] { |
| | | return [ |
| | | { field: 'id', title: 'ç³è¯·ç¼å·', width: 80 }, |
| | | { field: 'employeeName', title: 'ç³è¯·äºº', minWidth: 100 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { |
| | | field: 'leaveType', |
| | | title: '请åç±»å', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_LEAVE_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'startTime', title: 'å¼å§æ¶é´', width: 160 }, |
| | | { field: 'endTime', title: 'ç»ææ¶é´', width: 160 }, |
| | | { field: 'duration', title: 'æ¶é¿(天)', width: 90 }, |
| | | { |
| | | field: 'status', |
| | | title: '审æ¹ç¶æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_AUDIT_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 200, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmLeaveApi } from '#/api/hrm/leave'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | cancelLeave, |
| | | deleteLeave, |
| | | getLeavePage, |
| | | submitLeave, |
| | | } from '#/api/hrm/leave'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** å建请åç³è¯· */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥ç请åç³è¯· */ |
| | | function handleDetail(row: HrmLeaveApi.Leave) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾è¯·åç³è¯· */ |
| | | function handleEdit(row: HrmLeaveApi.Leave) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å é¤è¯·åç³è¯· */ |
| | | async function handleDelete(row: HrmLeaveApi.Leave) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', ['请åç³è¯·']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteLeave(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', ['请åç³è¯·'])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æäº¤è¯·åç³è¯· */ |
| | | async function handleSubmit(row: HrmLeaveApi.Leave) { |
| | | await submitLeave(row.id!); |
| | | message.success('æäº¤æå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** æ¤é请åç³è¯· */ |
| | | async function handleCancel(row: HrmLeaveApi.Leave) { |
| | | await cancelLeave(row.id!); |
| | | message.success('æ¤éæå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getLeavePage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmLeaveApi.Leave>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="请åç³è¯·å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['请åç³è¯·']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:leave:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ¥ç', |
| | | type: 'link', |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:leave:update'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤', |
| | | type: 'link', |
| | | auth: ['hrm:leave:submit'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¤é', |
| | | type: 'link', |
| | | auth: ['hrm:leave:cancel'], |
| | | ifShow: row.status === 10, |
| | | onClick: handleCancel.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:leave:delete'], |
| | | ifShow: row.status === 0 || row.status === 40, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', ['请åç³è¯·']), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { HrmLeaveApi } from '#/api/hrm/leave'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createLeave, getLeave, updateLeave } from '#/api/hrm/leave'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const isDetail = computed(() => formType.value === 'detail'); |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return 'æ¥ç请åç³è¯·'; |
| | | } |
| | | return formType.value === 'update' ? 'ä¿®æ¹è¯·åç³è¯·' : 'æ°å¢è¯·åç³è¯·'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as HrmLeaveApi.Leave; |
| | | try { |
| | | if (data.id) { |
| | | await updateLeave(data); |
| | | } else { |
| | | await createLeave(data); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setState({ schema: useFormSchema(data.formType) }); |
| | | formApi.setDisabled(data.formType === 'detail'); |
| | | modalApi.setState({ showConfirmButton: data.formType !== 'detail' }); |
| | | if (!data?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await formApi.setValues(await getLeave(data.id)); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[600px]"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmResignationApi } from '#/api/hrm/resignation'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { getEmployeeSimpleList } from '#/api/hrm/employee'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç¦»èç³è¯·ç表å */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'resignationType', |
| | | label: '离èç±»å', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | buttonStyle: 'solid', |
| | | optionType: 'button', |
| | | options: getDictOptions(DICT_TYPE.HRM_RESIGNATION_TYPE, 'number'), |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'expectedDate', |
| | | label: 'é¢è®¡ç¦»èæ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©é¢è®¡ç¦»èæ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'handoverUserId', |
| | | label: '交æ¥äºº', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getEmployeeSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©äº¤æ¥äºº', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reason', |
| | | label: '离èåå ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç¦»èåå ', |
| | | rows: 3, |
| | | maxlength: 500, |
| | | showCount: true, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'fileUrl', |
| | | label: 'éä»¶', |
| | | component: 'Input', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请ä¸ä¼ éä»¶', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'resignationType', |
| | | label: '离èç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_RESIGNATION_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©ç¦»èç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '审æ¹ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_AUDIT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å®¡æ¹ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmResignationApi.Resignation>['columns'] { |
| | | return [ |
| | | { field: 'id', title: 'ç³è¯·ç¼å·', width: 80 }, |
| | | { field: 'employeeName', title: 'ç³è¯·äºº', minWidth: 100 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { field: 'postName', title: 'å²ä½', minWidth: 120 }, |
| | | { |
| | | field: 'resignationType', |
| | | title: '离èç±»å', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_RESIGNATION_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'expectedDate', title: 'é¢è®¡ç¦»èæ¥æ', width: 120 }, |
| | | { field: 'handoverUserName', title: '交æ¥äºº', minWidth: 100 }, |
| | | { |
| | | field: 'handoverStatus', |
| | | title: '交æ¥ç¶æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_HANDOVER_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: '审æ¹ç¶æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_AUDIT_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 200, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmResignationApi } from '#/api/hrm/resignation'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | cancelResignation, |
| | | deleteResignation, |
| | | getResignationPage, |
| | | submitResignation, |
| | | updateHandoverStatus, |
| | | } from '#/api/hrm/resignation'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** å建离èç³è¯· */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥ç离èç³è¯· */ |
| | | function handleDetail(row: HrmResignationApi.Resignation) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ç¦»èç³è¯· */ |
| | | function handleEdit(row: HrmResignationApi.Resignation) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å é¤ç¦»èç³è¯· */ |
| | | async function handleDelete(row: HrmResignationApi.Resignation) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', ['离èç³è¯·']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteResignation(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', ['离èç³è¯·'])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æäº¤ç¦»èç³è¯· */ |
| | | async function handleSubmit(row: HrmResignationApi.Resignation) { |
| | | await submitResignation(row.id!); |
| | | message.success('æäº¤æå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** æ¤é离èç³è¯· */ |
| | | async function handleCancel(row: HrmResignationApi.Resignation) { |
| | | await cancelResignation(row.id!); |
| | | message.success('æ¤éæå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** æ´æ°äº¤æ¥ç¶æ */ |
| | | async function handleUpdateHandover(row: HrmResignationApi.Resignation) { |
| | | // 交æ¥ç¶æï¼0-æªå¼å§ï¼10-è¿è¡ä¸ï¼20-已宿 |
| | | const nextStatus = row.handoverStatus === 0 ? 10 : row.handoverStatus === 10 ? 20 : 0; |
| | | const statusText = nextStatus === 10 ? 'è¿è¡ä¸' : nextStatus === 20 ? '已宿' : 'æªå¼å§'; |
| | | await updateHandoverStatus(row.id!, nextStatus); |
| | | message.success(`交æ¥ç¶æå·²æ´æ°ä¸ºï¼${statusText}`); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getResignationPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmResignationApi.Resignation>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="离èç³è¯·å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['离èç³è¯·']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:resignation:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ¥ç', |
| | | type: 'link', |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:resignation:update'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤', |
| | | type: 'link', |
| | | auth: ['hrm:resignation:submit'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¤é', |
| | | type: 'link', |
| | | auth: ['hrm:resignation:cancel'], |
| | | ifShow: row.status === 10, |
| | | onClick: handleCancel.bind(null, row), |
| | | }, |
| | | { |
| | | label: '交æ¥', |
| | | type: 'link', |
| | | auth: ['hrm:resignation:handover'], |
| | | ifShow: row.status === 20 && row.handoverStatus !== 20, |
| | | popConfirm: { |
| | | title: row.handoverStatus === 0 ? '确认å¼å§äº¤æ¥åï¼' : 'ç¡®è®¤å®æäº¤æ¥åï¼', |
| | | confirm: handleUpdateHandover.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:resignation:delete'], |
| | | ifShow: row.status === 0 || row.status === 40, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', ['离èç³è¯·']), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { HrmResignationApi } from '#/api/hrm/resignation'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createResignation, getResignation, updateResignation } from '#/api/hrm/resignation'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const isDetail = computed(() => formType.value === 'detail'); |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return 'æ¥ç离èç³è¯·'; |
| | | } |
| | | return formType.value === 'update' ? 'ä¿®æ¹ç¦»èç³è¯·' : 'æ°å¢ç¦»èç³è¯·'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 110, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as HrmResignationApi.Resignation; |
| | | try { |
| | | if (data.id) { |
| | | await updateResignation(data); |
| | | } else { |
| | | await createResignation(data); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setState({ schema: useFormSchema(data.formType) }); |
| | | formApi.setDisabled(data.formType === 'detail'); |
| | | modalApi.setState({ showConfirmButton: data.formType !== 'detail' }); |
| | | if (!data?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await formApi.setValues(await getResignation(data.id)); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[600px]"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmSalaryCalculationApi } from '#/api/hrm/salary/calculation'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { getEmployeeSimpleList } from '#/api/hrm/employee'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'period', |
| | | label: 'æ ¸ç®å¨æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | picker: 'month', |
| | | allowClear: true, |
| | | format: 'YYYY-MM', |
| | | valueFormat: 'YYYY-MM', |
| | | style: { width: '100%' }, |
| | | placeholder: 'è¯·éæ©æ ¸ç®å¨æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'userId', |
| | | label: 'åå·¥', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getEmployeeSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©åå·¥', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'deptId', |
| | | label: 'é¨é¨', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const data = await getDeptList(); |
| | | return handleTree(data); |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | childrenField: 'children', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©é¨é¨', |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_SALARY_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmSalaryCalculationApi.SalaryCalculation>['columns'] { |
| | | return [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { field: 'no', title: 'æ ¸ç®åå·', minWidth: 140 }, |
| | | { field: 'period', title: 'æ ¸ç®å¨æ', width: 100 }, |
| | | { field: 'userName', title: 'åå·¥å§å', minWidth: 100 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { field: 'baseSalary', title: 'åºæ¬å·¥èµ', width: 100, formatter: 'formatAmount2' }, |
| | | { field: 'performanceSalary', title: '绩æå·¥èµ', width: 100, formatter: 'formatAmount2' }, |
| | | { field: 'overtimePay', title: 'å çè´¹', width: 100, formatter: 'formatAmount2' }, |
| | | { field: 'mealAllowance', title: 'é¤è¡¥', width: 80, formatter: 'formatAmount2' }, |
| | | { field: 'transportAllowance', title: '交é补贴', width: 80, formatter: 'formatAmount2' }, |
| | | { field: 'otherAllowance', title: 'å
¶ä»è¡¥è´´', width: 80, formatter: 'formatAmount2' }, |
| | | { field: 'socialSecurityDeduction', title: 'ç¤¾ä¿æ£æ¬¾', width: 100, formatter: 'formatAmount2' }, |
| | | { field: 'housingFundDeduction', title: 'å
¬ç§¯éæ£æ¬¾', width: 100, formatter: 'formatAmount2' }, |
| | | { field: 'taxDeduction', title: 'ä¸ªç¨æ£æ¬¾', width: 100, formatter: 'formatAmount2' }, |
| | | { field: 'otherDeduction', title: 'å
¶ä»æ£æ¬¾', width: 80, formatter: 'formatAmount2' }, |
| | | { field: 'actualSalary', title: 'å®åå·¥èµ', width: 100, formatter: 'formatAmount2' }, |
| | | { field: 'workDays', title: 'åºå¤å¤©æ°', width: 80 }, |
| | | { field: 'overtimeHours', title: 'å çæ¶é¿(h)', width: 100 }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_SALARY_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 120, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** é¢è§è¡¨æ ¼çåå®ä¹ï¼ç¨äºAnt Design Tableï¼ */ |
| | | export function usePreviewColumns() { |
| | | return [ |
| | | { title: 'åå·¥å§å', dataIndex: 'userName', width: 100, fixed: 'left' }, |
| | | { title: 'é¨é¨', dataIndex: 'deptName', width: 120 }, |
| | | { title: 'åºæ¬å·¥èµ', dataIndex: 'baseSalary', width: 110 }, |
| | | { title: '绩æå·¥èµ', dataIndex: 'performanceSalary', width: 110 }, |
| | | { title: 'å çè´¹', dataIndex: 'overtimePay', width: 110 }, |
| | | { title: 'é¤è¡¥', dataIndex: 'mealAllowance', width: 100 }, |
| | | { title: '交é补贴', dataIndex: 'transportAllowance', width: 100 }, |
| | | { title: 'å
¶ä»è¡¥è´´', dataIndex: 'otherAllowance', width: 100 }, |
| | | { title: 'ç¤¾ä¿æ£æ¬¾', dataIndex: 'socialSecurityDeduction', width: 110 }, |
| | | { title: 'å
¬ç§¯éæ£æ¬¾', dataIndex: 'housingFundDeduction', width: 110 }, |
| | | { title: 'ä¸ªç¨æ£æ¬¾', dataIndex: 'taxDeduction', width: 110 }, |
| | | { title: 'å
¶ä»æ£æ¬¾', dataIndex: 'otherDeduction', width: 100 }, |
| | | { title: 'å®åå·¥èµ', dataIndex: 'actualSalary', width: 100, fixed: 'right' }, |
| | | { title: 'åºå¤å¤©æ°', dataIndex: 'workDays', width: 90 }, |
| | | { title: 'å çæ¶é¿', dataIndex: 'overtimeHours', width: 100 }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmSalaryCalculationApi } from '#/api/hrm/salary/calculation'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page } from '#/packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart } from '#/packages/utils/src'; |
| | | |
| | | import { message, Modal, Form, FormItem, DatePicker, TreeSelect, Table, InputNumber, Alert } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | calculateSalary, |
| | | confirmSalary, |
| | | exportSalaryCalculation, |
| | | getSalaryCalculationPage, |
| | | previewSalaryCalculation, |
| | | saveSalaryCalculation, |
| | | } from '#/api/hrm/salary/calculation'; |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema, usePreviewColumns } from './data'; |
| | | |
| | | // æ ¸ç®å¼¹çªç¸å
³ |
| | | const calculateVisible = ref(false); |
| | | const calculatePeriod = ref<string>(); |
| | | const calculateDeptId = ref<number>(); |
| | | const deptTreeData = ref<any[]>([]); |
| | | const previewLoading = ref(false); |
| | | const previewData = ref<HrmSalaryCalculationApi.SalaryCalculation[]>([]); |
| | | const submitLoading = ref(false); |
| | | const calculateLoading = ref(false); |
| | | |
| | | // æå¼æ ¸ç®å¼¹çª |
| | | async function openCalculateModal() { |
| | | const formValues = await gridApi.formApi.getValues(); |
| | | calculatePeriod.value = formValues.period; |
| | | calculateDeptId.value = formValues.deptId; |
| | | previewData.value = []; |
| | | // å è½½é¨é¨æ |
| | | const data = await getDeptList(); |
| | | deptTreeData.value = handleTree(data); |
| | | calculateVisible.value = true; |
| | | } |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ§è¡èªé
¬æ ¸ç®ï¼èªå¨è®¡ç®å¹¶ä¿åï¼ */ |
| | | async function handleCalculate() { |
| | | if (!calculatePeriod.value) { |
| | | message.warning('è¯·éæ©æ ¸ç®å¨æ'); |
| | | return; |
| | | } |
| | | calculateLoading.value = true; |
| | | try { |
| | | await calculateSalary({ |
| | | period: calculatePeriod.value, |
| | | deptId: calculateDeptId.value, |
| | | }); |
| | | message.success('èªé
¬æ ¸ç®æ§è¡æå'); |
| | | calculateVisible.value = false; |
| | | handleRefresh(); |
| | | } finally { |
| | | calculateLoading.value = false; |
| | | } |
| | | } |
| | | |
| | | /** é¢è§æ ¸ç®æ°æ® */ |
| | | async function handlePreview() { |
| | | if (!calculatePeriod.value) { |
| | | message.warning('è¯·éæ©æ ¸ç®å¨æ'); |
| | | return; |
| | | } |
| | | previewLoading.value = true; |
| | | try { |
| | | previewData.value = await previewSalaryCalculation({ |
| | | period: calculatePeriod.value, |
| | | deptId: calculateDeptId.value, |
| | | }); |
| | | if (previewData.value.length === 0) { |
| | | message.info('该æ¡ä»¶ä¸æ²¡æéè¦æ ¸ç®çåå·¥'); |
| | | } |
| | | } finally { |
| | | previewLoading.value = false; |
| | | } |
| | | } |
| | | |
| | | /** 计ç®å®åå·¥èµ */ |
| | | function calculateActualSalary(record: HrmSalaryCalculationApi.SalaryCalculation) { |
| | | const income = (record.baseSalary || 0) + |
| | | (record.performanceSalary || 0) + |
| | | (record.overtimePay || 0) + |
| | | (record.mealAllowance || 0) + |
| | | (record.transportAllowance || 0) + |
| | | (record.otherAllowance || 0); |
| | | const deduction = (record.socialSecurityDeduction || 0) + |
| | | (record.housingFundDeduction || 0) + |
| | | (record.taxDeduction || 0) + |
| | | (record.otherDeduction || 0); |
| | | return income - deduction; |
| | | } |
| | | |
| | | /** æ´æ°è¡æ°æ®å¹¶è®¡ç®å®åå·¥èµ */ |
| | | function updateFieldValue(record: HrmSalaryCalculationApi.SalaryCalculation, field: keyof HrmSalaryCalculationApi.SalaryCalculation, value: number) { |
| | | (record as any)[field] = value; |
| | | record.actualSalary = calculateActualSalary(record); |
| | | } |
| | | |
| | | /** æäº¤èªé
¬æ ¸ç®ï¼æå¨è°æ´åä¿åï¼ */ |
| | | async function handleSubmit() { |
| | | if (!calculatePeriod.value) { |
| | | message.warning('è¯·éæ©æ ¸ç®å¨æ'); |
| | | return; |
| | | } |
| | | if (previewData.value.length === 0) { |
| | | message.warning('请å
é¢è§æ ¸ç®æ°æ®'); |
| | | return; |
| | | } |
| | | submitLoading.value = true; |
| | | try { |
| | | await saveSalaryCalculation({ |
| | | period: calculatePeriod.value, |
| | | list: previewData.value, |
| | | }); |
| | | message.success('æ ¸ç®ä¿åæå'); |
| | | calculateVisible.value = false; |
| | | handleRefresh(); |
| | | } finally { |
| | | submitLoading.value = false; |
| | | } |
| | | } |
| | | |
| | | /** åæ¶å¼¹çª */ |
| | | function handleCancel() { |
| | | calculateVisible.value = false; |
| | | previewData.value = []; |
| | | } |
| | | |
| | | /** 确认èªé
¬æ ¸ç® */ |
| | | async function handleConfirm(row: HrmSalaryCalculationApi.SalaryCalculation) { |
| | | await confirmSalary([row.id!]); |
| | | message.success('确认æå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** æ¹é确认 */ |
| | | async function handleBatchConfirm() { |
| | | const records = gridApi.grid.getCheckboxRecords() as HrmSalaryCalculationApi.SalaryCalculation[]; |
| | | if (records.length === 0) { |
| | | message.warning('è¯·éæ©è¦ç¡®è®¤çè®°å½'); |
| | | return; |
| | | } |
| | | const ids = records.map((r) => r.id!); |
| | | await confirmSalary(ids); |
| | | message.success('确认æå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** 导åºèªé
¬æ ¸ç® */ |
| | | async function handleExport() { |
| | | const data = await exportSalaryCalculation(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'èªé
¬æ ¸ç®è¡¨.xls', source: data }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | checkboxConfig: { |
| | | highlight: true, |
| | | reserve: true, |
| | | }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getSalaryCalculationPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmSalaryCalculationApi.SalaryCalculation>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <!-- æ ¸ç®å¼¹çª --> |
| | | <Modal |
| | | v-model:open="calculateVisible" |
| | | title="èªé
¬æ ¸ç®" |
| | | width="1200px" |
| | | :footer="null" |
| | | :maskClosable="false" |
| | | > |
| | | <Alert |
| | | message="ä¸¤ç§æ ¸ç®æ¹å¼ï¼1. æ§è¡æ ¸ç® - èªå¨è®¡ç®å¹¶ä¿åï¼2. é¢è§æ°æ® - æå¨è°æ´åä¿å" |
| | | type="info" |
| | | show-icon |
| | | style="margin-bottom: 16px" |
| | | /> |
| | | |
| | | <Form layout="inline" class="mb-4"> |
| | | <FormItem label="æ ¸ç®å¨æ" required> |
| | | <DatePicker |
| | | v-model:value="calculatePeriod" |
| | | picker="month" |
| | | format="YYYY-MM" |
| | | valueFormat="YYYY-MM" |
| | | placeholder="è¯·éæ©æ ¸ç®å¨æ" |
| | | style="width: 180px" |
| | | /> |
| | | </FormItem> |
| | | <FormItem label="æ ¸ç®é¨é¨"> |
| | | <TreeSelect |
| | | v-model:value="calculateDeptId" |
| | | :tree-data="deptTreeData" |
| | | :field-names="{ label: 'name', value: 'id', children: 'children' }" |
| | | placeholder="å
¨é¨é¨é¨" |
| | | allow-clear |
| | | tree-default-expand-all |
| | | style="width: 200px" |
| | | /> |
| | | </FormItem> |
| | | <FormItem> |
| | | <a-button type="primary" :loading="calculateLoading" @click="handleCalculate"> |
| | | æ§è¡æ ¸ç® |
| | | </a-button> |
| | | </FormItem> |
| | | <FormItem> |
| | | <a-button :loading="previewLoading" @click="handlePreview"> |
| | | é¢è§æ°æ® |
| | | </a-button> |
| | | </FormItem> |
| | | </Form> |
| | | |
| | | <Table |
| | | v-if="previewData.length > 0" |
| | | :columns="usePreviewColumns()" |
| | | :data-source="previewData" |
| | | :pagination="false" |
| | | :scroll="{ x: 1800, y: 400 }" |
| | | size="small" |
| | | row-key="userId" |
| | | bordered |
| | | class="mb-4" |
| | | > |
| | | <template #bodyCell="{ column, record }"> |
| | | <template v-if="column.dataIndex === 'baseSalary'"> |
| | | <InputNumber |
| | | v-model:value="record.baseSalary" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 90px" |
| | | @change="(val: number) => updateFieldValue(record, 'baseSalary', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'performanceSalary'"> |
| | | <InputNumber |
| | | v-model:value="record.performanceSalary" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 90px" |
| | | @change="(val: number) => updateFieldValue(record, 'performanceSalary', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'overtimePay'"> |
| | | <InputNumber |
| | | v-model:value="record.overtimePay" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 90px" |
| | | @change="(val: number) => updateFieldValue(record, 'overtimePay', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'mealAllowance'"> |
| | | <InputNumber |
| | | v-model:value="record.mealAllowance" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 80px" |
| | | @change="(val: number) => updateFieldValue(record, 'mealAllowance', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'transportAllowance'"> |
| | | <InputNumber |
| | | v-model:value="record.transportAllowance" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 80px" |
| | | @change="(val: number) => updateFieldValue(record, 'transportAllowance', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'otherAllowance'"> |
| | | <InputNumber |
| | | v-model:value="record.otherAllowance" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 80px" |
| | | @change="(val: number) => updateFieldValue(record, 'otherAllowance', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'socialSecurityDeduction'"> |
| | | <InputNumber |
| | | v-model:value="record.socialSecurityDeduction" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 90px" |
| | | @change="(val: number) => updateFieldValue(record, 'socialSecurityDeduction', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'housingFundDeduction'"> |
| | | <InputNumber |
| | | v-model:value="record.housingFundDeduction" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 90px" |
| | | @change="(val: number) => updateFieldValue(record, 'housingFundDeduction', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'taxDeduction'"> |
| | | <InputNumber |
| | | v-model:value="record.taxDeduction" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 90px" |
| | | @change="(val: number) => updateFieldValue(record, 'taxDeduction', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'otherDeduction'"> |
| | | <InputNumber |
| | | v-model:value="record.otherDeduction" |
| | | :min="0" |
| | | :precision="2" |
| | | size="small" |
| | | style="width: 80px" |
| | | @change="(val: number) => updateFieldValue(record, 'otherDeduction', val)" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'actualSalary'"> |
| | | <span class="font-bold text-green-600">{{ record.actualSalary?.toFixed(2) }}</span> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'workDays'"> |
| | | <InputNumber |
| | | v-model:value="record.workDays" |
| | | :min="0" |
| | | :max="31" |
| | | :precision="0" |
| | | size="small" |
| | | style="width: 70px" |
| | | /> |
| | | </template> |
| | | <template v-else-if="column.dataIndex === 'overtimeHours'"> |
| | | <InputNumber |
| | | v-model:value="record.overtimeHours" |
| | | :min="0" |
| | | :precision="1" |
| | | size="small" |
| | | style="width: 80px" |
| | | /> |
| | | </template> |
| | | </template> |
| | | </Table> |
| | | |
| | | <div class="flex justify-end gap-2" v-if="previewData.length > 0"> |
| | | <a-button @click="handleCancel">åæ¶</a-button> |
| | | <a-button type="primary" :loading="submitLoading" @click="handleSubmit"> |
| | | ä¿åæ ¸ç® |
| | | </a-button> |
| | | </div> |
| | | </Modal> |
| | | |
| | | <Grid table-title="èªé
¬æ ¸ç®å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ ¸ç®', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:salary-calculation:calculate'], |
| | | onClick: openCalculateModal, |
| | | }, |
| | | { |
| | | label: 'æ¹é确认', |
| | | type: 'primary', |
| | | auth: ['hrm:salary-calculation:confirm'], |
| | | onClick: handleBatchConfirm, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['hrm:salary-calculation:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: '确认', |
| | | type: 'link', |
| | | auth: ['hrm:salary-calculation:confirm'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleConfirm.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeSocialSecurityApi } from '#/api/hrm/salary/employee-social-security'; |
| | | |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { getEmployeeSimpleList } from '#/api/hrm/employee'; |
| | | import { getSocialSecuritySchemeList } from '#/api/hrm/salary/social-security-scheme'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'update'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹å工社ä¿å
¬ç§¯éæ¡£æ¡ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'userId', |
| | | label: 'åå·¥', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getEmployeeSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | showSearch: true, |
| | | filterOption: (input: string, option: any) => |
| | | option.label?.toLowerCase().includes(input.toLowerCase()), |
| | | placeholder: 'è¯·éæ©åå·¥', |
| | | }, |
| | | rules: 'selectRequired', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'socialSecuritySchemeId', |
| | | label: 'ç¤¾ä¿æ¹æ¡', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const list = await getSocialSecuritySchemeList({ status: 0 }); |
| | | return list; |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©ç¤¾ä¿å
¬ç§¯éæ¹æ¡', |
| | | }, |
| | | rules: 'selectRequired', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | // ç¼´çº³åºæ° |
| | | { |
| | | fieldName: 'socialSecurityBase', |
| | | label: '社ä¿åºæ°', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç¤¾ä¿åºæ°', |
| | | min: 0, |
| | | precision: 2, |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'housingFundBase', |
| | | label: 'å
¬ç§¯éåºæ°', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å
¬ç§¯éåºæ°', |
| | | min: 0, |
| | | precision: 2, |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | // çæä¿¡æ¯ |
| | | { |
| | | fieldName: 'effectiveDate', |
| | | label: 'çææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©çææ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å¯ç¨', value: 0 }, |
| | | { label: 'ç¦ç¨', value: 1 }, |
| | | ], |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'userName', |
| | | label: 'åå·¥å§å', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥åå·¥å§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'deptId', |
| | | label: 'é¨é¨', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const data = await getDeptList(); |
| | | return handleTree(data); |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | childrenField: 'children', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©é¨é¨', |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity>['columns'] { |
| | | return [ |
| | | { field: 'userName', title: 'åå·¥å§å', minWidth: 100 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { field: 'schemeName', title: 'ç¤¾ä¿æ¹æ¡', minWidth: 180 }, |
| | | { field: 'socialSecurityBase', title: '社ä¿åºæ°', width: 100, formatter: 'formatAmount2' }, |
| | | { field: 'housingFundBase', title: 'å
¬ç§¯éåºæ°', width: 100, formatter: 'formatAmount2' }, |
| | | { field: 'effectiveDate', title: 'çææ¥æ', width: 110 }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 80, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 200, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeSocialSecurityApi } from '#/api/hrm/salary/employee-social-security'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteEmployeeSocialSecurity, |
| | | getEmployeeSocialSecurityPage, |
| | | } from '#/api/hrm/salary/employee-social-security'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | import History from './modules/history.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [HistoryModal, historyModalApi] = useVbenModal({ |
| | | connectedComponent: History, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ°å¢æ¡£æ¡ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾æ¡£æ¡ */ |
| | | function handleEdit(row: HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** æ¥çåå² */ |
| | | function handleHistory(row: HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity) { |
| | | historyModalApi.setData({ userId: row.userId, userName: row.userName }).open(); |
| | | } |
| | | |
| | | /** å 餿¡£æ¡ */ |
| | | async function handleDelete(row: HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity) { |
| | | await deleteEmployeeSocialSecurity(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', ['å工社ä¿å
¬ç§¯éæ¡£æ¡'])); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getEmployeeSocialSecurityPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <HistoryModal /> |
| | | <Grid table-title="å工社ä¿å
¬ç§¯éæ¡£æ¡å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['å工社ä¿å
¬ç§¯éæ¡£æ¡']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:employee-social-security:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag :color="row.status === 0 ? 'success' : 'default'"> |
| | | {{ row.status === 0 ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:employee-social-security:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'åå²', |
| | | type: 'link', |
| | | onClick: handleHistory.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:employee-social-security:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', ['å工社ä¿å
¬ç§¯éæ¡£æ¡']), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { HrmEmployeeSocialSecurityApi } from '#/api/hrm/salary/employee-social-security'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createEmployeeSocialSecurity, |
| | | getEmployeeSocialSecurity, |
| | | updateEmployeeSocialSecurity, |
| | | } from '#/api/hrm/salary/employee-social-security'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const getTitle = computed(() => |
| | | formType.value === 'update' ? 'ä¿®æ¹å工社ä¿å
¬ç§¯éæ¡£æ¡' : 'æ°å¢å工社ä¿å
¬ç§¯éæ¡£æ¡', |
| | | ); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = |
| | | (await formApi.getValues()) as HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity; |
| | | try { |
| | | if (data.id) { |
| | | await updateEmployeeSocialSecurity(data); |
| | | } else { |
| | | await createEmployeeSocialSecurity(data); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | if (!data?.id) { |
| | | await formApi.resetForm(); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await formApi.setValues(await getEmployeeSocialSecurity(data.id)); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[700px]"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmEmployeeSocialSecurityApi } from '#/api/hrm/salary/employee-social-security'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { Table, Tag } from 'ant-design-vue'; |
| | | |
| | | import { getEmployeeSocialSecurityListByUser } from '#/api/hrm/salary/employee-social-security'; |
| | | |
| | | const historyList = ref<HrmEmployeeSocialSecurityApi.EmployeeSocialSecurity[]>([]); |
| | | const userName = ref<string>(''); |
| | | |
| | | const columns = [ |
| | | { title: 'ç¤¾ä¿æ¹æ¡', dataIndex: 'schemeName', width: 180 }, |
| | | { title: '社ä¿åºæ°', dataIndex: 'socialSecurityBase', width: 100 }, |
| | | { title: 'å
¬ç§¯éåºæ°', dataIndex: 'housingFundBase', width: 100 }, |
| | | { title: 'çææ¥æ', dataIndex: 'effectiveDate', width: 110 }, |
| | | { |
| | | title: 'ç¶æ', |
| | | dataIndex: 'status', |
| | | width: 80, |
| | | key: 'status', |
| | | }, |
| | | ]; |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | historyList.value = []; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ userId: number; userName: string }>(); |
| | | userName.value = data.userName; |
| | | modalApi.lock(); |
| | | try { |
| | | historyList.value = await getEmployeeSocialSecurityListByUser(data.userId); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="`${userName} - 社ä¿å
¬ç§¯éåå²è®°å½`" class="w-[600px]" :footer="null"> |
| | | <Table |
| | | :columns="columns" |
| | | :data-source="historyList" |
| | | :pagination="false" |
| | | size="small" |
| | | row-key="id" |
| | | > |
| | | <template #bodyCell="{ column, record }"> |
| | | <template v-if="column.key === 'status'"> |
| | | <Tag :color="record.status === 0 ? 'success' : 'default'"> |
| | | {{ record.status === 0 ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </Tag> |
| | | </template> |
| | | </template> |
| | | </Table> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmSalaryPaymentApi } from '#/api/hrm/salary/payment'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { getEmployeeSimpleList } from '#/api/hrm/employee'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'period', |
| | | label: '忾卿', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | picker: 'month', |
| | | allowClear: true, |
| | | format: 'YYYY-MM', |
| | | valueFormat: 'YYYY-MM', |
| | | style: { width: '100%' }, |
| | | placeholder: 'è¯·éæ©åæ¾å¨æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'userId', |
| | | label: 'åå·¥', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getEmployeeSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©åå·¥', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'deptId', |
| | | label: 'é¨é¨', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const data = await getDeptList(); |
| | | return handleTree(data); |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | childrenField: 'children', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©é¨é¨', |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'åæ¾ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'å¾
åæ¾', value: 10 }, |
| | | { label: '已忾', value: 20 }, |
| | | { label: 'å·²æ¤é', value: 30 }, |
| | | ], |
| | | placeholder: 'è¯·éæ©åæ¾ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmSalaryPaymentApi.SalaryPayment>['columns'] { |
| | | return [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { field: 'no', title: 'åæ¾åå·', minWidth: 140 }, |
| | | { field: 'period', title: '忾卿', width: 100 }, |
| | | { field: 'userName', title: 'åå·¥å§å', minWidth: 100 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { field: 'actualSalary', title: 'å®åå·¥èµ', width: 100, formatter: 'formatAmount2' }, |
| | | { |
| | | field: 'paymentMethod', |
| | | title: 'åæ¾æ¹å¼', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_PAYMENT_METHOD }, |
| | | }, |
| | | }, |
| | | { field: 'paymentTime', title: 'åæ¾æ¶é´', width: 160 }, |
| | | { field: 'paymentNo', title: 'åæ¾æµæ°´å·', minWidth: 140 }, |
| | | { |
| | | field: 'status', |
| | | title: 'åæ¾ç¶æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_SALARY_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 150, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmSalaryPaymentApi } from '#/api/hrm/salary/payment'; |
| | | |
| | | import { Page } from '#/packages/effects/common-ui/src'; |
| | | import { downloadFileFromBlobPart } from '#/packages/utils/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | exportSalaryPayment, |
| | | getSalaryPaymentPage, |
| | | paySalary, |
| | | revokeSalaryPayment, |
| | | } from '#/api/hrm/salary/payment'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ§è¡èªèµåæ¾ */ |
| | | async function handlePay() { |
| | | const records = gridApi.grid.getCheckboxRecords() as HrmSalaryPaymentApi.SalaryPayment[]; |
| | | if (records.length === 0) { |
| | | message.warning('è¯·éæ©è¦åæ¾çè®°å½'); |
| | | return; |
| | | } |
| | | const unpaidRecords = records.filter((r) => r.status === 10); |
| | | if (unpaidRecords.length === 0) { |
| | | message.warning('æéè®°å½ä¸æ²¡æå¾
åæ¾ç¶æçè®°å½'); |
| | | return; |
| | | } |
| | | const ids = unpaidRecords.map((r) => r.id!); |
| | | await paySalary(ids); |
| | | message.success('åæ¾æå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** æ¤éèªèµåæ¾ */ |
| | | async function handleRevoke(row: HrmSalaryPaymentApi.SalaryPayment) { |
| | | await revokeSalaryPayment(row.id!); |
| | | message.success('æ¤éæå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** 导åºèªèµåæ¾ */ |
| | | async function handleExport() { |
| | | const data = await exportSalaryPayment(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'èªèµåæ¾è¡¨.xls', source: data }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | checkboxConfig: { |
| | | highlight: true, |
| | | reserve: true, |
| | | }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getSalaryPaymentPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmSalaryPaymentApi.SalaryPayment>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <Grid table-title="èªèµåæ¾å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'åæ¾', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:salary-payment:pay'], |
| | | onClick: handlePay, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['hrm:salary-payment:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ¤é', |
| | | type: 'link', |
| | | auth: ['hrm:salary-payment:revoke'], |
| | | ifShow: row.status === 20, |
| | | popConfirm: { |
| | | title: '确认æ¤éè¯¥åæ¾è®°å½åï¼', |
| | | confirm: handleRevoke.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmSocialSecuritySchemeApi } from '#/api/hrm/salary/social-security-scheme'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'update'; |
| | | |
| | | /** æ ¼å¼åæ¯ä¾è¾å
¥ */ |
| | | function createRatioFormatter(step: number = 0.01) { |
| | | return { |
| | | formatter: (value: number) => value !== undefined && value !== null ? `${(value * 100).toFixed(step === 0.001 ? 1 : 0)}%` : '', |
| | | parser: (value: string) => { |
| | | const num = parseFloat(value.replace('%', '')); |
| | | return isNaN(num) ? 0 : num / 100; |
| | | }, |
| | | }; |
| | | } |
| | | |
| | | /** æ°å¢/ä¿®æ¹ç¤¾ä¿å
¬ç§¯éæ¹æ¡ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¹æ¡åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¹æ¡åç§°ï¼å¦ï¼2024å¹´åé叿 å', |
| | | }, |
| | | rules: 'required', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'effectiveDate', |
| | | label: 'çææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©çææ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å¯ç¨', value: 0 }, |
| | | { label: 'ç¦ç¨', value: 1 }, |
| | | ], |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | // å
»èä¿é© |
| | | { |
| | | fieldName: 'divider_pension', |
| | | label: 'å
»èä¿é©', |
| | | component: 'Divider', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'pensionCompanyRatio', |
| | | label: 'ä¼ä¸æ¯ä¾', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: 'ä¼ä¸ç¼´çº³æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.01, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'pensionPersonalRatio', |
| | | label: '个人æ¯ä¾', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '个人缴纳æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.01, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | // å»çä¿é© |
| | | { |
| | | fieldName: 'divider_medical', |
| | | label: 'å»çä¿é©', |
| | | component: 'Divider', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'medicalCompanyRatio', |
| | | label: 'ä¼ä¸æ¯ä¾', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: 'ä¼ä¸ç¼´çº³æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.01, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'medicalPersonalRatio', |
| | | label: '个人æ¯ä¾', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '个人缴纳æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.01, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | // 失ä¸ä¿é© |
| | | { |
| | | fieldName: 'divider_unemployment', |
| | | label: '失ä¸ä¿é©', |
| | | component: 'Divider', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'unemploymentCompanyRatio', |
| | | label: 'ä¼ä¸æ¯ä¾', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: 'ä¼ä¸ç¼´çº³æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.001, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(0.001), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'unemploymentPersonalRatio', |
| | | label: '个人æ¯ä¾', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '个人缴纳æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.001, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(0.001), |
| | | }, |
| | | }, |
| | | // 工伤/çè²ä¿é© |
| | | { |
| | | fieldName: 'divider_other', |
| | | label: '工伤/çè²ä¿é©', |
| | | component: 'Divider', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'workInjuryRatio', |
| | | label: '工伤ä¿é©', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '工伤ä¿é©æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.001, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(0.001), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'maternityRatio', |
| | | label: 'çè²ä¿é©', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: 'çè²ä¿é©æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.001, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(0.001), |
| | | }, |
| | | }, |
| | | // å
¬ç§¯é |
| | | { |
| | | fieldName: 'divider_housing', |
| | | label: 'å
¬ç§¯é', |
| | | component: 'Divider', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'housingFundCompanyRatio', |
| | | label: 'ä¼ä¸æ¯ä¾', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: 'ä¼ä¸ç¼´çº³æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.01, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'housingFundPersonalRatio', |
| | | label: '个人æ¯ä¾', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '个人缴纳æ¯ä¾', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.01, |
| | | precision: 4, |
| | | style: { width: '100%' }, |
| | | ...createRatioFormatter(), |
| | | }, |
| | | }, |
| | | // 夿³¨ |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¹æ¡åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥æ¹æ¡åç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'å¯ç¨', value: 0 }, |
| | | { label: 'ç¦ç¨', value: 1 }, |
| | | ], |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmSocialSecuritySchemeApi.SocialSecurityScheme>['columns'] { |
| | | return [ |
| | | { field: 'name', title: 'æ¹æ¡åç§°', minWidth: 180 }, |
| | | { field: 'effectiveDate', title: 'çææ¥æ', width: 110 }, |
| | | { |
| | | field: 'pensionCompanyRatio', |
| | | title: 'å
»è(ä¼ä¸)', |
| | | width: 100, |
| | | slots: { default: 'pensionCompanyRatio' }, |
| | | }, |
| | | { |
| | | field: 'pensionPersonalRatio', |
| | | title: 'å
»è(个人)', |
| | | width: 100, |
| | | slots: { default: 'pensionPersonalRatio' }, |
| | | }, |
| | | { |
| | | field: 'medicalPersonalRatio', |
| | | title: 'å»ç(个人)', |
| | | width: 100, |
| | | slots: { default: 'medicalPersonalRatio' }, |
| | | }, |
| | | { |
| | | field: 'housingFundPersonalRatio', |
| | | title: 'å
¬ç§¯é(个人)', |
| | | width: 100, |
| | | slots: { default: 'housingFundPersonalRatio' }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 80, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 150, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmSocialSecuritySchemeApi } from '#/api/hrm/salary/social-security-scheme'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteSocialSecurityScheme, |
| | | getSocialSecuritySchemeList, |
| | | } from '#/api/hrm/salary/social-security-scheme'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ°å¢æ¹æ¡ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾æ¹æ¡ */ |
| | | function handleEdit(row: HrmSocialSecuritySchemeApi.SocialSecurityScheme) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å 餿¹æ¡ */ |
| | | async function handleDelete(row: HrmSocialSecuritySchemeApi.SocialSecurityScheme) { |
| | | await deleteSocialSecurityScheme(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', ['社ä¿å
¬ç§¯éæ¹æ¡'])); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** æ ¼å¼åæ¯ä¾æ¾ç¤º */ |
| | | function formatRatio(ratio: number | undefined) { |
| | | if (ratio === undefined || ratio === null) return '-'; |
| | | return `${(ratio * 100).toFixed(1)}%`; |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async (_params, formValues) => |
| | | await getSocialSecuritySchemeList(formValues), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | pagerConfig: { |
| | | enabled: false, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmSocialSecuritySchemeApi.SocialSecurityScheme>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="社ä¿å
¬ç§¯éæ¹æ¡å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['社ä¿å
¬ç§¯éæ¹æ¡']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:social-security-scheme:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #pensionCompanyRatio="{ row }"> |
| | | {{ formatRatio(row.pensionCompanyRatio) }} |
| | | </template> |
| | | <template #pensionPersonalRatio="{ row }"> |
| | | {{ formatRatio(row.pensionPersonalRatio) }} |
| | | </template> |
| | | <template #medicalPersonalRatio="{ row }"> |
| | | {{ formatRatio(row.medicalPersonalRatio) }} |
| | | </template> |
| | | <template #housingFundPersonalRatio="{ row }"> |
| | | {{ formatRatio(row.housingFundPersonalRatio) }} |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag :color="row.status === 0 ? 'success' : 'default'"> |
| | | {{ row.status === 0 ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:social-security-scheme:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:social-security-scheme:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', ['社ä¿å
¬ç§¯éæ¹æ¡']), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { HrmSocialSecuritySchemeApi } from '#/api/hrm/salary/social-security-scheme'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createSocialSecurityScheme, |
| | | getSocialSecurityScheme, |
| | | updateSocialSecurityScheme, |
| | | } from '#/api/hrm/salary/social-security-scheme'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const getTitle = computed(() => |
| | | formType.value === 'update' ? 'ä¿®æ¹ç¤¾ä¿å
¬ç§¯éæ¹æ¡' : 'æ°å¢ç¤¾ä¿å
¬ç§¯éæ¹æ¡', |
| | | ); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = |
| | | (await formApi.getValues()) as HrmSocialSecuritySchemeApi.SocialSecurityScheme; |
| | | try { |
| | | if (data.id) { |
| | | await updateSocialSecurityScheme(data); |
| | | } else { |
| | | await createSocialSecurityScheme(data); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | if (!data?.id) { |
| | | await formApi.resetForm(); |
| | | // 设置é»è®¤å¼ |
| | | await formApi.setValues({ |
| | | pensionCompanyRatio: 0.16, |
| | | pensionPersonalRatio: 0.08, |
| | | medicalCompanyRatio: 0.095, |
| | | medicalPersonalRatio: 0.02, |
| | | unemploymentCompanyRatio: 0.005, |
| | | unemploymentPersonalRatio: 0.005, |
| | | workInjuryRatio: 0.005, |
| | | maternityRatio: 0.005, |
| | | housingFundCompanyRatio: 0.12, |
| | | housingFundPersonalRatio: 0.12, |
| | | status: 0, |
| | | }); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await formApi.setValues(await getSocialSecurityScheme(data.id)); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[700px]"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmTaxRateConfigApi } from '#/api/hrm/salary/tax-rate-config'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'update'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ä¸ªç¨ç¨çé
ç½®ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'level', |
| | | label: 'ç¨ç级æ°', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç¨ç级æ°', |
| | | min: 1, |
| | | max: 7, |
| | | precision: 0, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'minAmount', |
| | | label: 'åºçº³ç¨æå¾é¢ä¸é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ä¸éï¼å«ï¼', |
| | | min: 0, |
| | | precision: 2, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'maxAmount', |
| | | label: 'åºçº³ç¨æå¾é¢ä¸é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ä¸éï¼ä¸å«ï¼ï¼0表示æ ä¸é', |
| | | min: 0, |
| | | precision: 2, |
| | | }, |
| | | help: 'ä¸éä¸å«ï¼0表示æ ä¸é', |
| | | }, |
| | | { |
| | | fieldName: 'taxRate', |
| | | label: 'ç¨ç', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç¨ç', |
| | | min: 0, |
| | | max: 1, |
| | | step: 0.01, |
| | | precision: 4, |
| | | formatter: (value: number) => `${(value * 100).toFixed(0)}%`, |
| | | parser: (value: string) => parseFloat(value.replace('%', '')) / 100, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'quickDeduction', |
| | | label: 'éç®æ£é¤æ°', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥éç®æ£é¤æ°', |
| | | min: 0, |
| | | precision: 2, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmTaxRateConfigApi.TaxRateConfig>['columns'] { |
| | | return [ |
| | | { field: 'level', title: '级æ°', width: 80 }, |
| | | { |
| | | field: 'amountRange', |
| | | title: 'åºçº³ç¨æå¾é¢', |
| | | minWidth: 200, |
| | | slots: { default: 'amountRange' }, |
| | | }, |
| | | { |
| | | field: 'taxRate', |
| | | title: 'ç¨ç', |
| | | width: 100, |
| | | slots: { default: 'taxRate' }, |
| | | }, |
| | | { field: 'quickDeduction', title: 'éç®æ£é¤æ°', width: 120, formatter: 'formatAmount2' }, |
| | | { field: 'effectiveDate', title: 'çææ¥æ', width: 110 }, |
| | | { |
| | | title: 'æä½', |
| | | width: 150, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmTaxRateConfigApi } from '#/api/hrm/salary/tax-rate-config'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteTaxRateConfig, |
| | | getTaxRateConfigList, |
| | | } from '#/api/hrm/salary/tax-rate-config'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ°å¢é
ç½® */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾é
ç½® */ |
| | | function handleEdit(row: HrmTaxRateConfigApi.TaxRateConfig) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å é¤é
ç½® */ |
| | | async function handleDelete(row: HrmTaxRateConfigApi.TaxRateConfig) { |
| | | await deleteTaxRateConfig(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', ['个ç¨ç¨çé
ç½®'])); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async () => await getTaxRateConfigList(), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | pagerConfig: { |
| | | enabled: false, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmTaxRateConfigApi.TaxRateConfig>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="个ç¨ç¨çé
ç½®å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['个ç¨ç¨çé
ç½®']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:tax-rate-config:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #amountRange="{ row }"> |
| | | {{ row.minAmount }} ~ {{ row.maxAmount === 0 ? 'â' : row.maxAmount }} |
| | | </template> |
| | | <template #taxRate="{ row }"> |
| | | {{ (row.taxRate * 100).toFixed(0) }}% |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:tax-rate-config:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:tax-rate-config:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', ['个ç¨ç¨çé
ç½®']), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { HrmTaxRateConfigApi } from '#/api/hrm/salary/tax-rate-config'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createTaxRateConfigList, |
| | | getTaxRateConfig, |
| | | updateTaxRateConfig, |
| | | } from '#/api/hrm/salary/tax-rate-config'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const getTitle = computed(() => |
| | | formType.value === 'update' ? 'ä¿®æ¹ä¸ªç¨ç¨çé
ç½®' : 'æ°å¢ä¸ªç¨ç¨çé
ç½®', |
| | | ); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 150, |
| | | }, |
| | | wrapperClass: 'grid-cols-1', |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as HrmTaxRateConfigApi.TaxRateConfig; |
| | | try { |
| | | if (data.id) { |
| | | await updateTaxRateConfig(data); |
| | | } else { |
| | | await createTaxRateConfigList([data]); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | if (!data?.id) { |
| | | await formApi.resetForm(); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await formApi.setValues(await getTaxRateConfig(data.id)); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[600px]"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmTransferApi } from '#/api/hrm/transfer'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { getSimplePostList } from '#/api/system/post'; |
| | | import { handleTree } from '#/packages/utils/src'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹è°å²ç³è¯·ç表å */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'currentDeptId', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'currentPostId', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'currentDeptName', |
| | | label: 'åé¨é¨', |
| | | component: 'Input', |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: 'èªå¨è·å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'currentPostName', |
| | | label: 'åå²ä½', |
| | | component: 'Input', |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: 'èªå¨è·å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'targetDeptId', |
| | | label: 'ç®æ é¨é¨', |
| | | component: 'ApiTreeSelect', |
| | | componentProps: { |
| | | api: async () => { |
| | | const data = await getDeptList(); |
| | | return handleTree(data); |
| | | }, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | childrenField: 'children', |
| | | placeholder: 'è¯·éæ©ç®æ é¨é¨', |
| | | treeDefaultExpandAll: true, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'targetPostId', |
| | | label: 'ç®æ å²ä½', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getSimplePostList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©ç®æ å²ä½', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'transferType', |
| | | label: 'è°å²ç±»å', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | buttonStyle: 'solid', |
| | | optionType: 'button', |
| | | options: getDictOptions(DICT_TYPE.HRM_TRANSFER_TYPE, 'number'), |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'effectiveDate', |
| | | label: 'çææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©çææ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'reason', |
| | | label: 'è°å²åå ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è°å²åå ', |
| | | rows: 3, |
| | | maxlength: 500, |
| | | showCount: true, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'fileUrl', |
| | | label: 'éä»¶', |
| | | component: 'Input', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请ä¸ä¼ éä»¶', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'transferType', |
| | | label: 'è°å²ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_TRANSFER_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©è°å²ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '审æ¹ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_AUDIT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å®¡æ¹ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmTransferApi.Transfer>['columns'] { |
| | | return [ |
| | | { field: 'id', title: 'ç³è¯·ç¼å·', width: 80 }, |
| | | { field: 'employeeName', title: 'ç³è¯·äºº', minWidth: 100 }, |
| | | { field: 'currentDeptName', title: 'åé¨é¨', minWidth: 120 }, |
| | | { field: 'currentPostName', title: 'åå²ä½', minWidth: 120 }, |
| | | { field: 'targetDeptName', title: 'ç®æ é¨é¨', minWidth: 120 }, |
| | | { field: 'targetPostName', title: 'ç®æ å²ä½', minWidth: 120 }, |
| | | { |
| | | field: 'transferType', |
| | | title: 'è°å²ç±»å', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_TRANSFER_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'effectiveDate', title: 'çææ¥æ', width: 110 }, |
| | | { |
| | | field: 'status', |
| | | title: '审æ¹ç¶æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_AUDIT_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 200, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmTransferApi } from '#/api/hrm/transfer'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | cancelTransfer, |
| | | deleteTransfer, |
| | | getTransferPage, |
| | | submitTransfer, |
| | | } from '#/api/hrm/transfer'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** å建è°å²ç³è¯· */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥çè°å²ç³è¯· */ |
| | | function handleDetail(row: HrmTransferApi.Transfer) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾è°å²ç³è¯· */ |
| | | function handleEdit(row: HrmTransferApi.Transfer) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å é¤è°å²ç³è¯· */ |
| | | async function handleDelete(row: HrmTransferApi.Transfer) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', ['è°å²ç³è¯·']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteTransfer(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', ['è°å²ç³è¯·'])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æäº¤è°å²ç³è¯· */ |
| | | async function handleSubmit(row: HrmTransferApi.Transfer) { |
| | | await submitTransfer(row.id!); |
| | | message.success('æäº¤æå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | /** æ¤éè°å²ç³è¯· */ |
| | | async function handleCancel(row: HrmTransferApi.Transfer) { |
| | | await cancelTransfer(row.id!); |
| | | message.success('æ¤éæå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getTransferPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmTransferApi.Transfer>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid table-title="è°å²ç³è¯·å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['è°å²ç³è¯·']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:transfer:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ¥ç', |
| | | type: 'link', |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:transfer:update'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤', |
| | | type: 'link', |
| | | auth: ['hrm:transfer:submit'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¤é', |
| | | type: 'link', |
| | | auth: ['hrm:transfer:cancel'], |
| | | ifShow: row.status === 10, |
| | | onClick: handleCancel.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:transfer:delete'], |
| | | ifShow: row.status === 0 || row.status === 40, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', ['è°å²ç³è¯·']), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | import type { HrmTransferApi } from '#/api/hrm/transfer'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createTransfer, getTransfer, updateTransfer } from '#/api/hrm/transfer'; |
| | | import { getEmployeeSimpleList } from '#/api/hrm/employee'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | const currentEmployeeId = ref<number>(); |
| | | |
| | | const isDetail = computed(() => formType.value === 'detail'); |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return 'æ¥çè°å²ç³è¯·'; |
| | | } |
| | | return formType.value === 'update' ? 'ä¿®æ¹è°å²ç³è¯·' : 'æ°å¢è°å²ç³è¯·'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 100, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as HrmTransferApi.Transfer; |
| | | data.employeeId = currentEmployeeId.value; |
| | | try { |
| | | if (data.id) { |
| | | await updateTransfer(data); |
| | | } else { |
| | | await createTransfer(data); |
| | | } |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setState({ schema: useFormSchema(data.formType) }); |
| | | formApi.setDisabled(data.formType === 'detail'); |
| | | modalApi.setState({ showConfirmButton: data.formType !== 'detail' }); |
| | | if (!data?.id) { |
| | | // æ°å¢æ¶ï¼èªå¨è·åå½åç»å½åå·¥çé¨é¨åå²ä½ä¿¡æ¯ |
| | | const employees = await getEmployeeSimpleList(); |
| | | // TODO: è¿éåºè¯¥æ ¹æ®ç»å½ç¨æ·è·å对åºçå工信æ¯ï¼ææ¶å第ä¸ä¸ªåå·¥ä½ä¸ºç¤ºä¾ |
| | | const currentEmployee = employees[0]; |
| | | if (currentEmployee) { |
| | | currentEmployeeId.value = currentEmployee.id; |
| | | await formApi.setValues({ |
| | | currentDeptName: currentEmployee.deptName, |
| | | currentPostName: currentEmployee.postName, |
| | | }); |
| | | } |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const transfer = await getTransfer(data.id); |
| | | currentEmployeeId.value = transfer.employeeId; |
| | | await formApi.setValues(transfer); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[600px]"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |