| | |
| | | employeeStatus?: number; |
| | | annualLeaveBalance?: number; |
| | | sickLeaveBalance?: number; |
| | | salaryStructureId?: number; |
| | | baseSalary?: number; |
| | | roleIds?: number[]; |
| | | roleNames?: string[]; |
| | |
| | | /** å é¤ç´§æ¥è系人 */ |
| | | 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 HrmOvertimeApi { |
| | | /** å çç³è¯· */ |
| | | export interface Overtime { |
| | | id?: number; |
| | | employeeId?: number; |
| | | employeeName?: string; |
| | | employeeNo?: string; |
| | | deptName?: string; |
| | | postName?: string; |
| | | overtimeDate?: string; |
| | | startTime?: string; |
| | | endTime?: string; |
| | | duration?: number; |
| | | overtimeType?: number; |
| | | crossDay?: boolean; |
| | | mealDeduction?: boolean; |
| | | mealDeductionHours?: number; |
| | | reason?: string; |
| | | relatedType?: number; |
| | | relatedCode?: string; |
| | | workContent?: string; |
| | | status?: number; |
| | | processInstanceId?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | export interface ApproveProcess { |
| | | key: string; |
| | | name: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢å çç³è¯·å页å表 */ |
| | | export function getOvertimePage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmOvertimeApi.Overtime>>( |
| | | '/hrm/overtime/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢å çç³è¯·è¯¦æ
*/ |
| | | export function getOvertime(id: number) { |
| | | return requestClient.get<HrmOvertimeApi.Overtime>( |
| | | `/hrm/overtime/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åå½åç³è¯·äººå
³èçåå·¥æ¡£æ¡ */ |
| | | export function getCurrentOvertimeApplicant() { |
| | | return requestClient.get<HrmOvertimeApi.Overtime>('/hrm/overtime/current-applicant'); |
| | | } |
| | | |
| | | /** æ°å¢å çç³è¯·ï¼èç¨¿ï¼ */ |
| | | export function createOvertime(data: HrmOvertimeApi.Overtime) { |
| | | return requestClient.post('/hrm/overtime/create', data); |
| | | } |
| | | |
| | | /** æ´æ°å çç³è¯· */ |
| | | export function updateOvertime(data: HrmOvertimeApi.Overtime) { |
| | | return requestClient.put('/hrm/overtime/update', data); |
| | | } |
| | | |
| | | /** å é¤å çç³è¯· */ |
| | | export function deleteOvertime(id: number) { |
| | | return requestClient.delete(`/hrm/overtime/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æäº¤å çç³è¯·ï¼å起审æ¹ï¼ */ |
| | | export function submitOvertime(id: number, processDefinitionKey: string) { |
| | | return requestClient.post('/hrm/overtime/submit', null, { |
| | | params: { id, processDefinitionKey }, |
| | | }); |
| | | } |
| | | |
| | | /** æ¤éå çç³è¯· */ |
| | | export function cancelOvertime(id: number) { |
| | | return requestClient.post(`/hrm/overtime/cancel?id=${id}`); |
| | | } |
| | | |
| | | /** è·ååååå
¬ä¸çå¯ç¨å çå®¡æ¹æµç¨ */ |
| | | export function getOvertimeApproveProcessDefinitionList() { |
| | | return requestClient.get<HrmOvertimeApi.ApproveProcess[]>( |
| | | '/hrm/overtime/approve-process-definition-list', |
| | | ); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmSalaryStructureApi { |
| | | export interface SalaryStructure { |
| | | id?: number; |
| | | name?: string; |
| | | overtimePayRatio?: number; |
| | | restDayOvertimePayRatio?: number; |
| | | legalHolidayOvertimePayRatio?: number; |
| | | status?: number; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | export interface SalaryStructureQueryParam { |
| | | name?: string; |
| | | status?: number; |
| | | } |
| | | } |
| | | |
| | | export function getSalaryStructureList( |
| | | params?: HrmSalaryStructureApi.SalaryStructureQueryParam, |
| | | ) { |
| | | return requestClient.get<HrmSalaryStructureApi.SalaryStructure[]>( |
| | | '/hrm/salary/structure/list', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | export function getSalaryStructure(id: number) { |
| | | return requestClient.get<HrmSalaryStructureApi.SalaryStructure>( |
| | | `/hrm/salary/structure/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | export function createSalaryStructure( |
| | | data: HrmSalaryStructureApi.SalaryStructure, |
| | | ) { |
| | | return requestClient.post('/hrm/salary/structure/create', data); |
| | | } |
| | | |
| | | export function updateSalaryStructure( |
| | | data: HrmSalaryStructureApi.SalaryStructure, |
| | | ) { |
| | | return requestClient.put('/hrm/salary/structure/update', data); |
| | | } |
| | | |
| | | export function deleteSalaryStructure(id: number) { |
| | | return requestClient.delete(`/hrm/salary/structure/delete?id=${id}`); |
| | | } |
| | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | | import { getSalaryStructureList } from '#/api/hrm/salary/structure'; |
| | | import { getDeptList } from '#/api/system/dept'; |
| | | import { getSimplePostList } from '#/api/system/post'; |
| | | import { getSimpleRoleList } from '#/api/system/role'; |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'salaryStructureId', |
| | | label: 'èªé
Ⱦ', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: () => getSalaryStructureList({ status: 0 }), |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©èªé
Ⱦ', |
| | | }, |
| | | help: 'å³å®å·¥ä½æ¥å çåçï¼æªéæ©æ¶ä½¿ç¨ç³»ç»é»è®¤ 1.5 å', |
| | | }, |
| | | { |
| | | fieldName: 'baseSalary', |
| | | label: 'åºæ¬å·¥èµ', |
| | | component: 'InputNumber', |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmOvertimeApi } from '#/api/hrm/overtime'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | export const OVERTIME_TYPE_OPTIONS = [ |
| | | { label: '工使¥å ç', value: 1 }, |
| | | { label: '伿¯æ¥å ç', value: 2 }, |
| | | { label: 'æ³å®è忥å ç', value: 3 }, |
| | | ]; |
| | | |
| | | export const RELATED_TYPE_OPTIONS = [ |
| | | { label: '项ç®', value: 1 }, |
| | | { label: 'å·¥å', value: 2 }, |
| | | ]; |
| | | |
| | | export function getOvertimeTypeLabel(type?: number) { |
| | | return OVERTIME_TYPE_OPTIONS.find((item) => item.value === type)?.label || '-'; |
| | | } |
| | | |
| | | /** æ°å¢/ä¿®æ¹å çç³è¯·ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'employeeId', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'baseInfoDivider', |
| | | label: 'ç³è¯·äººåºç¡ä¿¡æ¯', |
| | | component: 'Divider', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'employeeName', |
| | | label: 'ç³è¯·äºº', |
| | | component: 'Input', |
| | | componentProps: { disabled: true, placeholder: 'ç³»ç»èªå¨å¸¦åº' }, |
| | | }, |
| | | { |
| | | fieldName: 'employeeNo', |
| | | label: 'å·¥å·', |
| | | component: 'Input', |
| | | componentProps: { disabled: true, placeholder: 'ç³»ç»èªå¨å¸¦åº' }, |
| | | }, |
| | | { |
| | | fieldName: 'deptName', |
| | | label: 'æå±é¨é¨', |
| | | component: 'Input', |
| | | componentProps: { disabled: true, placeholder: 'ç³»ç»èªå¨å¸¦åº' }, |
| | | }, |
| | | { |
| | | fieldName: 'postName', |
| | | label: 'å²ä½/è级', |
| | | component: 'Input', |
| | | componentProps: { disabled: true, placeholder: 'ç³»ç»èªå¨å¸¦åº' }, |
| | | }, |
| | | { |
| | | fieldName: 'overtimeInfoDivider', |
| | | label: 'å çæ ¸å¿ä¿¡æ¯', |
| | | component: 'Divider', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'overtimeDate', |
| | | label: 'å çæ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å çæ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'overtimeType', |
| | | label: 'å çç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: OVERTIME_TYPE_OPTIONS, |
| | | placeholder: 'è¯·éæ©å çç±»å', |
| | | }, |
| | | help: 'ä¸åç±»å对åºä¸åçèªèµè®¡ç®è§å', |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'startTime', |
| | | label: 'å¼å§æ¶é´', |
| | | component: 'TimePicker', |
| | | componentProps: { |
| | | format: 'HH:mm', |
| | | minuteStep: 30, |
| | | placeholder: 'è¯·éæ©å¼å§æ¶é´', |
| | | valueFormat: 'HH:mm:ss', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'endTime', |
| | | label: 'ç»ææ¶é´', |
| | | component: 'TimePicker', |
| | | componentProps: { |
| | | format: 'HH:mm', |
| | | minuteStep: 30, |
| | | placeholder: 'è¯·éæ©ç»ææ¶é´', |
| | | valueFormat: 'HH:mm:ss', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'crossDay', |
| | | label: 'æ¯å¦è·¨å¤©', |
| | | component: 'Switch', |
| | | componentProps: { |
| | | checkedChildren: 'æ¯', |
| | | disabled: true, |
| | | unCheckedChildren: 'å¦', |
| | | }, |
| | | defaultValue: false, |
| | | help: 'ç»ææ¶é´æ©äºæçäºå¼å§æ¶é´æ¶ï¼ç³»ç»èªå¨æ 记为跨天', |
| | | }, |
| | | { |
| | | fieldName: 'mealDeduction', |
| | | label: 'æ£é¤ç¨é¤æ¶é´', |
| | | component: 'Switch', |
| | | componentProps: { |
| | | checkedChildren: 'æ£é¤', |
| | | unCheckedChildren: '䏿£é¤', |
| | | }, |
| | | defaultValue: false, |
| | | }, |
| | | { |
| | | fieldName: 'mealDeductionHours', |
| | | label: 'ç¨é¤æ£é¤æ¶é¿', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | min: 0.5, |
| | | max: 4, |
| | | precision: 1, |
| | | step: 0.5, |
| | | style: { width: '100%' }, |
| | | }, |
| | | defaultValue: 1, |
| | | suffix: 'å°æ¶', |
| | | dependencies: { |
| | | triggerFields: ['mealDeduction'], |
| | | show: (values) => Boolean(values.mealDeduction), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'duration', |
| | | label: 'å çæ¶é¿', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | disabled: true, |
| | | min: 0, |
| | | precision: 1, |
| | | step: 0.5, |
| | | style: { width: '100%' }, |
| | | }, |
| | | help: 'æå®é
æ¶æ®µæ£é¤ç¨é¤æ¶é´åï¼èªå¨åæ´å° 0.5 å°æ¶', |
| | | suffix: 'å°æ¶', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'reasonInfoDivider', |
| | | label: 'å çäºç±', |
| | | component: 'Divider', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'reason', |
| | | label: 'å çåå ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | maxlength: 500, |
| | | placeholder: '请说æå ççå¿
è¦æ§ååå ', |
| | | rows: 3, |
| | | showCount: true, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'relatedType', |
| | | label: 'å
³èç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: RELATED_TYPE_OPTIONS, |
| | | placeholder: 'å¯éï¼é¡¹ç®æå·¥å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'relatedCode', |
| | | label: '项ç®/å·¥åç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | maxlength: 100, |
| | | placeholder: '请è¾å
¥å
³èé¡¹ç®æå·¥åç¼å·', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['relatedType'], |
| | | componentProps: (values) => ({ |
| | | allowClear: true, |
| | | maxlength: 100, |
| | | placeholder: values.relatedType === 1 |
| | | ? '请è¾å
¥é¡¹ç®ç¼å·' |
| | | : values.relatedType === 2 |
| | | ? '请è¾å
¥å·¥åç¼å·' |
| | | : '请å
éæ©å
³èç±»å', |
| | | disabled: !values.relatedType, |
| | | }), |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'workContent', |
| | | label: 'å·¥ä½å
容ç®è¿°', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | maxlength: 1000, |
| | | placeholder: '请ç®è¦æè¿°å çæé´è®¡å宿çå
·ä½å·¥ä½', |
| | | rows: 4, |
| | | showCount: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表æç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'overtimeDate', |
| | | label: 'å çæ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©å çæ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'overtimeType', |
| | | label: 'å çç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: OVERTIME_TYPE_OPTIONS, |
| | | placeholder: 'è¯·éæ©å çç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: '审æ¹ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_AUDIT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å®¡æ¹ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** åè¡¨åæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<HrmOvertimeApi.Overtime>['columns'] { |
| | | return [ |
| | | { field: 'id', title: 'ç³è¯·ç¼å·', width: 90 }, |
| | | { field: 'employeeName', title: 'ç³è¯·äºº', minWidth: 100 }, |
| | | { field: 'employeeNo', title: 'å·¥å·', minWidth: 110 }, |
| | | { field: 'deptName', title: 'æå±é¨é¨', minWidth: 120 }, |
| | | { field: 'overtimeDate', title: 'å çæ¥æ', width: 120 }, |
| | | { |
| | | field: 'overtimeType', |
| | | title: 'å çç±»å', |
| | | width: 130, |
| | | slots: { default: 'overtimeType' }, |
| | | }, |
| | | { field: 'startTime', title: 'å¼å§æ¶é´', width: 100 }, |
| | | { field: 'endTime', title: 'ç»ææ¶é´', width: 100 }, |
| | | { field: 'duration', title: 'æ¶é¿(å°æ¶)', width: 100 }, |
| | | { |
| | | 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 { HrmOvertimeApi } from '#/api/hrm/overtime'; |
| | | import type { DescriptionItemSchema } from '#/components/description'; |
| | | |
| | | import { h, onMounted, ref } from 'vue'; |
| | | |
| | | import { ContentWrap } from '@vben/common-ui'; |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { formatDateTime } from '@vben/utils'; |
| | | |
| | | import { Spin, Tag } from 'ant-design-vue'; |
| | | |
| | | import { getOvertime } from '#/api/hrm/overtime'; |
| | | import { DictTag } from '#/components/dict-tag'; |
| | | import { useDescription } from '#/components/description'; |
| | | import { getOvertimeTypeLabel } from '#/views/hrm/overtime/data'; |
| | | |
| | | defineOptions({ name: 'HrmOvertimeApproveDetail' }); |
| | | |
| | | const props = defineProps<{ id: string }>(); |
| | | const loading = ref(false); |
| | | const formData = ref<HrmOvertimeApi.Overtime>(); |
| | | |
| | | function textRender(value: unknown) { |
| | | return value === 0 || value ? String(value) : '-'; |
| | | } |
| | | |
| | | const [Descriptions] = useDescription({ |
| | | bordered: true, |
| | | column: 2, |
| | | schema: [ |
| | | { label: 'ç³è¯·äºº', field: 'employeeName', render: textRender }, |
| | | { label: 'å·¥å·', field: 'employeeNo', render: textRender }, |
| | | { label: 'æå±é¨é¨', field: 'deptName', render: textRender }, |
| | | { label: 'å²ä½/è级', field: 'postName', render: textRender }, |
| | | { label: 'å çæ¥æ', field: 'overtimeDate', render: textRender }, |
| | | { |
| | | label: 'å çç±»å', |
| | | field: 'overtimeType', |
| | | render: (value) => h(Tag, { color: value === 3 ? 'red' : value === 2 ? 'orange' : 'blue' }, () => getOvertimeTypeLabel(Number(value))), |
| | | }, |
| | | { label: 'å¼å§æ¶é´', field: 'startTime', render: textRender }, |
| | | { label: 'ç»ææ¶é´', field: 'endTime', render: textRender }, |
| | | { label: 'å çæ¶é¿', field: 'duration', render: (value) => `${textRender(value)} å°æ¶` }, |
| | | { label: 'æ¯å¦è·¨å¤©', field: 'crossDay', render: (value) => value ? 'æ¯' : 'å¦' }, |
| | | { label: 'ç¨é¤æ£é¤', field: 'mealDeduction', render: (value) => value ? 'æ¯' : 'å¦' }, |
| | | { label: 'æ£é¤æ¶é¿', field: 'mealDeductionHours', render: (value) => value ? `${value} å°æ¶` : '-' }, |
| | | { label: 'å çåå ', field: 'reason', render: textRender }, |
| | | { label: 'å
³èç±»å', field: 'relatedType', render: (value) => value === 1 ? '项ç®' : value === 2 ? 'å·¥å' : '-' }, |
| | | { label: '项ç®/å·¥åç¼å·', field: 'relatedCode', render: textRender }, |
| | | { label: 'å·¥ä½å
容ç®è¿°', field: 'workContent', render: textRender }, |
| | | { |
| | | label: '审æ¹ç¶æ', |
| | | field: 'status', |
| | | render: (value) => h(DictTag, { type: DICT_TYPE.HRM_AUDIT_STATUS, value }), |
| | | }, |
| | | { label: 'å建æ¶é´', field: 'createTime', render: (value) => formatDateTime(value) as string }, |
| | | ] as DescriptionItemSchema[], |
| | | }); |
| | | |
| | | onMounted(async () => { |
| | | loading.value = true; |
| | | try { |
| | | formData.value = await getOvertime(Number(props.id)); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <ContentWrap class="m-2"> |
| | | <Spin :spinning="loading" tip="å è½½ä¸..."> |
| | | <div class="rounded-lg border border-border bg-background p-4"> |
| | | <div class="mb-4 flex items-center gap-2"> |
| | | <span class="inline-block h-4 w-1 rounded bg-primary" /> |
| | | <span class="text-base font-semibold">å çç³è¯·ä¿¡æ¯</span> |
| | | </div> |
| | | <Descriptions :data="formData" /> |
| | | </div> |
| | | </Spin> |
| | | </ContentWrap> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmOvertimeApi } from '#/api/hrm/overtime'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, Modal, Select, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | cancelOvertime, |
| | | deleteOvertime, |
| | | getOvertimeApproveProcessDefinitionList, |
| | | getOvertimePage, |
| | | submitOvertime, |
| | | } from '#/api/hrm/overtime'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { |
| | | getOvertimeTypeLabel, |
| | | 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: HrmOvertimeApi.Overtime) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: HrmOvertimeApi.Overtime) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | async function handleDelete(row: HrmOvertimeApi.Overtime) { |
| | | const hideLoading = message.loading({ content: 'æ£å¨å é¤å çç³è¯·...', duration: 0 }); |
| | | try { |
| | | await deleteOvertime(row.id!); |
| | | message.success('å çç³è¯·å 餿å'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const submitModalVisible = ref(false); |
| | | const currentRow = ref<HrmOvertimeApi.Overtime>(); |
| | | const processList = ref<HrmOvertimeApi.ApproveProcess[]>([]); |
| | | const selectedProcessKey = ref<string>(); |
| | | const submitLoading = ref(false); |
| | | |
| | | async function handleSubmit(row: HrmOvertimeApi.Overtime) { |
| | | processList.value = await getOvertimeApproveProcessDefinitionList(); |
| | | currentRow.value = row; |
| | | selectedProcessKey.value = undefined; |
| | | submitModalVisible.value = true; |
| | | } |
| | | |
| | | async function confirmSubmit() { |
| | | if (!selectedProcessKey.value) { |
| | | message.warning('è¯·éæ©åååå
¬å®¡æ¹æµç¨'); |
| | | return; |
| | | } |
| | | submitLoading.value = true; |
| | | try { |
| | | await submitOvertime(currentRow.value!.id!, selectedProcessKey.value); |
| | | message.success('å·²æäº¤è³åååå
¬å®¡æ¹'); |
| | | submitModalVisible.value = false; |
| | | handleRefresh(); |
| | | } finally { |
| | | submitLoading.value = false; |
| | | } |
| | | } |
| | | |
| | | async function handleCancel(row: HrmOvertimeApi.Overtime) { |
| | | await cancelOvertime(row.id!); |
| | | message.success('æ¤éæå'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | function getOvertimeTypeColor(type?: number) { |
| | | return type === 1 ? 'blue' : type === 2 ? 'orange' : type === 3 ? 'red' : 'default'; |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { schema: useGridFormSchema() }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getOvertimePage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true, search: true }, |
| | | } as VxeTableGridOptions<HrmOvertimeApi.Overtime>, |
| | | }); |
| | | </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:overtime:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #overtimeType="{ row }"> |
| | | <Tag :color="getOvertimeTypeColor(row.overtimeType)"> |
| | | {{ getOvertimeTypeLabel(row.overtimeType) }} |
| | | </Tag> |
| | | </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:overtime:update'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤', |
| | | type: 'link', |
| | | auth: ['hrm:overtime:submit'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¤é', |
| | | type: 'link', |
| | | auth: ['hrm:overtime:cancel'], |
| | | ifShow: row.status === 10, |
| | | onClick: handleCancel.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:overtime:delete'], |
| | | ifShow: row.status === 0 || row.status === 40, |
| | | popConfirm: { |
| | | title: '确认å é¤è¯¥å çç³è¯·åï¼', |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | <Modal |
| | | v-model:open="submitModalVisible" |
| | | title="æäº¤è³åååå
¬" |
| | | width="420px" |
| | | :confirm-loading="submitLoading" |
| | | @ok="confirmSubmit" |
| | | > |
| | | <Select |
| | | v-model:value="selectedProcessKey" |
| | | class="w-full" |
| | | placeholder="è¯·éæ©å çå®¡æ¹æµç¨" |
| | | :options="processList.map((item) => ({ label: item.name, value: item.key }))" |
| | | /> |
| | | </Modal> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmOvertimeApi } from '#/api/hrm/overtime'; |
| | | import type { FormType } from '../data'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createOvertime, |
| | | getCurrentOvertimeApplicant, |
| | | getOvertime, |
| | | updateOvertime, |
| | | } from '#/api/hrm/overtime'; |
| | | import { $t } from '#/locales'; |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const isDetail = computed(() => formType.value === 'detail'); |
| | | const getTitle = computed(() => { |
| | | if (isDetail.value) return 'æ¥çå çç³è¯·'; |
| | | return formType.value === 'update' ? 'ä¿®æ¹å çç³è¯·' : 'æ°å¢å çç³è¯·'; |
| | | }); |
| | | |
| | | /** æ ¹æ®æ¶æ®µã跨天æ è¯åç¨é¤æ£é¤è®¡ç®å çæ¶é¿ï¼å¹¶æ 0.5 å°æ¶åæ´ */ |
| | | function calculateDuration(values: Record<string, any>) { |
| | | const { startTime, endTime } = values; |
| | | if (!startTime || !endTime) return { crossDay: false, duration: 0 }; |
| | | |
| | | const toMinutes = (time: string) => { |
| | | const [hour = 0, minute = 0] = time.split(':').map(Number); |
| | | return hour * 60 + minute; |
| | | }; |
| | | const startMinutes = toMinutes(startTime); |
| | | let endMinutes = toMinutes(endTime); |
| | | const crossDay = endMinutes <= startMinutes; |
| | | if (crossDay) endMinutes += 24 * 60; |
| | | |
| | | const deductionMinutes = values.mealDeduction |
| | | ? Number(values.mealDeductionHours || 0) * 60 |
| | | : 0; |
| | | const actualHours = Math.max(0, (endMinutes - startMinutes - deductionMinutes) / 60); |
| | | return { |
| | | crossDay, |
| | | duration: Math.round(actualHours * 2) / 2, |
| | | }; |
| | | } |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { class: 'w-full' }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 120, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | scrollToFirstError: true, |
| | | showDefaultActions: false, |
| | | handleValuesChange: (values, changedFields) => { |
| | | const durationFields = [ |
| | | 'startTime', |
| | | 'endTime', |
| | | 'mealDeduction', |
| | | 'mealDeductionHours', |
| | | ]; |
| | | if (changedFields.some((field) => durationFields.includes(field))) { |
| | | const result = calculateDuration(values); |
| | | void formApi.setValues(result); |
| | | } |
| | | if (changedFields.includes('relatedType') && !values.relatedType) { |
| | | void formApi.setFieldValue('relatedCode', undefined); |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | async function setCurrentEmployee() { |
| | | const employee = await getCurrentOvertimeApplicant(); |
| | | await formApi.setValues({ |
| | | employeeId: employee.employeeId, |
| | | employeeName: employee.employeeName, |
| | | employeeNo: employee.employeeNo, |
| | | deptName: employee.deptName, |
| | | postName: employee.postName, |
| | | mealDeduction: false, |
| | | mealDeductionHours: 1, |
| | | crossDay: false, |
| | | duration: 0, |
| | | }); |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) return; |
| | | |
| | | const data = (await formApi.getValues()) as HrmOvertimeApi.Overtime; |
| | | if (!data.employeeId) { |
| | | message.warning('å½åè´¦å·æªå
³èå工档æ¡ï¼ææ¶æ æ³æäº¤å çç³è¯·'); |
| | | return; |
| | | } |
| | | if (!data.duration || data.duration <= 0) { |
| | | message.warning('å çæ¶é¿å¿
é¡»å¤§äº 0 å°æ¶ï¼è¯·æ£æ¥æ¶æ®µæç¨é¤æ£é¤'); |
| | | return; |
| | | } |
| | | if (data.relatedType && !data.relatedCode?.trim()) { |
| | | message.warning('è¯·éæ©å
³èç±»ååå¡«å对åºçé¡¹ç®æå·¥åç¼å·'); |
| | | return; |
| | | } |
| | | |
| | | modalApi.lock(); |
| | | try { |
| | | if (data.id) await updateOvertime(data); |
| | | else await createOvertime(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() }); |
| | | formApi.setDisabled(isDetail.value); |
| | | modalApi.setState({ showConfirmButton: !isDetail.value }); |
| | | |
| | | if (!data.id) { |
| | | await setCurrentEmployee(); |
| | | return; |
| | | } |
| | | |
| | | modalApi.lock(); |
| | | try { |
| | | await formApi.setValues(await getOvertime(data.id)); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[900px]"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmSalaryStructureApi } from '#/api/hrm/salary/structure'; |
| | | |
| | | export type FormType = 'create' | 'update'; |
| | | |
| | | const statusOptions = [ |
| | | { label: 'å¯ç¨', value: 0 }, |
| | | { label: 'ç¦ç¨', value: 1 }, |
| | | ]; |
| | | |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'ç»æåç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | maxlength: 100, |
| | | placeholder: 'ä¾å¦ï¼æ ååå·¥èªé
¬ã管çå±èªé
¬', |
| | | showCount: true, |
| | | }, |
| | | rules: 'required', |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'overtimePayRatio', |
| | | label: '工使¥åç', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | addonAfter: 'å', |
| | | max: 10, |
| | | min: 0, |
| | | precision: 2, |
| | | step: 0.1, |
| | | placeholder: '0 表示ä¸è®¡å çå·¥èµ', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'restDayOvertimePayRatio', |
| | | label: '伿¯æ¥åç', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | addonAfter: 'å', |
| | | max: 10, |
| | | min: 0, |
| | | precision: 2, |
| | | step: 0.1, |
| | | placeholder: '0 表示ä¸è®¡è¯¥ç±»åå çå·¥èµ', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'legalHolidayOvertimePayRatio', |
| | | label: 'æ³å®è忥åç', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | addonAfter: 'å', |
| | | max: 10, |
| | | min: 0, |
| | | precision: 2, |
| | | step: 0.1, |
| | | placeholder: '0 表示ä¸è®¡è¯¥ç±»åå çå·¥èµ', |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { options: statusOptions }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | maxlength: 500, |
| | | placeholder: 'å¡«åéç¨å²ä½ãè级æç¹æ®è®¡èªè¯´æ', |
| | | rows: 3, |
| | | showCount: true, |
| | | }, |
| | | 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: statusOptions, |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function useGridColumns(): VxeTableGridOptions<HrmSalaryStructureApi.SalaryStructure>['columns'] { |
| | | return [ |
| | | { field: 'name', title: 'ç»æåç§°', minWidth: 180 }, |
| | | { |
| | | field: 'overtimePayRatio', |
| | | title: '工使¥å çåç', |
| | | width: 145, |
| | | slots: { default: 'overtimePayRatio' }, |
| | | }, |
| | | { |
| | | field: 'restDayOvertimePayRatio', |
| | | title: '伿¯æ¥å çåç', |
| | | width: 145, |
| | | slots: { default: 'restDayOvertimePayRatio' }, |
| | | }, |
| | | { |
| | | field: 'legalHolidayOvertimePayRatio', |
| | | title: 'æ³å®è忥åç', |
| | | width: 165, |
| | | slots: { default: 'legalHolidayOvertimePayRatio' }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 90, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'actions', |
| | | title: 'æä½', |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | width: 150, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmSalaryStructureApi } from '#/api/hrm/salary/structure'; |
| | | |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { Alert, message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteSalaryStructure, |
| | | getSalaryStructureList, |
| | | } from '#/api/hrm/salary/structure'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | defineOptions({ name: 'HrmSalaryStructure' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: HrmSalaryStructureApi.SalaryStructure) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | async function handleDelete(row: HrmSalaryStructureApi.SalaryStructure) { |
| | | await deleteSalaryStructure(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', ['èªé
Ⱦ'])); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | function formatRatio(value?: number) { |
| | | return value === undefined || value === null ? '-' : `${value.toFixed(2)} å`; |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { schema: useGridFormSchema() }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | pagerConfig: { enabled: false }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async (_params, formValues) => |
| | | await getSalaryStructureList(formValues), |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true, search: true }, |
| | | } as VxeTableGridOptions<HrmSalaryStructureApi.SalaryStructure>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Alert |
| | | class="mb-4" |
| | | description="åºæ¬å·¥èµåå工档æ¡ï¼ç¤¾ä¿åå
¬ç§¯éåºæ°ååå·¥ç¤¾ä¿æ¡£æ¡ãè¿éä»
ç»´æ¤å·¥ä½æ¥ã伿¯æ¥åæ³å®è忥å çåçã" |
| | | message="èªé
¬ç»æç»´æ¤å ç计èªè§å" |
| | | show-icon |
| | | type="info" |
| | | /> |
| | | <Grid table-title="èªé
¬ç»æå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['èªé
Ⱦ']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:salary-structure:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #overtimePayRatio="{ row }"> |
| | | <Tag :color="(row.overtimePayRatio ?? 0) > 0 ? 'blue' : 'default'"> |
| | | {{ (row.overtimePayRatio ?? 0) > 0 ? formatRatio(row.overtimePayRatio) : 'ä¸è®¡å çå·¥èµ' }} |
| | | </Tag> |
| | | </template> |
| | | <template #restDayOvertimePayRatio="{ row }"> |
| | | <Tag :color="(row.restDayOvertimePayRatio ?? 0) > 0 ? 'blue' : 'default'"> |
| | | {{ (row.restDayOvertimePayRatio ?? 0) > 0 ? formatRatio(row.restDayOvertimePayRatio) : 'ä¸è®¡å çå·¥èµ' }} |
| | | </Tag> |
| | | </template> |
| | | <template #legalHolidayOvertimePayRatio="{ row }"> |
| | | <Tag :color="(row.legalHolidayOvertimePayRatio ?? 0) > 0 ? 'blue' : 'default'"> |
| | | {{ (row.legalHolidayOvertimePayRatio ?? 0) > 0 ? formatRatio(row.legalHolidayOvertimePayRatio) : 'ä¸è®¡å çå·¥èµ' }} |
| | | </Tag> |
| | | </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:salary-structure:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:salary-structure:delete'], |
| | | popConfirm: { |
| | | title: '确认å é¤è¯¥èªé
¬ç»æåï¼å·²è¢«å工使ç¨çç»æä¸å¯å é¤ã', |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmSalaryStructureApi } from '#/api/hrm/salary/structure'; |
| | | import type { FormType } from '../data'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { Alert, message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createSalaryStructure, |
| | | getSalaryStructure, |
| | | updateSalaryStructure, |
| | | } from '#/api/hrm/salary/structure'; |
| | | 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: 110, |
| | | }, |
| | | 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(); |
| | | try { |
| | | const data = |
| | | (await formApi.getValues()) as HrmSalaryStructureApi.SalaryStructure; |
| | | if (data.id) { |
| | | await updateSalaryStructure(data); |
| | | } else { |
| | | await createSalaryStructure(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; |
| | | await formApi.resetForm(); |
| | | if (!data.id) { |
| | | await formApi.setValues({ |
| | | legalHolidayOvertimePayRatio: 3, |
| | | overtimePayRatio: 1.5, |
| | | restDayOvertimePayRatio: 2, |
| | | status: 0, |
| | | }); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await formApi.setValues(await getSalaryStructure(data.id)); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-[680px]"> |
| | | <Alert |
| | | class="mx-4 mb-4" |
| | | description="ä¸ç§å çç±»ååå«è®¾ç½®åçï¼æä¸é¡¹è®¾ä¸º 0 æ¶ï¼ä»
该类åä¸è®¡ç®å çå·¥èµï¼å®¡æ¹éè¿çå çæ¶é¿ä»æ£å¸¸ç»è®¡ã" |
| | | message="å çå·¥èµè®¡ç®è¯´æ" |
| | | show-icon |
| | | type="info" |
| | | /> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |