feat(hrm): 新增人事管理模块功能
- 添加转正申请功能,包括创建、编辑、审批流程
- 添加薪酬结构管理功能,支持基本工资、绩效占比等配置
- 添加员工薪酬档案管理功能,可单独维护员工个人薪酬
- 实现转正申请的提交、审核、驳回等完整流程
- 添加薪酬相关的API接口和服务
- 集成员工选择组件优化用户体验
- 完善相关表单验证和错误处理机制
| | |
| | | ) { |
| | | return requestClient.put('/hrm/regularize/audit', { id, pass, reviewRemark }); |
| | | } |
| | | |
| | | export function createRegularize(data: HrmRegularizeApi.Regularize) { |
| | | return requestClient.post<number>('/hrm/regularize/create', data); |
| | | } |
| | | |
| | | export function updateRegularize(data: HrmRegularizeApi.Regularize) { |
| | | return requestClient.put('/hrm/regularize/update', data); |
| | | } |
| | | |
| | | export function deleteRegularize(id: number) { |
| | | return requestClient.delete('/hrm/regularize/delete', { params: { id } }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmEmployeeSalaryApi { |
| | | export interface EmployeeSalary { |
| | | id?: number; |
| | | userId?: number; |
| | | employeeNo?: string; |
| | | employeeName?: string; |
| | | salaryStructureId?: number; |
| | | salaryStructureName?: string; |
| | | baseSalary?: number; |
| | | performanceSalary?: number; |
| | | mealAllowance?: number; |
| | | transportAllowance?: number; |
| | | otherAllowance?: number; |
| | | effectiveDate?: string; |
| | | status?: number; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | export interface PageParams extends PageParam { |
| | | userId?: number; |
| | | salaryStructureId?: number; |
| | | status?: number; |
| | | } |
| | | } |
| | | |
| | | export function getEmployeeSalaryPage(params: HrmEmployeeSalaryApi.PageParams) { |
| | | return requestClient.get<PageResult<HrmEmployeeSalaryApi.EmployeeSalary>>( |
| | | '/hrm/employee-salary/page', { params }, |
| | | ); |
| | | } |
| | | |
| | | export function getEmployeeSalary(id: number) { |
| | | return requestClient.get<HrmEmployeeSalaryApi.EmployeeSalary>( |
| | | '/hrm/employee-salary/get', { params: { id } }, |
| | | ); |
| | | } |
| | | |
| | | export function getEmployeeSalaryHistory(userId: number) { |
| | | return requestClient.get<HrmEmployeeSalaryApi.EmployeeSalary[]>( |
| | | '/hrm/employee-salary/list-by-user', { params: { userId } }, |
| | | ); |
| | | } |
| | | |
| | | export function createEmployeeSalary(data: HrmEmployeeSalaryApi.EmployeeSalary) { |
| | | return requestClient.post('/hrm/employee-salary/create', data); |
| | | } |
| | | |
| | | export function updateEmployeeSalary(data: HrmEmployeeSalaryApi.EmployeeSalary) { |
| | | return requestClient.put('/hrm/employee-salary/update', data); |
| | | } |
| | | |
| | | export function deleteEmployeeSalary(id: number) { |
| | | return requestClient.delete('/hrm/employee-salary/delete', { params: { id } }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmSalaryStructureApi { |
| | | export interface SalaryStructure { |
| | | id?: number; |
| | | name?: string; |
| | | baseSalary?: number; |
| | | performanceRatio?: number; |
| | | overtimePayRatio?: number; |
| | | socialSecurityBase?: number; |
| | | housingFundBase?: number; |
| | | status?: number; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | export interface SalaryStructureQueryParam extends Partial<PageParam> { |
| | | name?: string; |
| | | status?: number; |
| | | } |
| | | } |
| | | |
| | | export function getSalaryStructurePage(params: HrmSalaryStructureApi.SalaryStructureQueryParam) { |
| | | return requestClient.get<PageResult<HrmSalaryStructureApi.SalaryStructure>>( |
| | | '/hrm/salary/structure/page', { params }, |
| | | ); |
| | | } |
| | | |
| | | 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}`); |
| | | } |
| | |
| | | }); |
| | | } |
| | | |
| | | /** çææ¡ç /äºç»´ç å¾çï¼æå¡ç«¯ zxing æå¾ï¼è¿å PNG Base64ï¼ä¸è½åºï¼ */ |
| | | /** çææ¡ç /äºç»´ç å¾çï¼æå¡ç«¯è¿å PNG data URLï¼ä¸è½åºï¼ */ |
| | | export function renderBarcodeImage( |
| | | content: string, |
| | | format?: number, |
| | |
| | | getTicket, |
| | | resolveTicket, |
| | | } from '#/api/aftersales/ticket'; |
| | | import { UserSelect } from '#/views/system/user/components'; |
| | | |
| | | defineOptions({ name: 'AftersalesTicketDetail' }); |
| | | |
| | |
| | | assignUserId.value = undefined; |
| | | } |
| | | |
| | | async function handleAssignUserChange(value: number | number[] | undefined) { |
| | | assignUserId.value = Array.isArray(value) ? value[0] : value; |
| | | } |
| | | |
| | | async function handleAssign() { |
| | | if (!assignUserId.value) { |
| | | message.warning('请è¾å
¥å¤ç人ID'); |
| | | message.warning('è¯·éæ©å¤ç人'); |
| | | return; |
| | | } |
| | | await assignTicket(ticketId.value, assignUserId.value); |
| | |
| | | <Button @click="toggleAssignInput">åé
å¤ç人</Button> |
| | | </template> |
| | | <template v-else> |
| | | <Input |
| | | v-model:value="assignUserId" |
| | | placeholder="å¤ç人ID" |
| | | type="number" |
| | | style="width: 120px" |
| | | @pressEnter="handleAssign" |
| | | <UserSelect |
| | | :model-value="assignUserId" |
| | | placeholder="è¯·éæ©å¤ç人" |
| | | style="width: 200px" |
| | | @update:model-value="handleAssignUserChange" |
| | | /> |
| | | <Button size="small" type="primary" @click="handleAssign">确认</Button> |
| | | <Button size="small" @click="toggleAssignInput">åæ¶</Button> |
| | |
| | | import type { AftersalesTicketApi } from '#/api/aftersales/ticket'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { AftersaleTicketStatusEnum } from '@vben/constants'; |
| | |
| | | import { useTicketGridColumns, useTicketGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const router = useRouter(); |
| | | const sceneType = ref('1'); |
| | | |
| | | /** å¤çåºæ¯ç±»åç忢 */ |
| | |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥çå·¥å */ |
| | | /** æ¥çå·¥åï¼è·³è½¬è¯¦æ
页ï¼ç¶ææµè½¬æä½éä¸å¨è¯¥é¡µé¢ï¼ */ |
| | | function handleDetail(row: AftersalesTicketApi.Ticket) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | router.push({ name: 'AftersalesTicketDetail', params: { id: row.id } }); |
| | | } |
| | | |
| | | /** ç¼è¾å·¥å */ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmRegularizeApi } from '#/api/hrm/regularize'; |
| | | |
| | | import { getSimpleUserList } from '#/api/system/user'; |
| | | |
| | | export const statusOptions = [ |
| | | { label: 'è稿', value: 0 }, |
| | | { label: '审æ¹ä¸', value: 10 }, |
| | | { label: 'å·²éè¿', value: 20 }, |
| | | { label: '已驳å', value: 30 }, |
| | | { label: '已忶', value: 40 }, |
| | | ]; |
| | | |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { fieldName: 'id', component: 'Input', dependencies: { triggerFields: [''], show: () => false } }, |
| | | { |
| | | fieldName: 'userId', label: 'ç³è¯·äºº', component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getSimpleUserList, labelField: 'nickname', valueField: 'id', |
| | | allowClear: true, showSearch: true, optionFilterProp: 'label', |
| | | placeholder: 'ä¸éå为å½åç¨æ·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'expectedDate', label: 'æè½¬æ£æ¥æ', component: 'DatePicker', |
| | | componentProps: { valueFormat: 'YYYY-MM-DD', style: { width: '100%' } }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'reason', label: '转æ£åå ', component: 'Textarea', |
| | | componentProps: { rows: 4, maxlength: 1000, showCount: true }, |
| | | rules: 'required', formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'remark', label: '夿³¨', component: 'Textarea', |
| | | componentProps: { rows: 2, maxlength: 500 }, formItemClass: 'col-span-2', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { fieldName: 'no', label: 'ç³è¯·åå·', component: 'Input', componentProps: { allowClear: true } }, |
| | | { |
| | | fieldName: 'status', label: '审æ¹ç¶æ', component: 'Select', |
| | | componentProps: { options: statusOptions, allowClear: true }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function useGridColumns(): VxeTableGridOptions<HrmRegularizeApi.Regularize>['columns'] { |
| | | return [ |
| | | { field: 'no', title: 'ç³è¯·åå·', minWidth: 175 }, |
| | | { field: 'userName', title: 'ç³è¯·äºº', minWidth: 110 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 120 }, |
| | | { field: 'expectedDate', title: 'æè½¬æ£æ¥æ', width: 125 }, |
| | | { field: 'reason', title: '转æ£åå ', minWidth: 200, showOverflow: true }, |
| | | { field: 'status', title: 'ç¶æ', width: 105, slots: { default: 'status' } }, |
| | | { field: 'reviewerName', title: 'å®¡æ ¸äºº', width: 110 }, |
| | | { field: 'createTime', title: 'å建æ¶é´', width: 180, formatter: 'formatDateTime' }, |
| | | { field: 'actions', title: 'æä½', fixed: 'right', width: 300, slots: { default: 'actions' } }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmRegularizeApi } from '#/api/hrm/regularize'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { Page, useVbenModal } from '#/packages/effects/common-ui/src'; |
| | | import { Input, message, Modal, Tag } from 'ant-design-vue'; |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | auditRegularize, deleteRegularize, getRegularizePage, submitRegularize, |
| | | } from '#/api/hrm/regularize'; |
| | | import { statusOptions, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | defineOptions({ name: 'HrmRegularize' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ connectedComponent: Form, destroyOnClose: true }); |
| | | const auditOpen = ref(false); |
| | | const auditLoading = ref(false); |
| | | const auditId = ref<number>(); |
| | | const auditPass = ref(true); |
| | | const auditRemark = ref(''); |
| | | |
| | | function refresh() { gridApi.query(); } |
| | | function create() { formModalApi.setData({}).open(); } |
| | | function edit(row: HrmRegularizeApi.Regularize) { formModalApi.setData({ id: row.id }).open(); } |
| | | async function submit(row: HrmRegularizeApi.Regularize) { |
| | | await submitRegularize(row.id!); |
| | | message.success('å·²æäº¤å®¡æ¹'); |
| | | refresh(); |
| | | } |
| | | async function remove(row: HrmRegularizeApi.Regularize) { |
| | | await deleteRegularize(row.id!); |
| | | message.success('å 餿å'); |
| | | refresh(); |
| | | } |
| | | function openAudit(row: HrmRegularizeApi.Regularize, pass: boolean) { |
| | | auditId.value = row.id; |
| | | auditPass.value = pass; |
| | | auditRemark.value = ''; |
| | | auditOpen.value = true; |
| | | } |
| | | async function confirmAudit() { |
| | | if (!auditId.value) return; |
| | | if (!auditPass.value && !auditRemark.value.trim()) { |
| | | message.warning('请填å驳ååå '); |
| | | return; |
| | | } |
| | | auditLoading.value = true; |
| | | try { |
| | | await auditRegularize(auditId.value, auditPass.value, auditRemark.value.trim()); |
| | | auditOpen.value = false; |
| | | message.success('å®¡æ ¸å®æ'); |
| | | refresh(); |
| | | } finally { auditLoading.value = false; } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { schema: useGridFormSchema() }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), height: 'auto', keepSource: true, |
| | | proxyConfig: { ajax: { query: async ({ page }, formValues) => |
| | | getRegularizePage({ pageNo: page.currentPage, pageSize: page.pageSize, ...formValues }) } }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true, search: true }, |
| | | } as VxeTableGridOptions<HrmRegularizeApi.Regularize>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="refresh" /> |
| | | <Modal v-model:open="auditOpen" :confirm-loading="auditLoading" |
| | | :title="auditPass ? 'éè¿è½¬æ£ç³è¯·' : '驳å转æ£ç³è¯·'" @ok="confirmAudit"> |
| | | <p class="mb-2">{{ auditPass ? '确认éè¿è¯¥è½¬æ£ç³è¯·ï¼' : '请填å驳ååå ã' }}</p> |
| | | <Input.TextArea v-model:value="auditRemark" :rows="3" :maxlength="500" show-count |
| | | :placeholder="auditPass ? 'å®¡æ ¸æè§ï¼éå¡«ï¼' : '驳ååå ï¼å¿
å¡«ï¼'" /> |
| | | </Modal> |
| | | <Grid table-title="转æ£ç³è¯·"> |
| | | <template #toolbar-tools> |
| | | <TableAction :actions="[{ label: 'æ°å¢ç³è¯·', type: 'primary', icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:regularize:create'], onClick: create }]" /> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag :color="row.status === 20 ? 'success' : row.status === 30 ? 'error' : 'default'"> |
| | | {{ row.statusName || statusOptions.find((s) => s.value === row.status)?.label || '-' }} |
| | | </Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction :actions="[ |
| | | ...(row.status === 0 || row.status === 30 ? [ |
| | | { label: 'ç¼è¾', type: 'link' as const, auth: ['hrm:regularize:update'], onClick: edit.bind(null, row) }, |
| | | { label: 'æäº¤', type: 'link' as const, auth: ['hrm:regularize:update'], |
| | | popConfirm: { title: 'ç¡®å®æäº¤è½¬æ£å®¡æ¹åï¼', confirm: submit.bind(null, row) } }, |
| | | { label: 'å é¤', type: 'link' as const, danger: true, auth: ['hrm:regularize:delete'], |
| | | popConfirm: { title: 'ç¡®å®å é¤è¯¥ç³è¯·åï¼', confirm: remove.bind(null, row) } }, |
| | | ] : []), |
| | | ...(row.status === 10 ? [ |
| | | { label: 'éè¿', type: 'link' as const, auth: ['hrm:regularize:audit'], |
| | | onClick: openAudit.bind(null, row, true) }, |
| | | { label: '驳å', type: 'link' as const, danger: true, auth: ['hrm:regularize:audit'], |
| | | onClick: openAudit.bind(null, row, false) }, |
| | | ] : []), |
| | | ]" /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmRegularizeApi } from '#/api/hrm/regularize'; |
| | | |
| | | 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 { createRegularize, getRegularize, updateRegularize } from '#/api/hrm/regularize'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const isEdit = ref(false); |
| | | const title = computed(() => isEdit.value ? 'ä¿®æ¹è½¬æ£ç³è¯·' : 'æ°å¢è½¬æ£ç³è¯·'); |
| | | 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 HrmRegularizeApi.Regularize; |
| | | if (isEdit.value) await updateRegularize(data); |
| | | else await createRegularize(data); |
| | | await modalApi.close(); |
| | | message.success('ä¿åæå'); |
| | | emit('success'); |
| | | } finally { modalApi.unlock(); } |
| | | }, |
| | | async onOpenChange(open: boolean) { |
| | | if (!open) return; |
| | | const { id } = modalApi.getData<{ id?: number }>(); |
| | | isEdit.value = !!id; |
| | | await formApi.resetForm(); |
| | | if (id) { |
| | | modalApi.lock(); |
| | | try { await formApi.setValues(await getRegularize(id)); } |
| | | finally { modalApi.unlock(); } |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="title" class="w-[680px]"><Form class="mx-4" /></Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeSalaryApi } from '#/api/hrm/salary/employee-salary'; |
| | | |
| | | import { getEmployeeSimpleList } from '#/api/hrm/employee'; |
| | | import { getSalaryStructureList } from '#/api/hrm/salary/structure'; |
| | | |
| | | const statusOptions = [ |
| | | { label: 'å¯ç¨', value: 0 }, |
| | | { label: 'ç¦ç¨', value: 1 }, |
| | | ]; |
| | | |
| | | 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, optionFilterProp: 'label', placeholder: 'è¯·éæ©åå·¥', |
| | | }, |
| | | rules: 'selectRequired', formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'salaryStructureId', label: 'èªé
Ⱦ', component: 'ApiSelect', |
| | | componentProps: { |
| | | api: () => getSalaryStructureList({ status: 0 }), |
| | | labelField: 'name', valueField: 'id', |
| | | placeholder: 'è¯·éæ©å·²å¯ç¨çèªé
Ⱦ', |
| | | }, |
| | | rules: 'selectRequired', formItemClass: 'col-span-2', |
| | | }, |
| | | ...([ |
| | | ['baseSalary', 'åºæ¬å·¥èµ'], ['performanceSalary', '绩æå·¥èµ'], |
| | | ['mealAllowance', 'é¤è¡¥'], ['transportAllowance', '交é补贴'], |
| | | ['otherAllowance', 'å
¶ä»è¡¥è´´'], |
| | | ] as const).map(([fieldName, label]) => ({ |
| | | fieldName, label, component: 'InputNumber' as const, |
| | | componentProps: { min: 0, precision: 2, style: { width: '100%' } }, |
| | | ...(fieldName === 'baseSalary' ? { rules: 'required' as const } : {}), |
| | | })), |
| | | { |
| | | fieldName: 'effectiveDate', label: 'çææ¥æ', component: 'DatePicker', |
| | | componentProps: { valueFormat: 'YYYY-MM-DD', style: { width: '100%' } }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'status', label: 'ç¶æ', component: 'RadioGroup', |
| | | componentProps: { options: statusOptions }, rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', label: '夿³¨', component: 'Textarea', |
| | | componentProps: { rows: 2, maxlength: 500 }, formItemClass: 'col-span-2', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'userId', label: 'åå·¥', component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getEmployeeSimpleList, labelField: 'name', valueField: 'id', |
| | | allowClear: true, showSearch: true, optionFilterProp: 'label', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', label: 'ç¶æ', component: 'Select', |
| | | componentProps: { options: statusOptions, allowClear: true }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function useGridColumns(): VxeTableGridOptions<HrmEmployeeSalaryApi.EmployeeSalary>['columns'] { |
| | | return [ |
| | | { field: 'employeeNo', title: 'åå·¥ç¼å·', minWidth: 120 }, |
| | | { field: 'employeeName', title: 'åå·¥å§å', minWidth: 100 }, |
| | | { field: 'salaryStructureName', title: 'èªé
Ⱦ', minWidth: 140 }, |
| | | { field: 'baseSalary', title: 'åºæ¬å·¥èµ', width: 115, formatter: 'formatAmount2' }, |
| | | { field: 'performanceSalary', title: '绩æå·¥èµ', width: 115, formatter: 'formatAmount2' }, |
| | | { field: 'effectiveDate', title: 'çææ¥æ', width: 120 }, |
| | | { field: 'status', title: 'ç¶æ', width: 90, slots: { default: 'status' } }, |
| | | { field: 'createTime', title: 'å建æ¶é´', width: 180, formatter: 'formatDateTime' }, |
| | | { field: 'actions', title: 'æä½', fixed: 'right', width: 180, slots: { default: 'actions' } }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmEmployeeSalaryApi } from '#/api/hrm/salary/employee-salary'; |
| | | |
| | | 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 { deleteEmployeeSalary, getEmployeeSalaryPage } from '#/api/hrm/salary/employee-salary'; |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | defineOptions({ name: 'HrmEmployeeSalary' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ connectedComponent: Form, destroyOnClose: true }); |
| | | function refresh() { gridApi.query(); } |
| | | function create() { formModalApi.setData({}).open(); } |
| | | function edit(row: HrmEmployeeSalaryApi.EmployeeSalary) { formModalApi.setData({ id: row.id }).open(); } |
| | | async function remove(row: HrmEmployeeSalaryApi.EmployeeSalary) { |
| | | await deleteEmployeeSalary(row.id!); |
| | | message.success('å 餿å'); |
| | | refresh(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { schema: useGridFormSchema() }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), height: 'auto', keepSource: true, |
| | | proxyConfig: { ajax: { query: async ({ page }, formValues) => |
| | | getEmployeeSalaryPage({ pageNo: page.currentPage, pageSize: page.pageSize, ...formValues }) } }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true, search: true }, |
| | | } as VxeTableGridOptions<HrmEmployeeSalaryApi.EmployeeSalary>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="refresh" /> |
| | | <Grid table-title="åå·¥èªé
¬æ¡£æ¡"> |
| | | <template #toolbar-tools> |
| | | <TableAction :actions="[{ label: 'æ°å¢æ¡£æ¡', type: 'primary', icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:salary:create'], onClick: create }]" /> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag :color="row.status === 0 ? 'success' : 'default'"> |
| | | {{ row.status === 0 ? 'å¯ç¨' : 'ç¦ç¨' }} |
| | | </Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction :actions="[ |
| | | { label: 'ç¼è¾', type: 'link', icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:salary:update'], onClick: edit.bind(null, row) }, |
| | | { label: 'å é¤', type: 'link', danger: true, icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:salary:delete'], popConfirm: { |
| | | title: 'ç¡®å®å é¤è¯¥èªé
¬æ¡£æ¡åï¼', confirm: remove.bind(null, row), |
| | | } }, |
| | | ]" /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmEmployeeSalaryApi } from '#/api/hrm/salary/employee-salary'; |
| | | |
| | | 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 { |
| | | createEmployeeSalary, getEmployeeSalary, updateEmployeeSalary, |
| | | } from '#/api/hrm/salary/employee-salary'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const isEdit = ref(false); |
| | | const title = computed(() => isEdit.value ? 'ä¿®æ¹åå·¥èªé
¬æ¡£æ¡' : 'æ°å¢åå·¥èªé
¬æ¡£æ¡'); |
| | | |
| | | 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 HrmEmployeeSalaryApi.EmployeeSalary; |
| | | if (isEdit.value) await updateEmployeeSalary(data); |
| | | else await createEmployeeSalary(data); |
| | | await modalApi.close(); |
| | | message.success('ä¿åæå'); |
| | | emit('success'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(open: boolean) { |
| | | if (!open) return; |
| | | const { id } = modalApi.getData<{ id?: number }>(); |
| | | isEdit.value = !!id; |
| | | await formApi.resetForm(); |
| | | if (id) { |
| | | modalApi.lock(); |
| | | try { await formApi.setValues(await getEmployeeSalary(id)); } |
| | | finally { modalApi.unlock(); } |
| | | } else { |
| | | await formApi.setValues({ baseSalary: 0, status: 0 }); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="title" class="w-[760px]"> |
| | | <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: 'baseSalary', |
| | | label: 'åºæ¬å·¥èµ', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | addonAfter: 'å
', |
| | | min: 0, |
| | | precision: 2, |
| | | style: { width: '100%' }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'performanceRatio', |
| | | label: '绩æå æ¯', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | addonAfter: '%', |
| | | min: 0, |
| | | max: 100, |
| | | precision: 2, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'overtimePayRatio', |
| | | label: 'å çåç', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | addonAfter: 'å', |
| | | max: 10, |
| | | min: 0, |
| | | precision: 2, |
| | | step: 0.1, |
| | | style: { width: '100%' }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'socialSecurityBase', |
| | | label: '社ä¿åºæ°', |
| | | component: 'InputNumber', |
| | | componentProps: { addonAfter: 'å
', min: 0, precision: 2, style: { width: '100%' } }, |
| | | }, |
| | | { |
| | | fieldName: 'housingFundBase', |
| | | label: 'å
¬ç§¯éåºæ°', |
| | | component: 'InputNumber', |
| | | componentProps: { addonAfter: 'å
', min: 0, precision: 2, style: { width: '100%' } }, |
| | | }, |
| | | { |
| | | 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: 'baseSalary', title: 'åºæ¬å·¥èµ', width: 125, formatter: 'formatAmount2' }, |
| | | { field: 'performanceRatio', title: '绩æå æ¯', width: 110, slots: { default: 'performanceRatio' } }, |
| | | { field: 'overtimePayRatio', title: 'å çåç', width: 110, slots: { default: 'overtimePayRatio' } }, |
| | | { field: 'socialSecurityBase', title: '社ä¿åºæ°', width: 125, formatter: 'formatAmount2' }, |
| | | { field: 'housingFundBase', title: 'å
¬ç§¯éåºæ°', width: 125, formatter: 'formatAmount2' }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 90, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | 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, |
| | | getSalaryStructurePage, |
| | | } 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, suffix = 'å') { |
| | | return value === undefined || value === null ? '-' : `${Number(value).toFixed(2)} ${suffix}`; |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { schema: useGridFormSchema() }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getSalaryStructurePage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...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:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #performanceRatio="{ row }"> |
| | | {{ formatRatio(row.performanceRatio, '%') }} |
| | | </template> |
| | | <template #overtimePayRatio="{ row }"> |
| | | <Tag :color="(row.overtimePayRatio ?? 0) > 0 ? 'blue' : 'default'"> |
| | | {{ (row.overtimePayRatio ?? 0) > 0 ? formatRatio(row.overtimePayRatio) : 'ä¸è®¡å çå·¥èµ' }} |
| | | </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:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:salary: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({ |
| | | baseSalary: 0, |
| | | overtimePayRatio: 1.5, |
| | | performanceRatio: 0, |
| | | 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="ç»´æ¤æ¬å·¥ç¨æ¯æçåºæ¬å·¥èµã绩æå æ¯ãå çåçå社ä¿åå
¬ç§¯éåºæ°ãå工个人èªé
¬å¯å¨åå·¥èªé
¬æ¡£æ¡ä¸åç¬ç»´æ¤ã" |
| | | message="èªé
¬ç»æè¯´æ" |
| | | show-icon |
| | | type="info" |
| | | /> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | content.value.trim(), |
| | | parseResult.value?.format, |
| | | ); |
| | | imageBase64.value = base64 ? `data:image/png;base64,${base64}` : ''; |
| | | imageBase64.value = base64 |
| | | ? (base64.startsWith('data:image/') ? base64 : `data:image/png;base64,${base64}`) |
| | | : ''; |
| | | if (!base64) { |
| | | message.warning('æå¾å¤±è´¥ï¼è¯·æ£æ¥æ¡ç å
容æ¯å¦ç¬¦åæéæ ¼å¼'); |
| | | } |
| | |
| | | |
| | | /** èªå¨çæå¼å
³åæ´ */ |
| | | async function handleAutoGenerateChange( |
| | | newValue: boolean, |
| | | row: MesWmBarcodeConfigApi.BarcodeConfig, |
| | | ): Promise<boolean> { |
| | | const text = row.autoGenerateFlag ? 'å¯ç¨' : 'åç¨'; |
| | | if (!row.id) { |
| | | message.error('æ¡ç é
ç½®ç¼å·ç¼ºå¤±ï¼è¯·å·æ°åéè¯'); |
| | | return false; |
| | | } |
| | | const text = newValue ? 'å¯ç¨' : 'åç¨'; |
| | | try { |
| | | await confirm(`确认è¦${text}èªå¨çæåï¼`); |
| | | } catch { |
| | | return false; |
| | | } |
| | | await updateBarcodeConfig(row); |
| | | await updateBarcodeConfig({ ...row, autoGenerateFlag: newValue }); |
| | | message.success(`${text}æå`); |
| | | return true; |
| | | } |
| | |
| | | /** é
ç½®å表çåæ®µ */ |
| | | export function useConfigGridColumns( |
| | | onAutoGenerateChange: ( |
| | | newValue: boolean, |
| | | row: MesWmBarcodeConfigApi.BarcodeConfig, |
| | | ) => Promise<boolean>, |
| | | ): VxeTableGridOptions<MesWmBarcodeConfigApi.BarcodeConfig>['columns'] { |
| | |
| | | cellRender: { |
| | | name: 'CellSwitch', |
| | | attrs: { beforeChange: onAutoGenerateChange }, |
| | | props: { checkedValue: true, unCheckedValue: false }, |
| | | }, |
| | | }, |
| | | { |
| | |
| | | |
| | | /** 跳转æ¡ç é
ç½® */ |
| | | function handleConfig() { |
| | | router.push({ path: '/mes/wm/barcode/config' }); |
| | | router.push({ path: '/wls/barcode/config' }); |
| | | } |
| | | |
| | | /** æå¼å®æ´æ¹æ¬¡è¿½æº¯æ¸
å */ |
| | | function handleTraceCenter() { |
| | | router.push({ path: '/mes/qc/batch-trace' }); |
| | | router.push({ path: '/qc/batch-trace' }); |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | |
| | | <OutStockModalRef @success="handleRefresh" /> |
| | | <SnDetailModal ref="snDetailModalRef" /> |
| | | |
| | | <Tabs v-model:active-key="activeKey" class="px-4 pt-1" type="card"> |
| | | <Tabs.TabPane key="group" tab="SN ç åç»"> |
| | | <div class="sn-page flex h-full min-h-0 flex-col"> |
| | | <Tabs |
| | | v-model:active-key="activeKey" |
| | | class="sn-tabs min-h-0 flex-1 px-4 pt-1" |
| | | type="card" |
| | | > |
| | | <Tabs.TabPane key="group" class="min-h-0" tab="SN ç åç»"> |
| | | <div class="h-full min-h-0"> |
| | | <Grid table-title="SN ç å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </div> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="in-stock" tab="å¨åºåä»¶"> |
| | | <Tabs.TabPane key="in-stock" class="min-h-0" tab="å¨åºåä»¶"> |
| | | <div class="h-full min-h-0"> |
| | | <InStockGrid table-title="SN åä»¶ç¶æå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | |
| | | /> |
| | | </template> |
| | | </InStockGrid> |
| | | </div> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | </div> |
| | | </Page> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .sn-tabs :deep(.ant-tabs-content-holder) { |
| | | min-height: 0; |
| | | flex: 1; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .sn-tabs :deep(.ant-tabs-content), |
| | | .sn-tabs :deep(.ant-tabs-tabpane) { |
| | | height: 100%; |
| | | min-height: 0; |
| | | } |
| | | </style> |