| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace AfterSaleCompensationApi { |
| | | /** å®åèµä»/æå¡è¡¥å¿å */ |
| | | export interface Compensation { |
| | | id?: number; |
| | | compensationNo?: string; // èµä»åå· |
| | | compensationType?: number; // èµä»ç±»åï¼1æ
éèµä» 2æå¡è¡¥å¿ |
| | | ticketId?: number; // å
³èå®åå·¥åç¼å· |
| | | customerId?: number; // 客æ·ç¼å· |
| | | customerName?: string; // 客æ·åç§° |
| | | saleOrderId?: number; // å
³èéå®è®¢åç¼å· |
| | | amount?: number; // èµä»éé¢ |
| | | payee?: string; // èµä»å¯¹è±¡ |
| | | reason?: string; // èµä»åå |
| | | auditStatus?: number; // 审æ¹ç¶æï¼0æªæäº¤ 10审æ¹ä¸ 20å®¡æ ¸éè¿ 30å®¡æ ¸ä¸éè¿ 40已忶 |
| | | auditStatusName?: string; // 审æ¹ç¶æåç§° |
| | | processInstanceId?: string; // å®¡æ¹æµç¨å®ä¾ç¼å· |
| | | auditUserId?: number; // å®¡æ ¸äºº |
| | | auditTime?: string; // å®¡æ ¸æ¶é´ |
| | | auditRemark?: string; // å®¡æ ¸å¤æ³¨ |
| | | payStatus?: number; // æ¯ä»ç¶æï¼è´¦å¡èå¨é¢çï¼ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** 审æ¹åæ° */ |
| | | export interface AuditReq { |
| | | id: number; |
| | | auditStatus: number; // 20éè¿ 30驳å |
| | | remark?: string; |
| | | } |
| | | } |
| | | |
| | | /** å建èµä»/æå¡è¡¥å¿å */ |
| | | export function createCompensation(data: AfterSaleCompensationApi.Compensation) { |
| | | return requestClient.post<number>('/aftersales/compensation/create', data); |
| | | } |
| | | |
| | | /** æ´æ°èµä»/æå¡è¡¥å¿å */ |
| | | export function updateCompensation(data: AfterSaleCompensationApi.Compensation) { |
| | | return requestClient.put<boolean>('/aftersales/compensation/update', data); |
| | | } |
| | | |
| | | /** å é¤èµä»/æå¡è¡¥å¿å */ |
| | | export function deleteCompensation(id: number) { |
| | | return requestClient.delete<boolean>('/aftersales/compensation/delete', { |
| | | params: { id }, |
| | | }); |
| | | } |
| | | |
| | | /** è·åèµä»/æå¡è¡¥å¿å */ |
| | | export function getCompensation(id: number) { |
| | | return requestClient.get<AfterSaleCompensationApi.Compensation>( |
| | | `/aftersales/compensation/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åèµä»/æå¡è¡¥å¿åå页 */ |
| | | export function getCompensationPage(params: PageParam) { |
| | | return requestClient.get<PageResult<AfterSaleCompensationApi.Compensation>>( |
| | | '/aftersales/compensation/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æäº¤èµä»/æå¡è¡¥å¿åå®¡æ¹ */ |
| | | export function submitCompensation(id: number, processDefinitionKey: string) { |
| | | return requestClient.put<boolean>('/aftersales/compensation/submit', null, { |
| | | params: { id, processDefinitionKey }, |
| | | }); |
| | | } |
| | | |
| | | /** 审æ¹èµä»/æå¡è¡¥å¿å */ |
| | | export function auditCompensation(data: AfterSaleCompensationApi.AuditReq) { |
| | | return requestClient.put<boolean>('/aftersales/compensation/audit', data); |
| | | } |
| | | |
| | | /** è·åèµä»/æå¡è¡¥å¿åå®¡æ¹æµç¨å®ä¹å表 */ |
| | | export function getApproveProcessList() { |
| | | return requestClient.get<{ id: string; name: string; version: number; key: string }[]>( |
| | | '/aftersales/compensation/approve-process-list', |
| | | ); |
| | | } |
| | |
| | | return requestClient.post('/bi/decision/forecast/generate', null, { |
| | | params: { forecastCode }, |
| | | }); |
| | | } |
| | | |
| | | export function generateForecastWorkOrder(data: { |
| | | forecastId: number; |
| | | workOrderName?: string; |
| | | workOrderType: number; |
| | | requestDate?: string; |
| | | quantity: number; |
| | | remark?: string; |
| | | }) { |
| | | return requestClient.post<number>('/bi/decision/forecast/generate-work-order', data); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmPerformanceAppraiseApi { |
| | | /** èæ ¸ææ */ |
| | | export interface AppraiseItem { |
| | | id?: number; |
| | | appraiseId?: number; |
| | | itemName?: string; // ææ åç§° |
| | | weight?: number; // æé |
| | | scoringStandard?: string; // è¯åæ å |
| | | scorerType?: number; // è¯å人类åï¼1èªè¯ 2ä¸çº§è¯ |
| | | sort?: number; // æåº |
| | | /** è¯åæ¶åå¡«ç¨ */ |
| | | selfScore?: number; |
| | | superiorScore?: number; |
| | | } |
| | | |
| | | /** èæ ¸ç»æææ æç» */ |
| | | export interface ResultItem { |
| | | id?: number; |
| | | resultId?: number; |
| | | itemId?: number; |
| | | itemName?: string; |
| | | weight?: number; |
| | | selfScore?: number; |
| | | superiorScore?: number; |
| | | } |
| | | |
| | | /** 绩æèæ ¸æ¹æ¡ */ |
| | | export interface Appraise { |
| | | id?: number; |
| | | name?: string; // æ¹æ¡åç§° |
| | | periodType?: number; // èæ ¸å¨æç±»åï¼1æåº¦ 2å£åº¦ 3年度 |
| | | period?: string; // èæ ¸å¨ææè¿° |
| | | scopeType?: number; // èæ ¸èå´ç±»åï¼1å
¨é¨åå·¥ 2æå®çç» |
| | | status?: number; // æ¹æ¡ç¶æï¼0è稿 10å·²åå¸ 20è¿è¡ä¸ 30已宿 |
| | | statusName?: string; |
| | | auditStatus?: number; // 审æ¹ç¶æï¼0æªæäº¤ 10审æ¹ä¸ 20éè¿ 30驳å 40已忶 |
| | | auditStatusName?: string; |
| | | processInstanceId?: string; |
| | | auditUserId?: number; |
| | | auditTime?: string; |
| | | auditRemark?: string; |
| | | remark?: string; |
| | | itemCount?: number; // èæ ¸ææ æ° |
| | | resultCount?: number; // å·²è¯åäººæ° |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** èæ ¸ç»æï¼æåå·¥èåï¼ */ |
| | | export interface AppraiseResult { |
| | | id?: number; |
| | | appraiseId?: number; |
| | | employeeId?: number; |
| | | employeeName?: string; |
| | | selfScore?: number; // èªè¯æ»å |
| | | superiorScore?: number; // ä¸çº§è¯æ»å |
| | | finalScore?: number; // 综åå¾å |
| | | grade?: number; // 档级ï¼1ä¼ç§ 2è¯å¥½ 3åæ ¼ 4å¾
æ¹è¿ |
| | | comment?: string; // è¯è¯ |
| | | items?: ResultItem[]; // ææ è¯åæç» |
| | | } |
| | | } |
| | | |
| | | /** å建绩æèæ ¸æ¹æ¡ */ |
| | | export function createAppraise(data: HrmPerformanceAppraiseApi.Appraise) { |
| | | return requestClient.post<number>('/hrm/performance-appraise/create', data); |
| | | } |
| | | |
| | | /** æ´æ°ç»©æèæ ¸æ¹æ¡ */ |
| | | export function updateAppraise(data: HrmPerformanceAppraiseApi.Appraise) { |
| | | return requestClient.put<boolean>('/hrm/performance-appraise/update', data); |
| | | } |
| | | |
| | | /** å é¤ç»©æèæ ¸æ¹æ¡ */ |
| | | export function deleteAppraise(ids: number[]) { |
| | | return requestClient.delete<boolean>('/hrm/performance-appraise/delete', { |
| | | params: { ids: ids.join(',') }, |
| | | }); |
| | | } |
| | | |
| | | /** è·å绩æèæ ¸æ¹æ¡ */ |
| | | export function getAppraise(id: number) { |
| | | return requestClient.get<HrmPerformanceAppraiseApi.Appraise>( |
| | | `/hrm/performance-appraise/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·å绩æèæ ¸æ¹æ¡å页 */ |
| | | export function getAppraisePage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmPerformanceAppraiseApi.Appraise>>( |
| | | '/hrm/performance-appraise/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** è·åèæ ¸ææ å表 */ |
| | | export function getAppraiseItemList(appraiseId: number) { |
| | | return requestClient.get<HrmPerformanceAppraiseApi.AppraiseItem[]>( |
| | | `/hrm/performance-appraise/item-list?appraiseId=${appraiseId}`, |
| | | ); |
| | | } |
| | | |
| | | /** ä¿åèæ ¸ææ ï¼æ´è¡¨æ¿æ¢ï¼ */ |
| | | export function saveAppraiseItems(data: { |
| | | appraiseId: number; |
| | | items: HrmPerformanceAppraiseApi.AppraiseItem[]; |
| | | }) { |
| | | return requestClient.post<boolean>('/hrm/performance-appraise/item-save', data); |
| | | } |
| | | |
| | | /** æäº¤ç»©æèæ ¸æ¹æ¡å®¡æ¹ */ |
| | | export function submitAppraise(id: number, processDefinitionKey: string) { |
| | | return requestClient.post<boolean>('/hrm/performance-appraise/submit', null, { |
| | | params: { id, processDefinitionKey }, |
| | | }); |
| | | } |
| | | |
| | | /** 审æ¹ç»©æèæ ¸æ¹æ¡ */ |
| | | export function auditAppraise(data: { id: number; auditStatus: number; remark?: string }) { |
| | | return requestClient.put<boolean>('/hrm/performance-appraise/audit', data); |
| | | } |
| | | |
| | | /** è·å绩æèæ ¸æ¹æ¡å®¡æ¹æµç¨å®ä¹å表 */ |
| | | export function getAppraiseProcessList() { |
| | | return requestClient.get<{ id: string; name: string; version: number; key: string }[]>( |
| | | '/hrm/performance-appraise/approve-process-list', |
| | | ); |
| | | } |
| | | |
| | | /** è·åèæ ¸ç»æå页 */ |
| | | export function getAppraiseResultPage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmPerformanceAppraiseApi.AppraiseResult>>( |
| | | '/hrm/performance-appraise/result-page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** ä¿ååå·¥èæ ¸ç»æï¼èªå¨è®¡ç®ç»¼åå¾å䏿¡£çº§ï¼ */ |
| | | export function saveAppraiseResult(data: { |
| | | appraiseId: number; |
| | | employeeId: number; |
| | | comment?: string; |
| | | items: { itemId: number; selfScore?: number; superiorScore?: number }[]; |
| | | }) { |
| | | return requestClient.post<boolean>('/hrm/performance-appraise/result-save', data); |
| | | } |
| | | |
| | | /** è·ååå·¥èæ ¸ç»æï¼å«ææ æç»ï¼ */ |
| | | export function getAppraiseResult(appraiseId: number, employeeId: number) { |
| | | return requestClient.get<HrmPerformanceAppraiseApi.AppraiseResult>( |
| | | `/hrm/performance-appraise/result?appraiseId=${appraiseId}&employeeId=${employeeId}`, |
| | | ); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmWorkHourStatisticsApi { |
| | | /** å工工æ¶ï¼èå¤å£å¾ï¼ */ |
| | | export interface EmployeeWorkHour { |
| | | userId?: number; |
| | | userName?: string; |
| | | deptId?: number; |
| | | deptName?: string; |
| | | workDays?: number; // åºå¤å¤©æ° |
| | | workHours?: number; // æ£å¸¸å·¥æ¶ï¼äººæ¶ï¼ |
| | | overtimeHours?: number; // å çå·¥æ¶ï¼äººæ¶ï¼ |
| | | } |
| | | |
| | | /** çç»å·¥æ¶ï¼æ¥å·¥å£å¾ï¼ */ |
| | | export interface TeamWorkHour { |
| | | teamId?: number; |
| | | teamName?: string; |
| | | taskCount?: number; // 任塿° |
| | | totalDuration?: number; // æ»ç产æ¶é¿ï¼äººæ¥ï¼ |
| | | workHours?: number; // æ»å·¥æ¶ï¼äººæ¶ï¼ |
| | | } |
| | | |
| | | export interface EmployeeWorkHourReq { |
| | | userId?: number; |
| | | deptId?: number; |
| | | startDate?: string; |
| | | endDate?: string; |
| | | } |
| | | |
| | | export interface TeamWorkHourReq { |
| | | startTime?: string; |
| | | endTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** å工工æ¶ç»è®¡ï¼èå¤å£å¾ï¼ */ |
| | | export function getEmployeeWorkHourSummary( |
| | | params: HrmWorkHourStatisticsApi.EmployeeWorkHourReq = {}, |
| | | ) { |
| | | return requestClient.get<HrmWorkHourStatisticsApi.EmployeeWorkHour[]>( |
| | | '/hrm/work-hour-statistics/employee-summary', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** çç»å·¥æ¶ç»è®¡ï¼æ¥å·¥å£å¾ï¼è·¨æ¨¡åèå MESï¼ */ |
| | | export function getTeamWorkHourSummary( |
| | | params: HrmWorkHourStatisticsApi.TeamWorkHourReq = {}, |
| | | ) { |
| | | return requestClient.get<HrmWorkHourStatisticsApi.TeamWorkHour[]>( |
| | | '/hrm/work-hour-statistics/team-summary', |
| | | { params }, |
| | | ); |
| | | } |
| | |
| | | tbDeviceId?: string; |
| | | deviceName?: string; |
| | | } |
| | | |
| | | /** ä¾çµåæ°æ¯å¯¹æ ¡éªç»è®¡ï¼æè®¾å¤èåå¼å¸¸çï¼ */ |
| | | export interface CheckSummary { |
| | | tbDeviceId?: string; // æ°é设å¤ID |
| | | deviceName?: string; // 设å¤åç§° |
| | | totalCount?: number; // åæ°æ»æ° |
| | | normalCount?: number; // æ£å¸¸æ°é |
| | | anomalyCount?: number; // å¼å¸¸æ°é |
| | | anomalyRate?: number; // å¼å¸¸ç(%) |
| | | } |
| | | } |
| | | |
| | | /** æåæ°éæ°æ®å¹¶å
¥åºï¼è¿ 5 åéè®¾å¤æ°æ®ï¼ */ |
| | |
| | | '/mes/dv/telemetry/device-list', |
| | | ); |
| | | } |
| | | |
| | | /** ä¾çµåæ°æ¯å¯¹æ ¡éªæç»ï¼åè®¾å¤ææ°åæ° vs æ åå¼ï¼ */ |
| | | export function getTelemetryCheckList( |
| | | params: MesDvTelemetryApi.LatestReq = {}, |
| | | ) { |
| | | return requestClient.get<MesDvTelemetryApi.Telemetry[]>( |
| | | '/mes/dv/telemetry/check', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** ä¾çµåæ°æ¯å¯¹æ ¡éªç»è®¡ï¼æè®¾å¤èåå¼å¸¸çï¼ */ |
| | | export function getTelemetryCheckSummary( |
| | | params: MesDvTelemetryApi.LatestReq = {}, |
| | | ) { |
| | | return requestClient.get<MesDvTelemetryApi.CheckSummary[]>( |
| | | '/mes/dv/telemetry/check-summary', |
| | | { params }, |
| | | ); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesProAlternativePlanApi { |
| | | /** MES ä½ä¸æ¿ä»£æ¹æ¡ */ |
| | | export interface AlternativePlan { |
| | | id?: number; |
| | | code?: string; // æ¹æ¡ç¼ç |
| | | name?: string; // æ¹æ¡åç§° |
| | | mainProcessId?: number; // 主工åºç¼å· |
| | | mainProcessName?: string; // 主工åºåç§° |
| | | altProcessId?: number; // æ¿ä»£å·¥åºç¼å· |
| | | altProcessName?: string; // æ¿ä»£å·¥åºåç§° |
| | | altCondition?: string; // æ¿ä»£æ¡ä»¶ |
| | | scope?: string; // éç¨èå´ |
| | | status?: number; // ç¶æï¼0è稿 10å·²åå¸ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** å建ä½ä¸æ¿ä»£æ¹æ¡ */ |
| | | export function createPlan(data: MesProAlternativePlanApi.AlternativePlan) { |
| | | return requestClient.post('/mes/pro/alternative-plan/create', data); |
| | | } |
| | | |
| | | /** æ´æ°ä½ä¸æ¿ä»£æ¹æ¡ */ |
| | | export function updatePlan(data: MesProAlternativePlanApi.AlternativePlan) { |
| | | return requestClient.put('/mes/pro/alternative-plan/update', data); |
| | | } |
| | | |
| | | /** å é¤ä½ä¸æ¿ä»£æ¹æ¡ */ |
| | | export function deletePlan(ids: number[]) { |
| | | return requestClient.delete('/mes/pro/alternative-plan/delete', { |
| | | params: { ids: ids.join(',') }, |
| | | }); |
| | | } |
| | | |
| | | /** è·åä½ä¸æ¿ä»£æ¹æ¡ */ |
| | | export function getPlan(id: number) { |
| | | return requestClient.get<MesProAlternativePlanApi.AlternativePlan>( |
| | | `/mes/pro/alternative-plan/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åä½ä¸æ¿ä»£æ¹æ¡å页 */ |
| | | export function getPlanPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesProAlternativePlanApi.AlternativePlan>>( |
| | | '/mes/pro/alternative-plan/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** åå¸ä½ä¸æ¿ä»£æ¹æ¡ */ |
| | | export function publishPlan(id: number) { |
| | | return requestClient.put(`/mes/pro/alternative-plan/publish?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesProMaintenanceCalendarApi { |
| | | /** MES çµåè¿ç»´æ¥å */ |
| | | export interface MaintenanceCalendar { |
| | | id?: number; |
| | | code?: string; // æ¥åç¼ç |
| | | name?: string; // æ¥ååç§° |
| | | workDate?: string; // ä½ä¸æ¥æ |
| | | shift?: number; // çæ¬¡ |
| | | workType?: number; // ä½ä¸ç±»åï¼1åçµè¿ç»´ 2çº¿è·¯å·¡æ£ 3åçµè¿ç»´ 4å¨è½å
æ¾çµ 5çµç½è°åº¦ |
| | | region?: string; // ä½ä¸åºå/线路 |
| | | teamId?: number; // å¼ççç»ç¼å· |
| | | teamName?: string; // å¼ççç»åç§° |
| | | personnel?: string; // å¼ç人å |
| | | status?: number; // ç¶æï¼0è稿 10å·²åå¸ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** å建çµåè¿ç»´æ¥å */ |
| | | export function createCalendar(data: MesProMaintenanceCalendarApi.MaintenanceCalendar) { |
| | | return requestClient.post('/mes/pro/maintenance-calendar/create', data); |
| | | } |
| | | |
| | | /** æ´æ°çµåè¿ç»´æ¥å */ |
| | | export function updateCalendar(data: MesProMaintenanceCalendarApi.MaintenanceCalendar) { |
| | | return requestClient.put('/mes/pro/maintenance-calendar/update', data); |
| | | } |
| | | |
| | | /** å é¤çµåè¿ç»´æ¥å */ |
| | | export function deleteCalendar(ids: number[]) { |
| | | return requestClient.delete('/mes/pro/maintenance-calendar/delete', { |
| | | params: { ids: ids.join(',') }, |
| | | }); |
| | | } |
| | | |
| | | /** è·åçµåè¿ç»´æ¥å */ |
| | | export function getCalendar(id: number) { |
| | | return requestClient.get<MesProMaintenanceCalendarApi.MaintenanceCalendar>( |
| | | `/mes/pro/maintenance-calendar/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åçµåè¿ç»´æ¥åå页 */ |
| | | export function getCalendarPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesProMaintenanceCalendarApi.MaintenanceCalendar>>( |
| | | '/mes/pro/maintenance-calendar/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** åå¸çµåè¿ç»´æ¥å */ |
| | | export function publishCalendar(id: number) { |
| | | return requestClient.put(`/mes/pro/maintenance-calendar/publish?id=${id}`); |
| | | } |
| | |
| | | AFTERSALE_ISSUE_TYPE: 'aftersale_issue_type', // å®åé®é¢ç±»å |
| | | AFTERSALE_RETURN_TYPE: 'aftersale_return_type', // å®åéè´§ç±»å |
| | | AFTERSALE_SEVERITY_LEVEL: 'aftersale_severity_level', // å®å严éç¨åº¦ |
| | | AFTER_SALE_COMPENSATION_TYPE: 'after_sale_compensation_type', // å®åèµä»ç±»å |
| | | } as const; |
| | | |
| | | /** ========== ERP - ä¼ä¸èµæºè®¡å模å ========== */ |
| | |
| | | MES_QC_OUTSOURCE_TEST_RESULT: 'mes_qc_outsource_test_result', // MES å§å¤æ£æµç»æ |
| | | MES_PRO_PROCESS_TYPE: 'mes_pro_process_type', // MES çµåå·¥èºåç±» |
| | | MES_PRO_EMERGENCY_PLAN_TYPE: 'mes_pro_emergency_plan_type', // MES åºæ¥å¤ç½®é¢æ¡ç±»å |
| | | MES_PRO_CALENDAR_WORK_TYPE: 'mes_pro_calendar_work_type', // MES çµåè¿ç»´ä½ä¸ç±»å |
| | | MES_MD_AUTO_CODE_CYCLE_METHOD: 'mes_md_auto_code_cycle_method', // MES ç¼ç è§åå¾ªç¯æ¹å¼ |
| | | MES_MD_AUTO_CODE_PADDED_METHOD: 'mes_md_auto_code_padded_method', // MES ç¼ç è§åè¡¥é½æ¹å¼ |
| | | MES_MD_AUTO_CODE_PART_TYPE: 'mes_md_auto_code_part_type', // MES ç¼ç è§ååæ®µç±»å |
| | |
| | | HRM_CONTRACT_TERM_TYPE: 'hrm_contract_term_type', // HRM ååæéç±»å |
| | | HRM_CONTRACT_STATUS: 'hrm_contract_status', // HRM ååç¶æ |
| | | HRM_CONTRACT_TERMINATE_STATUS: 'hrm_contract_terminate_status', // HRM ååè§£é¤ç»æ¢ç¶æ |
| | | HRM_PERFORMANCE_PERIOD_TYPE: 'hrm_performance_period_type', // HRM 绩æèæ ¸å¨æç±»å |
| | | HRM_PERFORMANCE_SCOPE_TYPE: 'hrm_performance_scope_type', // HRM 绩æèæ ¸èå´ç±»å |
| | | HRM_PERFORMANCE_SCORER_TYPE: 'hrm_performance_scorer_type', // HRM 绩æèæ ¸è¯å人类å |
| | | HRM_PERFORMANCE_GRADE: 'hrm_performance_grade', // HRM 绩æèæ ¸æ¡£çº§ |
| | | } as const; |
| | | |
| | | /** åå
¸ç±»åæä¸¾ - ç»ä¸å¯¼åº */ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | import AfterSaleTicketSelect from './modules/ticket-select.vue'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** 审æ¹ç¶æå¸¸é */ |
| | | export const AUDIT_STATUS = { |
| | | DRAFT: 0, |
| | | PROCESS: 10, |
| | | APPROVE: 20, |
| | | REJECT: 30, |
| | | CANCEL: 40, |
| | | }; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType, formApi?: VbenFormApi): VbenFormSchema[] { |
| | | const isReadonly = formType === 'detail'; |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'compensationNo', |
| | | label: 'èµä»åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ç³»ç»èªå¨çæ', |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'compensationType', |
| | | label: 'èµä»ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.AFTER_SALE_COMPENSATION_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©èµä»ç±»å', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'ticketId', |
| | | label: 'å
³èå®åå·¥å', |
| | | component: markRaw(AfterSaleTicketSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®åå·¥å', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'saleOrderId', |
| | | label: 'å
³èéå®è®¢å', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 0, |
| | | placeholder: '请è¾å
¥éå®è®¢åç¼å·', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'amount', |
| | | label: 'èµä»éé¢', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥èµä»éé¢', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'payee', |
| | | label: 'èµä»å¯¹è±¡', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'å¦ï¼å¼ ä¸', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reason', |
| | | label: 'èµä»åå ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥èµä»åå ', |
| | | rows: 3, |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: isReadonly, |
| | | }, |
| | | formItemClass: 'col-span-3', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'compensationNo', |
| | | label: 'èµä»åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥èµä»åå·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'compensationType', |
| | | label: 'èµä»ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.AFTER_SALE_COMPENSATION_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©èµä»ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'auditStatus', |
| | | label: '审æ¹ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'æªæäº¤', value: AUDIT_STATUS.DRAFT }, |
| | | { label: '审æ¹ä¸', value: AUDIT_STATUS.PROCESS }, |
| | | { label: 'å®¡æ ¸éè¿', value: AUDIT_STATUS.APPROVE }, |
| | | { label: 'å®¡æ ¸ä¸éè¿', value: AUDIT_STATUS.REJECT }, |
| | | { label: '已忶', value: AUDIT_STATUS.CANCEL }, |
| | | ], |
| | | placeholder: 'è¯·éæ©å®¡æ¹ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çè¡¨æ ¼å */ |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { type: 'seq', title: 'åºå·', width: 60 }, |
| | | { |
| | | field: 'compensationNo', |
| | | title: 'èµä»åå·', |
| | | minWidth: 160, |
| | | slots: { default: 'compensationNo' }, |
| | | }, |
| | | { |
| | | field: 'compensationType', |
| | | title: 'èµä»ç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.AFTER_SALE_COMPENSATION_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'customerName', |
| | | title: '客æ·åç§°', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'amount', |
| | | title: 'èµä»éé¢', |
| | | width: 110, |
| | | }, |
| | | { |
| | | field: 'payee', |
| | | title: 'èµä»å¯¹è±¡', |
| | | minWidth: 110, |
| | | }, |
| | | { |
| | | field: 'reason', |
| | | title: 'èµä»åå ', |
| | | minWidth: 200, |
| | | showOverflow: true, |
| | | }, |
| | | { |
| | | field: 'auditStatus', |
| | | title: '审æ¹ç¶æ', |
| | | width: 110, |
| | | slots: { default: 'auditStatus' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 260, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { AfterSaleCompensationApi } from '#/api/aftersales/compensation'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { isEmpty } from '@vben/utils'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteCompensation, |
| | | getApproveProcessList, |
| | | getCompensationPage, |
| | | } from '#/api/aftersales/compensation'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { AUDIT_STATUS, useGridColumns, useGridFormSchema } from './data'; |
| | | import AuditModal from './modules/audit-modal.vue'; |
| | | import Form from './modules/form.vue'; |
| | | import ProcessSelectModal from './modules/process-select-modal.vue'; |
| | | |
| | | /** å®åèµä»/æå¡è¡¥å¿åå表 */ |
| | | defineOptions({ name: 'AfterSaleCompensation' }); |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [AuditFormModal, auditModalApi] = useVbenModal({ |
| | | connectedComponent: AuditModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: AfterSaleCompensationApi.Compensation) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleDetail(row: AfterSaleCompensationApi.Compensation) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | async function handleDelete(id: number) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting'), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteCompensation(id); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ - å
è·åæµç¨å表 */ |
| | | async function handleSubmit(row: AfterSaleCompensationApi.Compensation) { |
| | | const hideLoading = message.loading({ |
| | | content: 'è·åå®¡æ¹æµç¨...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const processList = await getApproveProcessList(); |
| | | hideLoading(); |
| | | if (!processList || processList.length === 0) { |
| | | message.warning('ææ å¯ç¨å®¡æ¹æµç¨ï¼è¯·å
é
ç½® BPM æµç¨'); |
| | | return; |
| | | } |
| | | processModalApi.setData({ id: row.id, processList }).open(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 审æ¹ï¼éè¿/驳åï¼ */ |
| | | function handleAudit(row: AfterSaleCompensationApi.Compensation) { |
| | | auditModalApi.setData({ id: row.id }).open(); |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleViewProcess(row: AfterSaleCompensationApi.Compensation) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('è¯¥åæ®å°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ |
| | | name: 'BpmProcessInstanceDetail', |
| | | query: { id: row.processInstanceId }, |
| | | }); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: AfterSaleCompensationApi.Compensation[]; |
| | | }) { |
| | | checkedIds.value = records.map((item) => item.id!); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getCompensationPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<AfterSaleCompensationApi.Compensation>, |
| | | gridEvents: { |
| | | checkboxAll: handleRowCheckboxChange, |
| | | checkboxChange: handleRowCheckboxChange, |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ProcessModal @success="handleRefresh" /> |
| | | <AuditFormModal @success="handleRefresh" /> |
| | | <Grid table-title="èµä»/æå¡è¡¥å¿åå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['èµä»/æå¡è¡¥å¿å']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['aftersales:compensation:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #compensationNo="{ row }"> |
| | | <a class="link" @click="handleDetail(row)">{{ row.compensationNo }}</a> |
| | | </template> |
| | | <template #auditStatus="{ row }"> |
| | | <Tag v-if="row.auditStatus === AUDIT_STATUS.DRAFT" color="default">æªæäº¤</Tag> |
| | | <Tag v-else-if="row.auditStatus === AUDIT_STATUS.PROCESS" color="processing">审æ¹ä¸</Tag> |
| | | <Tag v-else-if="row.auditStatus === AUDIT_STATUS.APPROVE" color="success">å®¡æ ¸éè¿</Tag> |
| | | <Tag v-else-if="row.auditStatus === AUDIT_STATUS.REJECT" color="error">å®¡æ ¸ä¸éè¿</Tag> |
| | | <Tag v-else-if="row.auditStatus === AUDIT_STATUS.CANCEL" color="warning">已忶</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['aftersales:compensation:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['aftersales:compensation:update'], |
| | | ifShow: row.auditStatus === AUDIT_STATUS.DRAFT, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤å®¡æ¹', |
| | | type: 'link', |
| | | auth: ['aftersales:compensation:submit'], |
| | | ifShow: row.auditStatus === AUDIT_STATUS.DRAFT || row.auditStatus === AUDIT_STATUS.REJECT, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | { |
| | | label: '审æ¹', |
| | | type: 'link', |
| | | auth: ['aftersales:compensation:audit'], |
| | | ifShow: row.auditStatus === AUDIT_STATUS.PROCESS, |
| | | onClick: handleAudit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | ifShow: row.auditStatus !== AUDIT_STATUS.DRAFT && !!row.processInstanceId, |
| | | onClick: handleViewProcess.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['aftersales:compensation:delete'], |
| | | ifShow: row.auditStatus === AUDIT_STATUS.DRAFT, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.compensationNo]), |
| | | confirm: handleDelete.bind(null, row.id!), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { auditCompensation } from '#/api/aftersales/compensation'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const compensationId = ref<number>(); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 100, |
| | | }, |
| | | wrapperClass: 'grid-cols-1', |
| | | layout: 'vertical', |
| | | schema: [ |
| | | { |
| | | fieldName: 'auditStatus', |
| | | label: '审æ¹ç»æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'éè¿', value: 20 }, |
| | | { label: '驳å', value: 30 }, |
| | | ], |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '审æ¹å¤æ³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å®¡æ¹å¤æ³¨', |
| | | rows: 3, |
| | | }, |
| | | }, |
| | | ], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const values = await formApi.getValues<{ |
| | | auditStatus: number; |
| | | remark?: string; |
| | | }>(); |
| | | await auditCompensation({ |
| | | id: compensationId.value!, |
| | | auditStatus: values.auditStatus, |
| | | remark: values.remark, |
| | | }); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('审æ¹å®æ'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | compensationId.value = undefined; |
| | | await formApi.resetForm(); |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number }>(); |
| | | compensationId.value = data?.id; |
| | | await formApi.setValues({ auditStatus: 20 }); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="审æ¹èµä»/æå¡è¡¥å¿å" class="w-1/3"> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { AfterSaleCompensationApi } from '#/api/aftersales/compensation'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createCompensation, |
| | | getCompensation, |
| | | updateCompensation, |
| | | } from '#/api/aftersales/compensation'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<AfterSaleCompensationApi.Compensation>(); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | | return $t('ui.actionTitle.create', ['èµä»/æå¡è¡¥å¿å']); |
| | | } else if (formType.value === 'edit') { |
| | | return $t('ui.actionTitle.edit', ['èµä»/æå¡è¡¥å¿å']); |
| | | } else { |
| | | return 'èµä»/æå¡è¡¥å¿å详æ
'; |
| | | } |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 120, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'vertical', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = |
| | | (await formApi.getValues()) as AfterSaleCompensationApi.Compensation; |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createCompensation(data) |
| | | : updateCompensation(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | formApi.updateSchema(useFormSchema(formType.value, formApi)); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getCompensation(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-2/3" :show-confirm-button="formType !== 'detail'"> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message, Select } from 'ant-design-vue'; |
| | | |
| | | import { submitCompensation } from '#/api/aftersales/compensation'; |
| | | |
| | | interface ProcessDefinition { |
| | | id: string; |
| | | name: string; |
| | | version: number; |
| | | key: string; |
| | | } |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const compensationId = ref<number>(); |
| | | const processList = ref<ProcessDefinition[]>([]); |
| | | const selectedProcessKey = ref<string>(''); |
| | | |
| | | const processOptions = computed(() => { |
| | | return processList.value.map((p) => ({ |
| | | label: `${p.name} (V${p.version})`, |
| | | value: p.key, |
| | | })); |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!selectedProcessKey.value) { |
| | | message.warning('è¯·éæ©å®¡æ¹æµç¨'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await submitCompensation(compensationId.value!, selectedProcessKey.value); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æäº¤æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | compensationId.value = undefined; |
| | | selectedProcessKey.value = ''; |
| | | processList.value = []; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number; processList: ProcessDefinition[] }>(); |
| | | if (!data) { |
| | | return; |
| | | } |
| | | compensationId.value = data.id; |
| | | processList.value = data.processList || []; |
| | | if (processList.value.length > 0) { |
| | | selectedProcessKey.value = processList.value[0]?.key ?? ''; |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="éæ©å®¡æ¹æµç¨" class="w-1/3"> |
| | | <div class="mb-4"> |
| | | <span class="mr-2">å®¡æ¹æµç¨ï¼</span> |
| | | <Select |
| | | v-model:value="selectedProcessKey" |
| | | :options="processOptions" |
| | | style="width: 200px" |
| | | placeholder="è¯·éæ©å®¡æ¹æµç¨" |
| | | /> |
| | | </div> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { AftersalesTicketApi } from '#/api/aftersales/ticket'; |
| | | |
| | | import { computed, onMounted, ref } from 'vue'; |
| | | |
| | | import { Select } from 'ant-design-vue'; |
| | | |
| | | import { getTicketPage } from '#/api/aftersales/ticket'; |
| | | |
| | | defineOptions({ name: 'AfterSaleTicketSelect', inheritAttrs: false }); |
| | | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | allowClear?: boolean; |
| | | disabled?: boolean; |
| | | modelValue?: number; |
| | | placeholder?: string; |
| | | }>(), |
| | | { |
| | | allowClear: true, |
| | | disabled: false, |
| | | modelValue: undefined, |
| | | placeholder: 'è¯·éæ©å®åå·¥å', |
| | | }, |
| | | ); |
| | | |
| | | const emit = defineEmits<{ |
| | | 'update:modelValue': [value: number | undefined]; |
| | | }>(); |
| | | |
| | | const ticketList = ref<AftersalesTicketApi.Ticket[]>([]); |
| | | |
| | | const selectValue = computed({ |
| | | get: () => props.modelValue, |
| | | set: (value: number | undefined) => { |
| | | emit('update:modelValue', value); |
| | | }, |
| | | }); |
| | | |
| | | async function loadTickets() { |
| | | const { list } = await getTicketPage({ pageNo: 1, pageSize: 200 }); |
| | | ticketList.value = list || []; |
| | | } |
| | | |
| | | onMounted(loadTickets); |
| | | </script> |
| | | |
| | | <template> |
| | | <Select |
| | | v-bind="$attrs" |
| | | v-model:value="selectValue" |
| | | :allow-clear="allowClear" |
| | | :disabled="disabled" |
| | | :field-names="{ label: 'no', value: 'id' }" |
| | | :options="ticketList" |
| | | :placeholder="placeholder" |
| | | class="w-full" |
| | | option-filter-prop="no" |
| | | show-search |
| | | > |
| | | <template #option="{ value: id, label: no }"> |
| | | <div class="flex items-center gap-2"> |
| | | <span>{{ no }}</span> |
| | | <span class="text-xs text-gray-400"> |
| | | {{ ticketList.find((t) => t.id === id)?.name }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | </Select> |
| | | </template> |
| | |
| | | <script lang="ts" setup> |
| | | import { onMounted, ref } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { DatePicker, Select, Spin } from 'ant-design-vue'; |
| | | import { DatePicker, message, Select, Spin } from 'ant-design-vue'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | import { getForecastList } from '#/api/bi/decision/forecast'; |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | generateForecastWorkOrder, |
| | | getForecastList, |
| | | } from '#/api/bi/decision/forecast'; |
| | | |
| | | defineOptions({ name: 'DecisionForecastAnalysis' }); |
| | | |
| | |
| | | { label: 'æ¥è´è·é¢æµ', value: 'load_forecast_daily' }, |
| | | { label: 'å¨è´è·é¢æµ', value: 'load_forecast_weekly' }, |
| | | { label: 'æè´è·é¢æµ', value: 'load_forecast_monthly' }, |
| | | ]; |
| | | |
| | | const WORK_ORDER_TYPE_OPTIONS = [ |
| | | { label: 'çµåè¿ç»´', value: 4 }, |
| | | { label: 'åçµ', value: 5 }, |
| | | { label: 'æ£ä¿®', value: 6 }, |
| | | ]; |
| | | |
| | | async function loadData() { |
| | |
| | | } |
| | | |
| | | onMounted(loadData); |
| | | |
| | | /** ========== çæå·¥å ========== */ |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 100, |
| | | }, |
| | | wrapperClass: 'grid-cols-1', |
| | | layout: 'vertical', |
| | | schema: [ |
| | | { |
| | | fieldName: 'forecastId', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'workOrderName', |
| | | label: 'å·¥ååç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å·¥ååç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'workOrderType', |
| | | label: 'å·¥åç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: WORK_ORDER_TYPE_OPTIONS, |
| | | placeholder: 'è¯·éæ©å·¥åç±»å', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'requestDate', |
| | | label: 'éæ±æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©éæ±æ¥æ', |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | | label: 'ç产æ°é', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥ç产æ°é', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 3, |
| | | }, |
| | | }, |
| | | ], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const values = await formApi.getValues<{ |
| | | forecastId: number; |
| | | workOrderName?: string; |
| | | workOrderType: number; |
| | | requestDate?: string; |
| | | quantity: number; |
| | | remark?: string; |
| | | }>(); |
| | | await generateForecastWorkOrder({ |
| | | forecastId: Number(values.forecastId), |
| | | workOrderName: values.workOrderName, |
| | | workOrderType: Number(values.workOrderType), |
| | | requestDate: values.requestDate, |
| | | quantity: Number(values.quantity ?? 1), |
| | | remark: values.remark, |
| | | }); |
| | | await modalApi.close(); |
| | | message.success('å·¥åçææå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ item: any }>(); |
| | | const item = data?.item; |
| | | await formApi.resetForm(); |
| | | await formApi.setValues({ |
| | | forecastId: item?.id, |
| | | workOrderType: 4, |
| | | requestDate: item?.pointTime ?? dayjs().format('YYYY-MM-DD HH:mm:ss'), |
| | | quantity: item?.forecastValue ?? 1, |
| | | remark: `ç±è´è·é¢æµï¼${item?.forecastName ?? forecastCode.value}ï¼èªå¨çæ`, |
| | | }); |
| | | }, |
| | | }); |
| | | |
| | | function handleGenerate(item: any) { |
| | | modalApi.setData({ item }).open(); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Page :auto-content-height="true"> |
| | | <Modal title="è´è·é¢æµçæç产工å" class="w-1/2"> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | <div class="p-4"> |
| | | <div class="mb-4 flex items-center gap-4"> |
| | | <h2 class="text-lg font-bold">è´è·é¢æµ</h2> |
| | |
| | | ææ é¢æµæ°æ®ï¼è¯·å
é
ç½® KPI ææ åé¢è¦è§å忥ç |
| | | </div> |
| | | <div v-else class="grid grid-cols-1 gap-4 lg:grid-cols-2"> |
| | | <div v-for="item in forecastData" :key="item.id" class="rounded-lg border p-4"> |
| | | <div |
| | | v-for="item in forecastData" |
| | | :key="item.id" |
| | | class="rounded-lg border p-4" |
| | | > |
| | | <div class="mb-2 flex items-center justify-between"> |
| | | <span class="font-medium">{{ item.forecastName }}</span> |
| | | <span class="text-xs text-gray-400">{{ item.pointTime }}</span> |
| | |
| | | <div v-if="item.dimension" class="mt-1 text-xs text-gray-400"> |
| | | {{ item.dimension }}: {{ item.dimensionValue }} |
| | | </div> |
| | | <div class="mt-3"> |
| | | <a-button |
| | | type="primary" |
| | | ghost |
| | | size="small" |
| | | @click="handleGenerate(item)" |
| | | > |
| | | çæå·¥å |
| | | </a-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </Spin> |
| | | </div> |
| | | </Page> |
| | | </template> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** æ¹æ¡ç¶æå¸¸é */ |
| | | export const APPRAISE_STATUS = { |
| | | DRAFT: 0, |
| | | PUBLISHED: 10, |
| | | PROCESSING: 20, |
| | | FINISHED: 30, |
| | | }; |
| | | |
| | | /** 审æ¹ç¶æå¸¸é */ |
| | | export const AUDIT_STATUS = { |
| | | DRAFT: 0, |
| | | PROCESS: 10, |
| | | APPROVE: 20, |
| | | REJECT: 30, |
| | | CANCEL: 40, |
| | | }; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | const isReadonly = formType === 'detail'; |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¹æ¡åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¹æ¡åç§°', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'periodType', |
| | | label: 'èæ ¸å¨æç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.HRM_PERFORMANCE_PERIOD_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©èæ ¸å¨æç±»å', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'period', |
| | | label: 'èæ ¸å¨æ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'å¦ï¼2026-08', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'scopeType', |
| | | label: 'èæ ¸èå´', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.HRM_PERFORMANCE_SCOPE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©èæ ¸èå´', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¹æ¡åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¹æ¡åç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'periodType', |
| | | label: 'èæ ¸å¨æç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_PERFORMANCE_PERIOD_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©èæ ¸å¨æç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'æ¹æ¡ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'è稿', value: APPRAISE_STATUS.DRAFT }, |
| | | { label: 'å·²åå¸', value: APPRAISE_STATUS.PUBLISHED }, |
| | | { label: 'è¿è¡ä¸', value: APPRAISE_STATUS.PROCESSING }, |
| | | { label: '已宿', value: APPRAISE_STATUS.FINISHED }, |
| | | ], |
| | | placeholder: 'è¯·éæ©æ¹æ¡ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çè¡¨æ ¼å */ |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { type: 'seq', title: 'åºå·', width: 60 }, |
| | | { |
| | | field: 'name', |
| | | title: 'æ¹æ¡åç§°', |
| | | minWidth: 200, |
| | | slots: { default: 'name' }, |
| | | }, |
| | | { |
| | | field: 'periodType', |
| | | title: 'èæ ¸å¨æç±»å', |
| | | width: 120, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_PERFORMANCE_PERIOD_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'period', |
| | | title: 'èæ ¸å¨æ', |
| | | width: 110, |
| | | }, |
| | | { |
| | | field: 'scopeType', |
| | | title: 'èæ ¸èå´', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_PERFORMANCE_SCOPE_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'æ¹æ¡ç¶æ', |
| | | width: 100, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'auditStatus', |
| | | title: '审æ¹ç¶æ', |
| | | width: 100, |
| | | slots: { default: 'auditStatus' }, |
| | | }, |
| | | { |
| | | field: 'itemCount', |
| | | title: 'ææ æ°', |
| | | width: 80, |
| | | }, |
| | | { |
| | | field: 'resultCount', |
| | | title: 'å·²è¯å人æ°', |
| | | width: 100, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 320, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmPerformanceAppraiseApi } from '#/api/hrm/performance-appraise'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { isEmpty } from '@vben/utils'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteAppraise, |
| | | getAppraisePage, |
| | | getAppraiseProcessList, |
| | | } from '#/api/hrm/performance-appraise'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { APPRAISE_STATUS, AUDIT_STATUS, useGridColumns, useGridFormSchema } from './data'; |
| | | import AuditModal from './modules/audit-modal.vue'; |
| | | import Form from './modules/form.vue'; |
| | | import ItemModal from './modules/item.vue'; |
| | | import ProcessSelectModal from './modules/process-select-modal.vue'; |
| | | import ResultModal from './modules/result.vue'; |
| | | import ScoreModal from './modules/score.vue'; |
| | | |
| | | /** HRM 绩æèæ ¸æ¹æ¡å表 */ |
| | | defineOptions({ name: 'HrmPerformanceAppraise' }); |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ItemFormModal, itemModalApi] = useVbenModal({ |
| | | connectedComponent: ItemModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ScoreFormModal, scoreModalApi] = useVbenModal({ |
| | | connectedComponent: ScoreModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ResultFormModal, resultModalApi] = useVbenModal({ |
| | | connectedComponent: ResultModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [AuditFormModal, auditModalApi] = useVbenModal({ |
| | | connectedComponent: AuditModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: HrmPerformanceAppraiseApi.Appraise) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleDetail(row: HrmPerformanceAppraiseApi.Appraise) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleItems(row: HrmPerformanceAppraiseApi.Appraise) { |
| | | itemModalApi.setData({ id: row.id }).open(); |
| | | } |
| | | |
| | | function handleScore(row: HrmPerformanceAppraiseApi.Appraise) { |
| | | scoreModalApi.setData({ id: row.id }).open(); |
| | | } |
| | | |
| | | function handleResult(row: HrmPerformanceAppraiseApi.Appraise) { |
| | | resultModalApi.setData({ id: row.id }).open(); |
| | | } |
| | | |
| | | async function handleDelete(ids: number[]) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting'), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteAppraise(ids); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ - å
è·åæµç¨å表 */ |
| | | async function handleSubmit(row: HrmPerformanceAppraiseApi.Appraise) { |
| | | const hideLoading = message.loading({ |
| | | content: 'è·åå®¡æ¹æµç¨...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const processList = await getAppraiseProcessList(); |
| | | hideLoading(); |
| | | if (!processList || processList.length === 0) { |
| | | message.warning('ææ å¯ç¨å®¡æ¹æµç¨ï¼è¯·å
é
ç½® BPM æµç¨'); |
| | | return; |
| | | } |
| | | processModalApi.setData({ id: row.id, processList }).open(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 审æ¹ï¼éè¿/驳åï¼ */ |
| | | function handleAudit(row: HrmPerformanceAppraiseApi.Appraise) { |
| | | auditModalApi.setData({ id: row.id }).open(); |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleViewProcess(row: HrmPerformanceAppraiseApi.Appraise) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('è¯¥æ¹æ¡å°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ |
| | | name: 'BpmProcessInstanceDetail', |
| | | query: { id: row.processInstanceId }, |
| | | }); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: HrmPerformanceAppraiseApi.Appraise[]; |
| | | }) { |
| | | checkedIds.value = records.map((item) => item.id!); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getAppraisePage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmPerformanceAppraiseApi.Appraise>, |
| | | gridEvents: { |
| | | checkboxAll: handleRowCheckboxChange, |
| | | checkboxChange: handleRowCheckboxChange, |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ItemFormModal @success="handleRefresh" /> |
| | | <ScoreFormModal @success="handleRefresh" /> |
| | | <ResultFormModal @success="handleRefresh" /> |
| | | <ProcessModal @success="handleRefresh" /> |
| | | <AuditFormModal @success="handleRefresh" /> |
| | | <Grid table-title="绩æèæ ¸æ¹æ¡å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['绩æèæ ¸æ¹æ¡']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['hrm:performance-appraise:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:performance-appraise:delete'], |
| | | popConfirm: { |
| | | title: `æ¯å¦å 餿é䏿°æ®ï¼`, |
| | | confirm: handleDelete.bind(null, checkedIds), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #name="{ row }"> |
| | | <a class="link" @click="handleDetail(row)">{{ row.name }}</a> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag v-if="row.status === APPRAISE_STATUS.DRAFT" color="default">è稿</Tag> |
| | | <Tag v-else-if="row.status === APPRAISE_STATUS.PUBLISHED" color="processing">å·²åå¸</Tag> |
| | | <Tag v-else-if="row.status === APPRAISE_STATUS.PROCESSING" color="warning">è¿è¡ä¸</Tag> |
| | | <Tag v-else-if="row.status === APPRAISE_STATUS.FINISHED" color="success">已宿</Tag> |
| | | </template> |
| | | <template #auditStatus="{ row }"> |
| | | <Tag v-if="row.auditStatus === AUDIT_STATUS.DRAFT" color="default">æªæäº¤</Tag> |
| | | <Tag v-else-if="row.auditStatus === AUDIT_STATUS.PROCESS" color="processing">审æ¹ä¸</Tag> |
| | | <Tag v-else-if="row.auditStatus === AUDIT_STATUS.APPROVE" color="success">éè¿</Tag> |
| | | <Tag v-else-if="row.auditStatus === AUDIT_STATUS.REJECT" color="error">驳å</Tag> |
| | | <Tag v-else-if="row.auditStatus === AUDIT_STATUS.CANCEL" color="warning">已忶</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'ææ ', |
| | | type: 'link', |
| | | auth: ['hrm:performance-appraise:update'], |
| | | ifShow: row.status === APPRAISE_STATUS.DRAFT, |
| | | onClick: handleItems.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'è¯å', |
| | | type: 'link', |
| | | auth: ['hrm:performance-appraise:update'], |
| | | ifShow: row.status === APPRAISE_STATUS.PUBLISHED || row.status === APPRAISE_STATUS.PROCESSING, |
| | | onClick: handleScore.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'ç»æ', |
| | | type: 'link', |
| | | ifShow: (row.resultCount ?? 0) > 0, |
| | | onClick: handleResult.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:performance-appraise:update'], |
| | | ifShow: row.status === APPRAISE_STATUS.DRAFT, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤å®¡æ¹', |
| | | type: 'link', |
| | | auth: ['hrm:performance-appraise:submit'], |
| | | ifShow: row.auditStatus === AUDIT_STATUS.DRAFT || row.auditStatus === AUDIT_STATUS.REJECT, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | { |
| | | label: '审æ¹', |
| | | type: 'link', |
| | | auth: ['hrm:performance-appraise:audit'], |
| | | ifShow: row.auditStatus === AUDIT_STATUS.PROCESS, |
| | | onClick: handleAudit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | ifShow: row.auditStatus !== AUDIT_STATUS.DRAFT && !!row.processInstanceId, |
| | | onClick: handleViewProcess.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:performance-appraise:delete'], |
| | | ifShow: row.status === APPRAISE_STATUS.DRAFT, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.name]), |
| | | confirm: handleDelete.bind(null, [row.id!]), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { auditAppraise } from '#/api/hrm/performance-appraise'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const appraiseId = ref<number>(); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 100, |
| | | }, |
| | | wrapperClass: 'grid-cols-1', |
| | | layout: 'vertical', |
| | | schema: [ |
| | | { |
| | | fieldName: 'auditStatus', |
| | | label: '审æ¹ç»æ', |
| | | component: 'RadioGroup', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'éè¿', value: 20 }, |
| | | { label: '驳å', value: 30 }, |
| | | ], |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '审æ¹å¤æ³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å®¡æ¹å¤æ³¨', |
| | | rows: 3, |
| | | }, |
| | | }, |
| | | ], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const values = await formApi.getValues<{ |
| | | auditStatus: number; |
| | | remark?: string; |
| | | }>(); |
| | | await auditAppraise({ |
| | | id: appraiseId.value!, |
| | | auditStatus: values.auditStatus, |
| | | remark: values.remark, |
| | | }); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('审æ¹å®æ'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | appraiseId.value = undefined; |
| | | await formApi.resetForm(); |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number }>(); |
| | | appraiseId.value = data?.id; |
| | | await formApi.setValues({ auditStatus: 20 }); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="审æ¹ç»©æèæ ¸æ¹æ¡" class="w-1/3"> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmPerformanceAppraiseApi } from '#/api/hrm/performance-appraise'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createAppraise, |
| | | getAppraise, |
| | | updateAppraise, |
| | | } from '#/api/hrm/performance-appraise'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<HrmPerformanceAppraiseApi.Appraise>(); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | | return $t('ui.actionTitle.create', ['绩æèæ ¸æ¹æ¡']); |
| | | } else if (formType.value === 'edit') { |
| | | return $t('ui.actionTitle.edit', ['绩æèæ ¸æ¹æ¡']); |
| | | } else { |
| | | return '绩æèæ ¸æ¹æ¡è¯¦æ
'; |
| | | } |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 120, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'vertical', |
| | | schema: useFormSchema(formType.value), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = |
| | | (await formApi.getValues()) as HrmPerformanceAppraiseApi.Appraise; |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createAppraise(data) |
| | | : updateAppraise(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | formApi.updateSchema(useFormSchema(formType.value)); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getAppraise(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-2/3" :show-confirm-button="formType !== 'detail'"> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmPerformanceAppraiseApi } from '#/api/hrm/performance-appraise'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | getAppraiseItemList, |
| | | saveAppraiseItems, |
| | | } from '#/api/hrm/performance-appraise'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const appraiseId = ref<number>(); |
| | | const items = ref<HrmPerformanceAppraiseApi.AppraiseItem[]>([]); |
| | | |
| | | const SCORER_TYPE_OPTIONS = [ |
| | | { label: 'èªè¯', value: 1 }, |
| | | { label: 'ä¸çº§è¯', value: 2 }, |
| | | ]; |
| | | |
| | | function addRow() { |
| | | items.value.push({ |
| | | itemName: '', |
| | | weight: undefined, |
| | | scoringStandard: '', |
| | | scorerType: 1, |
| | | sort: items.value.length + 1, |
| | | }); |
| | | } |
| | | |
| | | function removeRow(index: number) { |
| | | items.value.splice(index, 1); |
| | | items.value.forEach((item, idx) => { |
| | | item.sort = idx + 1; |
| | | }); |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | // æ ¡éªï¼ææ åç§°ä¸æéå¿
å¡« |
| | | const invalid = items.value.some( |
| | | (item) => !item.itemName || item.weight == null, |
| | | ); |
| | | if (invalid) { |
| | | message.warning('请填å宿´çææ åç§°ä¸æé'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await saveAppraiseItems({ appraiseId: appraiseId.value!, items: items.value }); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('ææ ä¿åæå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | appraiseId.value = undefined; |
| | | items.value = []; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number }>(); |
| | | appraiseId.value = data?.id; |
| | | modalApi.lock(); |
| | | try { |
| | | items.value = await getAppraiseItemList(appraiseId.value!); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="é
ç½®èæ ¸ææ " class="w-3/4"> |
| | | <div class="mb-3 flex items-center justify-between"> |
| | | <span class="text-sm text-gray-500">è®¾ç½®èæ ¸ææ åç§°ãæéä¸è¯åæ åï¼æéå计建议 100</span> |
| | | <a-button type="primary" @click="addRow">æ·»å ææ </a-button> |
| | | </div> |
| | | <a-table |
| | | :data-source="items" |
| | | :pagination="false" |
| | | :scroll="{ y: 360 }" |
| | | bordered |
| | | row-key="sort" |
| | | size="small" |
| | | > |
| | | <a-table-column title="ææ åç§°" width="220"> |
| | | <template #default="{ record }"> |
| | | <a-input |
| | | v-model:value="record.itemName" |
| | | placeholder="请è¾å
¥ææ åç§°" |
| | | /> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="æé" width="120"> |
| | | <template #default="{ record }"> |
| | | <a-input-number |
| | | v-model:value="record.weight" |
| | | :min="0" |
| | | :precision="2" |
| | | class="!w-full" |
| | | placeholder="æé" |
| | | /> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="è¯åæ å" min-width="220"> |
| | | <template #default="{ record }"> |
| | | <a-input |
| | | v-model:value="record.scoringStandard" |
| | | placeholder="请è¾å
¥è¯åæ å" |
| | | /> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="è¯å人" width="120"> |
| | | <template #default="{ record }"> |
| | | <a-select |
| | | v-model:value="record.scorerType" |
| | | :options="SCORER_TYPE_OPTIONS" |
| | | class="!w-full" |
| | | /> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="æåº" width="90"> |
| | | <template #default="{ record }"> |
| | | <a-input-number |
| | | v-model:value="record.sort" |
| | | :min="1" |
| | | :precision="0" |
| | | class="!w-full" |
| | | /> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="æä½" width="80" align="center"> |
| | | <template #default="{ index }"> |
| | | <a-button type="link" danger @click="removeRow(index)">å é¤</a-button> |
| | | </template> |
| | | </a-table-column> |
| | | </a-table> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message, Select } from 'ant-design-vue'; |
| | | |
| | | import { submitAppraise } from '#/api/hrm/performance-appraise'; |
| | | |
| | | interface ProcessDefinition { |
| | | id: string; |
| | | name: string; |
| | | version: number; |
| | | key: string; |
| | | } |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const appraiseId = ref<number>(); |
| | | const processList = ref<ProcessDefinition[]>([]); |
| | | const selectedProcessKey = ref<string>(''); |
| | | |
| | | const processOptions = computed(() => { |
| | | return processList.value.map((p) => ({ |
| | | label: `${p.name} (V${p.version})`, |
| | | value: p.key, |
| | | })); |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!selectedProcessKey.value) { |
| | | message.warning('è¯·éæ©å®¡æ¹æµç¨'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await submitAppraise(appraiseId.value!, selectedProcessKey.value); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æäº¤æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | appraiseId.value = undefined; |
| | | selectedProcessKey.value = ''; |
| | | processList.value = []; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number; processList: ProcessDefinition[] }>(); |
| | | if (!data) { |
| | | return; |
| | | } |
| | | appraiseId.value = data.id; |
| | | processList.value = data.processList || []; |
| | | if (processList.value.length > 0) { |
| | | selectedProcessKey.value = processList.value[0]?.key ?? ''; |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="éæ©å®¡æ¹æµç¨" class="w-1/3"> |
| | | <div class="mb-4"> |
| | | <span class="mr-2">å®¡æ¹æµç¨ï¼</span> |
| | | <Select |
| | | v-model:value="selectedProcessKey" |
| | | :options="processOptions" |
| | | style="width: 200px" |
| | | placeholder="è¯·éæ©å®¡æ¹æµç¨" |
| | | /> |
| | | </div> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmPerformanceAppraiseApi } from '#/api/hrm/performance-appraise'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { Modal } from 'ant-design-vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictLabel } from '@vben/hooks'; |
| | | |
| | | import { getAppraiseResultPage } from '#/api/hrm/performance-appraise'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const appraiseId = ref<number>(); |
| | | const resultList = ref<HrmPerformanceAppraiseApi.AppraiseResult[]>([]); |
| | | const total = ref(0); |
| | | const pageNo = ref(1); |
| | | const pageSize = ref(10); |
| | | const loading = ref(false); |
| | | |
| | | const detailOpen = ref(false); |
| | | const detailRows = ref<HrmPerformanceAppraiseApi.ResultItem[]>([]); |
| | | |
| | | function getGradeLabel(grade?: number): string { |
| | | return grade == null ? '-' : getDictLabel(DICT_TYPE.HRM_PERFORMANCE_GRADE, grade); |
| | | } |
| | | |
| | | async function loadResults() { |
| | | loading.value = true; |
| | | try { |
| | | const data = await getAppraiseResultPage({ |
| | | appraiseId: appraiseId.value!, |
| | | pageNo: pageNo.value, |
| | | pageSize: pageSize.value, |
| | | }); |
| | | resultList.value = data.list ?? []; |
| | | total.value = data.total ?? 0; |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | function handlePageChange(page: number, size: number) { |
| | | pageNo.value = page; |
| | | pageSize.value = size; |
| | | loadResults(); |
| | | } |
| | | |
| | | function handleDetail(row: HrmPerformanceAppraiseApi.AppraiseResult) { |
| | | detailRows.value = row.items ?? []; |
| | | detailOpen.value = true; |
| | | } |
| | | |
| | | const [ModalRoot, modalApi] = useVbenModal({ |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | appraiseId.value = undefined; |
| | | resultList.value = []; |
| | | total.value = 0; |
| | | pageNo.value = 1; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number }>(); |
| | | appraiseId.value = data?.id; |
| | | await loadResults(); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <ModalRoot title="èæ ¸ç»æ" class="w-3/4"> |
| | | <div class="mb-3 text-sm text-gray-500"> |
| | | 综åå¾å = Σ(æé Ã åææ å¾å) / Σæéï¼æ¡£çº§ â¥90 ä¼ç§ / â¥80 è¯å¥½ / â¥60 åæ ¼ / <60 å¾
æ¹è¿ |
| | | </div> |
| | | <a-table |
| | | :data-source="resultList" |
| | | :loading="loading" |
| | | :pagination="{ |
| | | current: pageNo, |
| | | pageSize, |
| | | total, |
| | | showSizeChanger: true, |
| | | showTotal: (t: number) => `å
± ${t} 人`, |
| | | }" |
| | | :scroll="{ y: 380 }" |
| | | bordered |
| | | row-key="id" |
| | | size="small" |
| | | @change="(pag: { current?: number; pageSize?: number }) => |
| | | handlePageChange(pag.current ?? 1, pag.pageSize ?? 10)" |
| | | > |
| | | <a-table-column title="åå·¥" data-index="employeeName" min-width="120" /> |
| | | <a-table-column title="èªè¯æ»å" data-index="selfScore" width="100" /> |
| | | <a-table-column title="ä¸çº§è¯æ»å" data-index="superiorScore" width="110" /> |
| | | <a-table-column title="综åå¾å" data-index="finalScore" width="100"> |
| | | <template #default="{ record }"> |
| | | <span class="font-medium">{{ record.finalScore ?? '-' }}</span> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="档级" width="90"> |
| | | <template #default="{ record }"> |
| | | <a-tag :color="record.grade === 1 ? 'success' : record.grade === 2 ? 'processing' : record.grade === 3 ? 'warning' : 'error'"> |
| | | {{ getGradeLabel(record.grade) }} |
| | | </a-tag> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="è¯è¯" data-index="comment" min-width="160" ellipsis /> |
| | | <a-table-column title="æä½" width="90" align="center"> |
| | | <template #default="{ record }"> |
| | | <a-button type="link" @click="handleDetail(record)">æç»</a-button> |
| | | </template> |
| | | </a-table-column> |
| | | </a-table> |
| | | |
| | | <Modal |
| | | v-model:open="detailOpen" |
| | | title="ææ è¯åæç»" |
| | | width="640px" |
| | | :footer="null" |
| | | > |
| | | <a-table |
| | | :data-source="detailRows" |
| | | :pagination="false" |
| | | bordered |
| | | row-key="id" |
| | | size="small" |
| | | > |
| | | <a-table-column title="ææ åç§°" data-index="itemName" min-width="160" /> |
| | | <a-table-column title="æé" data-index="weight" width="90" /> |
| | | <a-table-column title="èªè¯å¾å" data-index="selfScore" width="100" /> |
| | | <a-table-column title="ä¸çº§è¯å¾å" data-index="superiorScore" width="110" /> |
| | | </a-table> |
| | | </Modal> |
| | | </ModalRoot> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmPerformanceAppraiseApi } from '#/api/hrm/performance-appraise'; |
| | | |
| | | import { markRaw, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | getAppraiseItemList, |
| | | getAppraiseResult, |
| | | saveAppraiseResult, |
| | | } from '#/api/hrm/performance-appraise'; |
| | | import { HrmEmployeeSelect } from '#/views/hrm/employee/components'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const appraiseId = ref<number>(); |
| | | const employeeId = ref<number>(); |
| | | const items = ref<HrmPerformanceAppraiseApi.AppraiseItem[]>([]); |
| | | const comment = ref<string>(''); |
| | | |
| | | const SCORER_TYPE_OPTIONS = [ |
| | | { label: 'èªè¯', value: 1 }, |
| | | { label: 'ä¸çº§è¯', value: 2 }, |
| | | ]; |
| | | |
| | | /** å è½½ææ å表ï¼ä»
å¨é¦æ¬¡æå¼æ¶ï¼ */ |
| | | async function loadItems() { |
| | | items.value = await getAppraiseItemList(appraiseId.value!); |
| | | } |
| | | |
| | | /** 忢åå·¥æ¶åå¡«å·²æè¯å */ |
| | | async function handleEmployeeChange(item: { id?: number } | undefined) { |
| | | employeeId.value = item?.id; |
| | | comment.value = ''; |
| | | if (!item?.id) { |
| | | items.value.forEach((it) => { |
| | | it.selfScore = undefined; |
| | | it.superiorScore = undefined; |
| | | }); |
| | | return; |
| | | } |
| | | const result = await getAppraiseResult(appraiseId.value!, item.id); |
| | | if (result?.items) { |
| | | items.value.forEach((it) => { |
| | | const detail = result.items!.find((ri) => ri.itemId === it.id); |
| | | it.selfScore = detail?.selfScore; |
| | | it.superiorScore = detail?.superiorScore; |
| | | }); |
| | | comment.value = result.comment ?? ''; |
| | | } else { |
| | | items.value.forEach((it) => { |
| | | it.selfScore = undefined; |
| | | it.superiorScore = undefined; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!employeeId.value) { |
| | | message.warning('è¯·éæ©åå·¥'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | await saveAppraiseResult({ |
| | | appraiseId: appraiseId.value!, |
| | | employeeId: employeeId.value!, |
| | | comment: comment.value, |
| | | items: items.value.map((item) => ({ |
| | | itemId: item.id!, |
| | | selfScore: item.selfScore, |
| | | superiorScore: item.superiorScore, |
| | | })), |
| | | }); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('è¯åä¿åæåï¼å·²èªå¨è®¡ç®ç»¼åå¾å'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | appraiseId.value = undefined; |
| | | employeeId.value = undefined; |
| | | items.value = []; |
| | | comment.value = ''; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number }>(); |
| | | appraiseId.value = data?.id; |
| | | modalApi.lock(); |
| | | try { |
| | | await loadItems(); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="åå·¥èæ ¸è¯å" class="w-3/4"> |
| | | <div class="mb-4 flex items-center gap-4"> |
| | | <span class="shrink-0">èæ ¸åå·¥ï¼</span> |
| | | <HrmEmployeeSelect |
| | | :model-value="employeeId" |
| | | class="!w-72" |
| | | @change="handleEmployeeChange" |
| | | @update:model-value="(value: number | undefined) => (employeeId = value)" |
| | | /> |
| | | </div> |
| | | <a-table |
| | | :data-source="items" |
| | | :pagination="false" |
| | | :scroll="{ y: 300 }" |
| | | bordered |
| | | row-key="id" |
| | | size="small" |
| | | > |
| | | <a-table-column title="ææ åç§°" min-width="180"> |
| | | <template #default="{ record }"> |
| | | <span>{{ record.itemName }}</span> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="æé" width="90"> |
| | | <template #default="{ record }"> |
| | | <span>{{ record.weight }}</span> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="è¯å人" width="90"> |
| | | <template #default="{ record }"> |
| | | <span>{{ record.scorerType === 1 ? 'èªè¯' : 'ä¸çº§è¯' }}</span> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="èªè¯å¾å" width="130"> |
| | | <template #default="{ record }"> |
| | | <a-input-number |
| | | v-model:value="record.selfScore" |
| | | :min="0" |
| | | :max="100" |
| | | :precision="1" |
| | | class="!w-full" |
| | | placeholder="èªè¯å¾å" |
| | | /> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="ä¸çº§è¯å¾å" width="130"> |
| | | <template #default="{ record }"> |
| | | <a-input-number |
| | | v-model:value="record.superiorScore" |
| | | :min="0" |
| | | :max="100" |
| | | :precision="1" |
| | | class="!w-full" |
| | | placeholder="ä¸çº§è¯å¾å" |
| | | /> |
| | | </template> |
| | | </a-table-column> |
| | | <a-table-column title="è¯åæ å" min-width="180"> |
| | | <template #default="{ record }"> |
| | | <span class="text-xs text-gray-400">{{ record.scoringStandard || '-' }}</span> |
| | | </template> |
| | | </a-table-column> |
| | | </a-table> |
| | | <div class="mt-3"> |
| | | <span class="mr-2">è¯è¯ï¼</span> |
| | | <a-textarea |
| | | v-model:value="comment" |
| | | :rows="2" |
| | | placeholder="请è¾å
¥è¯è¯" |
| | | class="!w-full" |
| | | /> |
| | | </div> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmWorkHourStatisticsApi } from '#/api/hrm/work-hour-statistics'; |
| | | |
| | | import { onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | |
| | | import { Tabs } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | getEmployeeWorkHourSummary, |
| | | getTeamWorkHourSummary, |
| | | } from '#/api/hrm/work-hour-statistics'; |
| | | |
| | | /** HRM å·¥æ¶ç»è®¡ï¼èå¤å£å¾ + æ¥å·¥å£å¾ï¼ */ |
| | | defineOptions({ name: 'HrmWorkHourStatistics' }); |
| | | |
| | | const activeTab = ref<'employee' | 'team'>('employee'); |
| | | |
| | | const contentRef = useTemplateRef('contentRef'); |
| | | let resizeObserver: ResizeObserver | undefined; |
| | | |
| | | /** 计ç®è¡¨æ ¼é«åº¦ï¼å
容åºå¯ç¨é«åº¦ - Tabs 导èªçåºå®å¼éï¼è¡¨æ ¼ææ»¡é¡µé¢ */ |
| | | function calcTableHeight() { |
| | | const el = contentRef.value; |
| | | if (!el) return; |
| | | const height = Math.max(el.clientHeight - 115, 240); |
| | | employeeGridApi.setGridOptions({ height }); |
| | | teamGridApi.setGridOptions({ height }); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | calcTableHeight(); |
| | | resizeObserver = new ResizeObserver(calcTableHeight); |
| | | if (contentRef.value) resizeObserver.observe(contentRef.value); |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | resizeObserver?.disconnect(); |
| | | }); |
| | | |
| | | const [EmployeeGrid, employeeGridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: [ |
| | | { |
| | | fieldName: 'dateRange', |
| | | label: 'æ¥æèå´', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: ['å¼å§æ¥æ', 'ç»ææ¥æ'], |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | gridOptions: { |
| | | columns: [ |
| | | { field: 'userName', title: 'åå·¥å§å', minWidth: 140 }, |
| | | { field: 'deptName', title: 'é¨é¨', minWidth: 140 }, |
| | | { field: 'workDays', title: 'åºå¤å¤©æ°', width: 110 }, |
| | | { field: 'workHours', title: 'æ£å¸¸å·¥æ¶(人æ¶)', width: 150 }, |
| | | { field: 'overtimeHours', title: 'å çå·¥æ¶(人æ¶)', width: 150 }, |
| | | ], |
| | | height: 400, |
| | | keepSource: true, |
| | | // è¿å裸æ°ç»ï¼éå
³éå页 |
| | | pagerConfig: { enabled: false }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async (_params, formValues) => { |
| | | const range = formValues?.dateRange ?? []; |
| | | return await getEmployeeWorkHourSummary({ |
| | | startDate: range?.[0], |
| | | endDate: range?.[1], |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'userId', isHover: true }, |
| | | toolbarConfig: { refresh: true, search: true }, |
| | | } as VxeTableGridOptions<HrmWorkHourStatisticsApi.EmployeeWorkHour>, |
| | | }); |
| | | |
| | | const [TeamGrid, teamGridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: [ |
| | | { |
| | | fieldName: 'timeRange', |
| | | label: 'æ¥æèå´', |
| | | component: 'RangePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | placeholder: ['å¼å§æ¶é´', 'ç»ææ¶é´'], |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | gridOptions: { |
| | | columns: [ |
| | | { field: 'teamName', title: 'çç»åç§°', minWidth: 160 }, |
| | | { field: 'taskCount', title: '任塿°', width: 100 }, |
| | | { field: 'totalDuration', title: 'æ»ç产æ¶é¿(人æ¥)', width: 160 }, |
| | | { field: 'workHours', title: 'æ»å·¥æ¶(人æ¶)', width: 150 }, |
| | | ], |
| | | height: 400, |
| | | keepSource: true, |
| | | pagerConfig: { enabled: false }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async (_params, formValues) => { |
| | | const range = formValues?.timeRange ?? []; |
| | | return await getTeamWorkHourSummary({ |
| | | startTime: range?.[0], |
| | | endTime: range?.[1], |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'teamId', isHover: true }, |
| | | toolbarConfig: { refresh: true, search: true }, |
| | | } as VxeTableGridOptions<HrmWorkHourStatisticsApi.TeamWorkHour>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <div ref="contentRef" class="flex h-full w-full flex-col"> |
| | | <Tabs v-model:active-key="activeTab" class="min-h-0 flex-1"> |
| | | <Tabs.TabPane key="employee" tab="å工工æ¶ï¼èå¤å£å¾ï¼"> |
| | | <EmployeeGrid table-title="å工工æ¶ç»è®¡" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="team" tab="çç»å·¥æ¶ï¼æ¥å·¥å£å¾ï¼"> |
| | | <TeamGrid table-title="çç»å·¥æ¶ç»è®¡" /> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | </div> |
| | | </Page> |
| | | </template> |
| | |
| | | columns: useLatestColumns(), |
| | | height: 320, |
| | | keepSource: true, |
| | | // /latest è¿å裸æ°ç»ï¼éå
³éå页ï¼å¦å vxe 伿 {list,total} è§£æå¯¼è´ä¸å±ç¤º |
| | | pagerConfig: { enabled: false }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async () => |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesDvTelemetryApi } from '#/api/mes/dv/telemetry'; |
| | | |
| | | import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | |
| | | import { Button, Card, Select, Statistic, Tag } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | getTelemetryCheckList, |
| | | getTelemetryCheckSummary, |
| | | getTelemetryDeviceList, |
| | | } from '#/api/mes/dv/telemetry'; |
| | | |
| | | /** MES ä¾çµåæ°æ¯å¯¹æ ¡éªï¼åè®¾å¤ææ°åæ° vs æ åå¼ï¼ */ |
| | | defineOptions({ name: 'MesDvTelemetryCheck' }); |
| | | |
| | | const devices = ref<MesDvTelemetryApi.DeviceItem[]>([]); |
| | | const selectedDeviceId = ref<string>(); |
| | | const summaryList = ref<MesDvTelemetryApi.CheckSummary[]>([]); |
| | | |
| | | const contentRef = useTemplateRef('contentRef'); |
| | | let resizeObserver: ResizeObserver | undefined; |
| | | |
| | | /** 计ç®è¡¨æ ¼é«åº¦ï¼å
容åºå¯ç¨é«åº¦ - 设å¤éæ©è¡/ç»è®¡å¡ç/å·¥å
·æ çåºå®å¼é */ |
| | | function calcTableHeight() { |
| | | const el = contentRef.value; |
| | | if (!el) return; |
| | | const height = Math.max(el.clientHeight - 230, 240); |
| | | checkGridApi.setGridOptions({ height }); |
| | | } |
| | | |
| | | onMounted(async () => { |
| | | calcTableHeight(); |
| | | resizeObserver = new ResizeObserver(calcTableHeight); |
| | | if (contentRef.value) resizeObserver.observe(contentRef.value); |
| | | devices.value = await getTelemetryDeviceList(); |
| | | await loadSummary(); |
| | | }); |
| | | |
| | | onBeforeUnmount(() => resizeObserver?.disconnect()); |
| | | |
| | | async function loadSummary() { |
| | | summaryList.value = await getTelemetryCheckSummary({ |
| | | tbDeviceId: selectedDeviceId.value, |
| | | }); |
| | | } |
| | | |
| | | /** éè®¾å¤æå·æ°ï¼éè½½ç»è®¡å¡ç + æç»è¡¨ */ |
| | | async function refreshAll() { |
| | | await loadSummary(); |
| | | checkGridApi.query(); |
| | | } |
| | | |
| | | /** éä¸è®¾å¤çç»è®¡ï¼æªéæ©åæ±æ»å
¨é¨è®¾å¤ */ |
| | | const currentSummary = computed(() => { |
| | | if (selectedDeviceId.value) { |
| | | return ( |
| | | summaryList.value.find((s) => s.tbDeviceId === selectedDeviceId.value) ?? |
| | | {} |
| | | ); |
| | | } |
| | | return summaryList.value.reduce( |
| | | (acc, s) => { |
| | | acc.totalCount = (acc.totalCount ?? 0) + (s.totalCount ?? 0); |
| | | acc.normalCount = (acc.normalCount ?? 0) + (s.normalCount ?? 0); |
| | | acc.anomalyCount = (acc.anomalyCount ?? 0) + (s.anomalyCount ?? 0); |
| | | return acc; |
| | | }, |
| | | { totalCount: 0, normalCount: 0, anomalyCount: 0 } as MesDvTelemetryApi.CheckSummary, |
| | | ); |
| | | }); |
| | | |
| | | const anomalyRate = computed(() => { |
| | | const total = currentSummary.value.totalCount ?? 0; |
| | | if (total <= 0) return 0; |
| | | return Number( |
| | | (((currentSummary.value.anomalyCount ?? 0) * 100) / total).toFixed(1), |
| | | ); |
| | | }); |
| | | |
| | | const [CheckGrid, checkGridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: [ |
| | | { field: 'deviceName', title: '设å¤åç§°', minWidth: 150 }, |
| | | { field: 'paramName', title: 'ä¿¡å·åç§°', minWidth: 140 }, |
| | | { field: 'paramKeyName', title: '设å¤KEYåç§°', minWidth: 130 }, |
| | | { field: 'standardValue', title: 'æ åå¼', width: 110 }, |
| | | { field: 'timelyValue', title: '宿¶å¼', width: 110 }, |
| | | { field: 'avgValue', title: 'åå¼', width: 110 }, |
| | | { field: 'maxValue', title: 'æå¤§å¼', width: 110 }, |
| | | { field: 'minValue', title: 'æå°å¼', width: 110 }, |
| | | { |
| | | field: 'whetherAnomaly', |
| | | title: 'æ¯å¦å¼å¸¸', |
| | | width: 100, |
| | | slots: { default: 'anomaly' }, |
| | | }, |
| | | ], |
| | | height: 400, |
| | | keepSource: true, |
| | | // /check è¿å裸æ°ç»ï¼éå
³éå页 |
| | | pagerConfig: { enabled: false }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async () => |
| | | await getTelemetryCheckList({ |
| | | tbDeviceId: selectedDeviceId.value, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true }, |
| | | } as VxeTableGridOptions<MesDvTelemetryApi.Telemetry>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <div ref="contentRef" class="flex h-full w-full flex-col p-4"> |
| | | <div class="mb-4 flex flex-wrap items-center gap-4"> |
| | | <Select |
| | | v-model:value="selectedDeviceId" |
| | | class="w-72" |
| | | allow-clear |
| | | placeholder="è¯·éæ©è®¾å¤ï¼ä¸éåå±ç¤ºå
¨é¨è®¾å¤ï¼" |
| | | :options=" |
| | | devices.map((d) => ({ label: d.deviceName, value: d.tbDeviceId })) |
| | | " |
| | | @change="refreshAll" |
| | | /> |
| | | <Button @click="refreshAll">å·æ°</Button> |
| | | </div> |
| | | <div class="mb-4 grid grid-cols-2 gap-4 lg:grid-cols-4"> |
| | | <Card> |
| | | <Statistic title="åæ°æ»æ°" :value="currentSummary.totalCount ?? 0" /> |
| | | </Card> |
| | | <Card> |
| | | <Statistic |
| | | title="æ£å¸¸" |
| | | :value="currentSummary.normalCount ?? 0" |
| | | :value-style="{ color: '#52c41a' }" |
| | | /> |
| | | </Card> |
| | | <Card> |
| | | <Statistic |
| | | title="å¼å¸¸" |
| | | :value="currentSummary.anomalyCount ?? 0" |
| | | :value-style="{ color: '#ff4d4f' }" |
| | | /> |
| | | </Card> |
| | | <Card> |
| | | <Statistic title="å¼å¸¸ç" :value="anomalyRate" suffix="%" /> |
| | | </Card> |
| | | </div> |
| | | <CheckGrid table-title="ä¾çµåæ°æ¯å¯¹æç»" class="min-h-0 flex-1"> |
| | | <template #anomaly="{ row }"> |
| | | <Tag :color="row.whetherAnomaly ? 'red' : 'green'"> |
| | | {{ row.whetherAnomaly ? 'å¼å¸¸' : 'æ£å¸¸' }} |
| | | </Tag> |
| | | </template> |
| | | </CheckGrid> |
| | | </div> |
| | | </Page> |
| | | </template> |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesDvTelemetryApi } from '#/api/mes/dv/telemetry'; |
| | | |
| | | import { onBeforeUnmount, ref } from 'vue'; |
| | | import { onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | |
| | | const lastUpdateTime = ref<Date>(); |
| | | let refreshTimer: ReturnType<typeof setInterval> | undefined; |
| | | |
| | | const contentRef = useTemplateRef('contentRef'); |
| | | let resizeObserver: ResizeObserver | undefined; |
| | | |
| | | /** 计ç®è¡¨æ ¼é«åº¦ï¼å
容åºå¯ç¨é«åº¦ - å·¥å
·æ è¡/Tabs导èªçåºå®å¼éï¼ä¿è¯è¡¨æ ¼ææ»¡é¡µé¢ï¼å¯¹é½è®¾å¤å°è´¦é¡µï¼ */ |
| | | function calcTableHeight() { |
| | | const el = contentRef.value; |
| | | if (!el) return; |
| | | const height = Math.max(el.clientHeight - 115, 240); |
| | | latestGridApi.setGridOptions({ height }); |
| | | historyGridApi.setGridOptions({ height }); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | calcTableHeight(); |
| | | resizeObserver = new ResizeObserver(calcTableHeight); |
| | | if (contentRef.value) resizeObserver.observe(contentRef.value); |
| | | }); |
| | | |
| | | const [LatestGrid, latestGridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: useLatestColumns(), |
| | | height: 'auto', |
| | | height: 400, |
| | | keepSource: true, |
| | | // /latest è¿å裸æ°ç»ï¼éå
³éå页ï¼å¦å vxe 伿 {list,total} è§£æå¯¼è´ä¸å±ç¤º |
| | | pagerConfig: { enabled: false }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async () => await getLatestTelemetry(), |
| | |
| | | formOptions: { schema: useHistoryGridFormSchema() }, |
| | | gridOptions: { |
| | | columns: useHistoryColumns(), |
| | | height: 'auto', |
| | | height: 400, |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | |
| | | } |
| | | |
| | | /** 忢宿¶èªå¨å·æ°ï¼æ¯ 30 ç§ï¼ */ |
| | | function toggleAutoRefresh(checked: boolean) { |
| | | function toggleAutoRefresh(checked: boolean | string | number) { |
| | | if (checked) { |
| | | refreshTimer = setInterval(() => { |
| | | if (activeTab.value === 'latest') { |
| | |
| | | } |
| | | |
| | | onBeforeUnmount(() => { |
| | | resizeObserver?.disconnect(); |
| | | if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | } |
| | |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <div class="p-4"> |
| | | <div ref="contentRef" class="flex h-full w-full flex-col"> |
| | | <div class="mb-4 flex items-center gap-4"> |
| | | <Button type="primary" :loading="pulling" @click="handlePull"> |
| | | æåææ°æ°æ® |
| | |
| | | æè¿æåï¼{{ dayjs(lastUpdateTime).format('YYYY-MM-DD HH:mm:ss') }} |
| | | </span> |
| | | </div> |
| | | <Tabs v-model:active-key="activeTab"> |
| | | <Tabs v-model:active-key="activeTab" class="min-h-0 flex-1"> |
| | | <Tabs.TabPane key="latest" tab="宿¶æ°æ®"> |
| | | <LatestGrid table-title="宿¶æ°æ®"> |
| | | <template #anomaly="{ row }"> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { ProProcessSelect } from '#/views/mes/pro/process/components'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** ç¶æå¸¸é */ |
| | | export const PLAN_STATUS = { |
| | | DRAFT: 0, |
| | | PUBLISHED: 10, |
| | | }; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType, formApi?: VbenFormApi): VbenFormSchema[] { |
| | | const isReadonly = formType === 'detail'; |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'æ¹æ¡ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ç³»ç»èªå¨çæ', |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¹æ¡åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¹æ¡åç§°', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'mainProcessId', |
| | | label: '主工åº', |
| | | component: markRaw(ProProcessSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä¸»å·¥åº', |
| | | disabled: isReadonly, |
| | | onChange: (item: { name?: string } | undefined) => { |
| | | formApi?.setFieldValue('mainProcessName', item?.name ?? ''); |
| | | }, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'altProcessId', |
| | | label: 'æ¿ä»£å·¥åº', |
| | | component: markRaw(ProProcessSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©æ¿ä»£å·¥åº', |
| | | disabled: isReadonly, |
| | | onChange: (item: { name?: string } | undefined) => { |
| | | formApi?.setFieldValue('altProcessName', item?.name ?? ''); |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'altCondition', |
| | | label: 'æ¿ä»£æ¡ä»¶', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'å¦ï¼è®¾å¤æ
é/è´è·åé/人åä¸è¶³', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'scope', |
| | | label: 'éç¨èå´', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'å¦ï¼110kV 线路', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: isReadonly, |
| | | }, |
| | | formItemClass: 'col-span-3', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: 'æ¹æ¡ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¹æ¡ç¼ç ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¹æ¡åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¹æ¡åç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'è稿', value: PLAN_STATUS.DRAFT }, |
| | | { label: 'å·²åå¸', value: PLAN_STATUS.PUBLISHED }, |
| | | ], |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çè¡¨æ ¼å */ |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { type: 'seq', title: 'åºå·', width: 60 }, |
| | | { |
| | | field: 'code', |
| | | title: 'æ¹æ¡ç¼ç ', |
| | | minWidth: 150, |
| | | slots: { default: 'code' }, |
| | | }, |
| | | { |
| | | field: 'name', |
| | | title: 'æ¹æ¡åç§°', |
| | | minWidth: 200, |
| | | }, |
| | | { |
| | | field: 'mainProcessName', |
| | | title: '主工åº', |
| | | minWidth: 130, |
| | | }, |
| | | { |
| | | field: 'altProcessName', |
| | | title: 'æ¿ä»£å·¥åº', |
| | | minWidth: 130, |
| | | }, |
| | | { |
| | | field: 'altCondition', |
| | | title: 'æ¿ä»£æ¡ä»¶', |
| | | minWidth: 200, |
| | | }, |
| | | { |
| | | field: 'scope', |
| | | title: 'éç¨èå´', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 260, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesProAlternativePlanApi } from '#/api/mes/pro/alternativeplan'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { isEmpty } from '@vben/utils'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deletePlan, getPlanPage, publishPlan } from '#/api/mes/pro/alternativeplan'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { PLAN_STATUS, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** MES ä½ä¸æ¿ä»£æ¹æ¡å表 */ |
| | | defineOptions({ name: 'MesProAlternativePlan' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: MesProAlternativePlanApi.AlternativePlan) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleDetail(row: MesProAlternativePlanApi.AlternativePlan) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | async function handleDelete(ids: number[]) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting'), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deletePlan(ids); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | async function handlePublish(row: MesProAlternativePlanApi.AlternativePlan) { |
| | | await publishPlan(row.id!); |
| | | message.success('å·²åå¸'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: MesProAlternativePlanApi.AlternativePlan[]; |
| | | }) { |
| | | checkedIds.value = records.map((item) => item.id!); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getPlanPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesProAlternativePlanApi.AlternativePlan>, |
| | | gridEvents: { |
| | | checkboxAll: handleRowCheckboxChange, |
| | | checkboxChange: handleRowCheckboxChange, |
| | | }, |
| | | }); |
| | | </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: ['mes:pro-alternative-plan:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pro-alternative-plan:delete'], |
| | | popConfirm: { |
| | | title: `æ¯å¦å 餿é䏿°æ®ï¼`, |
| | | confirm: handleDelete.bind(null, checkedIds), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #code="{ row }"> |
| | | <a class="link" @click="handleDetail(row)">{{ row.code }}</a> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag v-if="row.status === PLAN_STATUS.DRAFT" color="default">è稿</Tag> |
| | | <Tag v-else-if="row.status === PLAN_STATUS.PUBLISHED" color="success">å·²åå¸</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:pro-alternative-plan:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pro-alternative-plan:update'], |
| | | ifShow: row.status === PLAN_STATUS.DRAFT, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'åå¸', |
| | | type: 'link', |
| | | auth: ['mes:pro-alternative-plan:update'], |
| | | ifShow: row.status === PLAN_STATUS.DRAFT, |
| | | popConfirm: { |
| | | title: '确认åå¸è¯¥æ¹æ¡ï¼', |
| | | confirm: handlePublish.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pro-alternative-plan:delete'], |
| | | ifShow: row.status === PLAN_STATUS.DRAFT, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.code]), |
| | | confirm: handleDelete.bind(null, [row.id!]), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesProAlternativePlanApi } from '#/api/mes/pro/alternativeplan'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createPlan, getPlan, updatePlan } from '#/api/mes/pro/alternativeplan'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesProAlternativePlanApi.AlternativePlan>(); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | | return $t('ui.actionTitle.create', ['ä½ä¸æ¿ä»£æ¹æ¡']); |
| | | } else if (formType.value === 'edit') { |
| | | return $t('ui.actionTitle.edit', ['ä½ä¸æ¿ä»£æ¹æ¡']); |
| | | } else { |
| | | return 'ä½ä¸æ¿ä»£æ¹æ¡è¯¦æ
'; |
| | | } |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 120, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'vertical', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = |
| | | (await formApi.getValues()) as MesProAlternativePlanApi.AlternativePlan; |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createPlan(data) |
| | | : updatePlan(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | formApi.updateSchema(useFormSchema(formType.value, formApi)); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getPlan(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-2/3" :show-confirm-button="formType !== 'detail'"> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { CalTeamSelect } from '#/views/mes/cal/team/components'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** ç¶æå¸¸é */ |
| | | export const CAL_STATUS = { |
| | | DRAFT: 0, |
| | | PUBLISHED: 10, |
| | | }; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType, formApi?: VbenFormApi): VbenFormSchema[] { |
| | | const isReadonly = formType === 'detail'; |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'æ¥åç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ç³»ç»èªå¨çæ', |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¥ååç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¥ååç§°', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'workDate', |
| | | label: 'ä½ä¸æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä½ä¸æ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'shift', |
| | | label: 'çæ¬¡', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_CAL_SHIFT_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©çæ¬¡', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'workType', |
| | | label: 'ä½ä¸ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_PRO_CALENDAR_WORK_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©ä½ä¸ç±»å', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'region', |
| | | label: 'ä½ä¸åºå/线路', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'å¦ï¼110kV ååºçº¿', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'teamId', |
| | | label: 'å¼ççç»', |
| | | component: markRaw(CalTeamSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å¼ççç»', |
| | | disabled: isReadonly, |
| | | onChange: (item: { name?: string } | undefined) => { |
| | | formApi?.setFieldValue('teamName', item?.name ?? ''); |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'personnel', |
| | | label: 'å¼ç人å', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'å¦ï¼å¼ ä¸,æå', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: isReadonly, |
| | | }, |
| | | formItemClass: 'col-span-3', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: 'æ¥åç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¥åç¼ç ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¥ååç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¥ååç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'workType', |
| | | label: 'ä½ä¸ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PRO_CALENDAR_WORK_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©ä½ä¸ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'è稿', value: CAL_STATUS.DRAFT }, |
| | | { label: 'å·²åå¸', value: CAL_STATUS.PUBLISHED }, |
| | | ], |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çè¡¨æ ¼å */ |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { type: 'seq', title: 'åºå·', width: 60 }, |
| | | { |
| | | field: 'code', |
| | | title: 'æ¥åç¼ç ', |
| | | minWidth: 150, |
| | | slots: { default: 'code' }, |
| | | }, |
| | | { |
| | | field: 'name', |
| | | title: 'æ¥ååç§°', |
| | | minWidth: 180, |
| | | }, |
| | | { |
| | | field: 'workDate', |
| | | title: 'ä½ä¸æ¥æ', |
| | | width: 120, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'shift', |
| | | title: 'çæ¬¡', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_CAL_SHIFT_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'workType', |
| | | title: 'ä½ä¸ç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PRO_CALENDAR_WORK_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'region', |
| | | title: 'ä½ä¸åºå/线路', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'teamName', |
| | | title: 'å¼ççç»', |
| | | minWidth: 130, |
| | | }, |
| | | { |
| | | field: 'personnel', |
| | | title: 'å¼ç人å', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 260, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesProMaintenanceCalendarApi } from '#/api/mes/pro/maintenancecalendar'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { isEmpty } from '@vben/utils'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteCalendar, |
| | | getCalendarPage, |
| | | publishCalendar, |
| | | } from '#/api/mes/pro/maintenancecalendar'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { CAL_STATUS, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** MES çµåè¿ç»´æ¥åå表 */ |
| | | defineOptions({ name: 'MesProMaintenanceCalendar' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: MesProMaintenanceCalendarApi.MaintenanceCalendar) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleDetail(row: MesProMaintenanceCalendarApi.MaintenanceCalendar) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | async function handleDelete(ids: number[]) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting'), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteCalendar(ids); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | async function handlePublish(row: MesProMaintenanceCalendarApi.MaintenanceCalendar) { |
| | | await publishCalendar(row.id!); |
| | | message.success('å·²åå¸'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: MesProMaintenanceCalendarApi.MaintenanceCalendar[]; |
| | | }) { |
| | | checkedIds.value = records.map((item) => item.id!); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getCalendarPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesProMaintenanceCalendarApi.MaintenanceCalendar>, |
| | | gridEvents: { |
| | | checkboxAll: handleRowCheckboxChange, |
| | | checkboxChange: handleRowCheckboxChange, |
| | | }, |
| | | }); |
| | | </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: ['mes:pro-maintenance-calendar:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pro-maintenance-calendar:delete'], |
| | | popConfirm: { |
| | | title: `æ¯å¦å 餿é䏿°æ®ï¼`, |
| | | confirm: handleDelete.bind(null, checkedIds), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #code="{ row }"> |
| | | <a class="link" @click="handleDetail(row)">{{ row.code }}</a> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag v-if="row.status === CAL_STATUS.DRAFT" color="default">è稿</Tag> |
| | | <Tag v-else-if="row.status === CAL_STATUS.PUBLISHED" color="success">å·²åå¸</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:pro-maintenance-calendar:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pro-maintenance-calendar:update'], |
| | | ifShow: row.status === CAL_STATUS.DRAFT, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'åå¸', |
| | | type: 'link', |
| | | auth: ['mes:pro-maintenance-calendar:update'], |
| | | ifShow: row.status === CAL_STATUS.DRAFT, |
| | | popConfirm: { |
| | | title: '确认åå¸è¯¥æ¥åï¼', |
| | | confirm: handlePublish.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pro-maintenance-calendar:delete'], |
| | | ifShow: row.status === CAL_STATUS.DRAFT, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.code]), |
| | | confirm: handleDelete.bind(null, [row.id!]), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesProMaintenanceCalendarApi } from '#/api/mes/pro/maintenancecalendar'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createCalendar, |
| | | getCalendar, |
| | | updateCalendar, |
| | | } from '#/api/mes/pro/maintenancecalendar'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesProMaintenanceCalendarApi.MaintenanceCalendar>(); |
| | | const formType = ref<FormType>('create'); |
| | | |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'create') { |
| | | return $t('ui.actionTitle.create', ['çµåè¿ç»´æ¥å']); |
| | | } else if (formType.value === 'edit') { |
| | | return $t('ui.actionTitle.edit', ['çµåè¿ç»´æ¥å']); |
| | | } else { |
| | | return 'çµåè¿ç»´æ¥å详æ
'; |
| | | } |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 120, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'vertical', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = |
| | | (await formApi.getValues()) as MesProMaintenanceCalendarApi.MaintenanceCalendar; |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createCalendar(data) |
| | | : updateCalendar(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ formType: FormType; id?: number }>(); |
| | | formType.value = data.formType; |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | formApi.updateSchema(useFormSchema(formType.value, formApi)); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getCalendar(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-2/3" :show-confirm-button="formType !== 'detail'"> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |