| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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', |
| | | ); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace DecisionAlertApi { |
| | | export interface AlertRecord { |
| | | id: number; |
| | | ruleId: number; |
| | | kpiCode: string; |
| | | kpiName: string; |
| | | actualValue: number; |
| | | thresholdValue: number; |
| | | severity: number; |
| | | alertMessage: string; |
| | | status: number; |
| | | handlerId?: number; |
| | | handleTime?: string; |
| | | handleRemark?: string; |
| | | triggeredTime: string; |
| | | createTime: string; |
| | | } |
| | | |
| | | export interface AlertRule { |
| | | id?: number; |
| | | kpiId: number; |
| | | name: string; |
| | | severity: number; |
| | | comparisonOperator: string; |
| | | thresholdValue: number; |
| | | notificationType?: string; |
| | | enabled?: number; |
| | | remark?: string; |
| | | } |
| | | } |
| | | |
| | | export function getAlertPage(params: Record<string, unknown>) { |
| | | return requestClient.get('/bi/decision/alert/page', { params }); |
| | | } |
| | | |
| | | export function getUnprocessedCount() { |
| | | return requestClient.get<number>('/bi/decision/alert/unprocessed-count'); |
| | | } |
| | | |
| | | export function confirmAlert(id: number) { |
| | | return requestClient.post(`/bi/decision/alert/confirm/${id}`); |
| | | } |
| | | |
| | | export function handleAlert(id: number, handleRemark?: string) { |
| | | return requestClient.post('/bi/decision/alert/handle', { id, handleRemark }); |
| | | } |
| | | |
| | | export function getAlertRulePage(params: Record<string, unknown>) { |
| | | return requestClient.get('/bi/decision/alert-rule/page', { params }); |
| | | } |
| | | |
| | | export function getAlertRule(id: number) { |
| | | return requestClient.get<DecisionAlertApi.AlertRule>('/bi/decision/alert-rule/get', { params: { id } }); |
| | | } |
| | | |
| | | export function createAlertRule(data: DecisionAlertApi.AlertRule) { |
| | | return requestClient.post('/bi/decision/alert-rule/create', data); |
| | | } |
| | | |
| | | export function updateAlertRule(data: DecisionAlertApi.AlertRule) { |
| | | return requestClient.put('/bi/decision/alert-rule/update', data); |
| | | } |
| | | |
| | | export function deleteAlertRule(id: number) { |
| | | return requestClient.delete('/bi/decision/alert-rule/delete', { params: { id } }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace DecisionForecastApi { |
| | | export interface ForecastItem { |
| | | id: number; |
| | | forecastCode: string; |
| | | forecastName: string; |
| | | pointTime: string; |
| | | forecastValue: number; |
| | | actualValue?: number; |
| | | lowerBound?: number; |
| | | upperBound?: number; |
| | | confidenceLevel?: number; |
| | | modelVersion?: string; |
| | | dimension?: string; |
| | | dimensionValue?: string; |
| | | } |
| | | } |
| | | |
| | | export function getForecastList(forecastCode: string, params?: Record<string, unknown>) { |
| | | return requestClient.get<DecisionForecastApi.ForecastItem[]>('/bi/decision/forecast/list', { |
| | | params: { forecastCode, ...params }, |
| | | }); |
| | | } |
| | | |
| | | export function generateForecast(forecastCode: string) { |
| | | 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 { requestClient } from '#/api/request'; |
| | | |
| | | export namespace DecisionKpiApi { |
| | | export interface KpiItem { |
| | | code: string; |
| | | name: string; |
| | | category: string; |
| | | unit: string; |
| | | value: number; |
| | | alertStatus: 'normal' | 'warn' | 'critical'; |
| | | chartType: string; |
| | | status: number; |
| | | } |
| | | |
| | | export interface KpiOverview { |
| | | kpis: KpiItem[]; |
| | | total: number; |
| | | } |
| | | |
| | | export interface KpiDefinition { |
| | | id?: number; |
| | | code: string; |
| | | name: string; |
| | | category: string; |
| | | unit?: string; |
| | | dataSource?: string; |
| | | querySql?: string; |
| | | chartType?: string; |
| | | thresholdWarn?: number; |
| | | thresholdCritical?: number; |
| | | comparisonOperator?: string; |
| | | refreshInterval?: number; |
| | | sort?: number; |
| | | status?: number; |
| | | remark?: string; |
| | | } |
| | | } |
| | | |
| | | export function getKpiOverview() { |
| | | return requestClient.get<DecisionKpiApi.KpiOverview>('/bi/decision/kpi/overview'); |
| | | } |
| | | |
| | | export function getKpiDetail(kpiCode: string) { |
| | | return requestClient.get<Record<string, unknown>>(`/bi/decision/kpi/detail/${kpiCode}`); |
| | | } |
| | | |
| | | export function getKpiByCategory(category: string) { |
| | | return requestClient.get<Record<string, unknown>>(`/bi/decision/kpi/category/${category}`); |
| | | } |
| | | |
| | | export function refreshKpi(kpiCode: string) { |
| | | return requestClient.post<number>(`/bi/decision/kpi/refresh/${kpiCode}`); |
| | | } |
| | | |
| | | export function getKpiDefinitionPage(params: Record<string, unknown>) { |
| | | return requestClient.get('/bi/decision/kpi-definition/page', { params }); |
| | | } |
| | | |
| | | export function getKpiDefinitionList() { |
| | | return requestClient.get('/bi/decision/kpi-definition/list-all'); |
| | | } |
| | | |
| | | export function getKpiDefinition(id: number) { |
| | | return requestClient.get<DecisionKpiApi.KpiDefinition>('/bi/decision/kpi-definition/get', { params: { id } }); |
| | | } |
| | | |
| | | export function createKpiDefinition(data: DecisionKpiApi.KpiDefinition) { |
| | | return requestClient.post('/bi/decision/kpi-definition/create', data); |
| | | } |
| | | |
| | | export function updateKpiDefinition(data: DecisionKpiApi.KpiDefinition) { |
| | | return requestClient.put('/bi/decision/kpi-definition/update', data); |
| | | } |
| | | |
| | | export function deleteKpiDefinition(id: number) { |
| | | return requestClient.delete('/bi/decision/kpi-definition/delete', { params: { id } }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace CrmCancellationApi { |
| | | export interface Cancellation { |
| | | id?: number; |
| | | cancellationNo?: string; |
| | | customerId?: number; |
| | | customerName?: string; |
| | | contractId?: number; |
| | | contractNo?: string; |
| | | cancellationType?: string; |
| | | cancelReason?: string; |
| | | expectedCancelDate?: string; |
| | | actualCancelDate?: string; |
| | | refundAmount?: number; |
| | | applyUserId?: number; |
| | | applyUserName?: string; |
| | | applyTime?: string; |
| | | auditStatus?: number; |
| | | processInstanceId?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | export interface PageParams extends PageParam { |
| | | cancellationNo?: string; |
| | | customerId?: number; |
| | | cancellationType?: string; |
| | | auditStatus?: number; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢éæ·ç³è¯·å页 */ |
| | | export function getCancellationPage( |
| | | params: CrmCancellationApi.PageParams, |
| | | ) { |
| | | return requestClient.get<PageResult<CrmCancellationApi.Cancellation>>( |
| | | '/crm/cancellation/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢éæ·ç³è¯·è¯¦æ
*/ |
| | | export function getCancellation(id: number) { |
| | | return requestClient.get<CrmCancellationApi.Cancellation>( |
| | | `/crm/cancellation/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢éæ·ç³è¯· */ |
| | | export function createCancellation(data: CrmCancellationApi.Cancellation) { |
| | | return requestClient.post('/crm/cancellation/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹éæ·ç³è¯· */ |
| | | export function updateCancellation(data: CrmCancellationApi.Cancellation) { |
| | | return requestClient.put('/crm/cancellation/update', data); |
| | | } |
| | | |
| | | /** 审æ¹éæ·ç³è¯· */ |
| | | export function auditCancellation(data: { |
| | | id: number; |
| | | auditStatus: number; |
| | | }) { |
| | | return requestClient.put('/crm/cancellation/audit', data); |
| | | } |
| | | |
| | | /** å é¤éæ·ç³è¯· */ |
| | | export function deleteCancellation(id: number) { |
| | | return requestClient.delete(`/crm/cancellation/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºéæ·ç³è¯· */ |
| | | export function exportCancellation(params: Record<string, unknown>) { |
| | | return requestClient.download('/crm/cancellation/export-excel', { params }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace CrmContractCommandApi { |
| | | export interface ContractCommand { |
| | | id?: number; |
| | | contractId?: number; |
| | | contractNo?: string; |
| | | commandType?: string; |
| | | targetModule?: string; |
| | | targetId?: number; |
| | | bizNo?: string; |
| | | operatorId?: number; |
| | | operatorName?: string; |
| | | operateTime?: string; |
| | | status?: number; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | export interface PageParams extends PageParam { |
| | | contractId?: number; |
| | | commandType?: string; |
| | | status?: number; |
| | | bizNo?: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢ååæä»¤å页 */ |
| | | export function getContractCommandPage( |
| | | params: CrmContractCommandApi.PageParams, |
| | | ) { |
| | | return requestClient.get<PageResult<CrmContractCommandApi.ContractCommand>>( |
| | | '/crm/contract-command/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢ååæä»¤ */ |
| | | export function createContractCommand( |
| | | data: CrmContractCommandApi.ContractCommand, |
| | | ) { |
| | | return requestClient.post('/crm/contract-command/create', data); |
| | | } |
| | | |
| | | /** æ´æ°ååæä»¤ */ |
| | | export function updateContractCommand( |
| | | data: CrmContractCommandApi.ContractCommand, |
| | | ) { |
| | | return requestClient.put('/crm/contract-command/update', data); |
| | | } |
| | | |
| | | /** æ§è¡ååæä»¤ */ |
| | | export function executeContractCommand(id: number) { |
| | | return requestClient.put(`/crm/contract-command/execute?id=${id}`); |
| | | } |
| | | |
| | | /** åæ¶ååæä»¤ */ |
| | | export function cancelContractCommand(id: number) { |
| | | return requestClient.put(`/crm/contract-command/cancel?id=${id}`); |
| | | } |
| | | |
| | | /** å é¤ååæä»¤ */ |
| | | export function deleteContractCommand(id: number) { |
| | | return requestClient.delete(`/crm/contract-command/delete?id=${id}`); |
| | | } |
| | |
| | | contactName?: string; |
| | | orderId?: number; // å
³è ERP éå®è®¢åç¼å· |
| | | orderNo?: string; // ERP éå®è®¢ååå· |
| | | contractType?: string; // ååç±»åï¼ä¾ç¨çµ/è½æºæå¡çï¼ |
| | | voltageLevel?: string; // çµåç级 |
| | | electricityType?: string; // ç¨çµç±»å |
| | | contractCapacity?: number; // åå容éï¼kVAï¼ |
| | | tariffMode?: string; // çµä»·æ¨¡å¼ |
| | | meterMode?: string; // è®¡éæ¹å¼ |
| | | payCycle?: number; // ç¼´è´¹å¨æï¼æï¼ |
| | | commandCount?: number; // æä»¤æ°é |
| | | } |
| | | |
| | | /** åå产åä¿¡æ¯ */ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace CrmCreditRatingApi { |
| | | export interface CreditRating { |
| | | id?: number; |
| | | customerId?: number; |
| | | customerName?: string; |
| | | level?: string; |
| | | score?: number; |
| | | evalType?: string; |
| | | evalReason?: string; |
| | | operatorId?: number; |
| | | operatorName?: string; |
| | | operateTime?: string; |
| | | validUntil?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | export interface PageParams extends PageParam { |
| | | customerId?: number; |
| | | level?: string; |
| | | evalType?: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢ä¿¡ç¨è¯çº§å页 */ |
| | | export function getCreditRatingPage( |
| | | params: CrmCreditRatingApi.PageParams, |
| | | ) { |
| | | return requestClient.get<PageResult<CrmCreditRatingApi.CreditRating>>( |
| | | '/crm/credit-rating/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢ä¿¡ç¨è¯çº§è¯¦æ
*/ |
| | | export function getCreditRating(id: number) { |
| | | return requestClient.get<CrmCreditRatingApi.CreditRating>( |
| | | `/crm/credit-rating/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢ä¿¡ç¨è¯çº§ */ |
| | | export function createCreditRating(data: CrmCreditRatingApi.CreditRating) { |
| | | return requestClient.post('/crm/credit-rating/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹ä¿¡ç¨è¯çº§ */ |
| | | export function updateCreditRating(data: CrmCreditRatingApi.CreditRating) { |
| | | return requestClient.put('/crm/credit-rating/update', data); |
| | | } |
| | | |
| | | /** å é¤ä¿¡ç¨è¯çº§ */ |
| | | export function deleteCreditRating(id: number) { |
| | | return requestClient.delete(`/crm/credit-rating/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºä¿¡ç¨è¯çº§ */ |
| | | export function exportCreditRating(params: Record<string, unknown>) { |
| | | return requestClient.download('/crm/credit-rating/export-excel', { params }); |
| | | } |
| | |
| | | createTime: Date; // å建æ¶é´ |
| | | updateTime: Date; // æ´æ°æ¶é´ |
| | | poolDay?: number; // è·ç¦»è¿å
¥å
¬æµ·å¤©æ° |
| | | customerType?: string; // 客æ·åç±»ï¼æ¿ä¼/å·¥ä¸/å±
æ°çï¼ |
| | | creditLevel?: string; // å½åä¿¡ç¨ç级 |
| | | creditScore?: number; // å½åä¿¡ç¨å |
| | | overdueAmount?: number; // æ¬ è´¹éé¢ï¼æªå款+é¾æï¼ |
| | | overdueCount?: number; // æ¬ è´¹ç¬æ° |
| | | } |
| | | |
| | | /** 客æ·å¯¼å
¥è¯·æ± */ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace CrmRefundApi { |
| | | export interface Refund { |
| | | id?: number; |
| | | refundNo?: string; |
| | | customerId?: number; |
| | | customerName?: string; |
| | | contractId?: number; |
| | | contractNo?: string; |
| | | refundType?: string; |
| | | refundAmount?: number; |
| | | refundReason?: string; |
| | | applyUserId?: number; |
| | | applyUserName?: string; |
| | | applyTime?: string; |
| | | auditStatus?: number; |
| | | processInstanceId?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | export interface PageParams extends PageParam { |
| | | refundNo?: string; |
| | | customerId?: number; |
| | | refundType?: string; |
| | | auditStatus?: number; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢éè´¹åå页 */ |
| | | export function getRefundPage(params: CrmRefundApi.PageParams) { |
| | | return requestClient.get<PageResult<CrmRefundApi.Refund>>( |
| | | '/crm/refund/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢éè´¹å详æ
*/ |
| | | export function getRefund(id: number) { |
| | | return requestClient.get<CrmRefundApi.Refund>(`/crm/refund/get?id=${id}`); |
| | | } |
| | | |
| | | /** æ°å¢éè´¹å */ |
| | | export function createRefund(data: CrmRefundApi.Refund) { |
| | | return requestClient.post('/crm/refund/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹éè´¹å */ |
| | | export function updateRefund(data: CrmRefundApi.Refund) { |
| | | return requestClient.put('/crm/refund/update', data); |
| | | } |
| | | |
| | | /** 审æ¹éè´¹å */ |
| | | export function auditRefund(data: { id: number; auditStatus: number }) { |
| | | return requestClient.put('/crm/refund/audit', data); |
| | | } |
| | | |
| | | /** å é¤éè´¹å */ |
| | | export function deleteRefund(id: number) { |
| | | return requestClient.delete(`/crm/refund/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºéè´¹å */ |
| | | export function exportRefund(params: Record<string, unknown>) { |
| | | return requestClient.download('/crm/refund/export-excel', { params }); |
| | | } |
| | |
| | | createTime?: Date; |
| | | updateTime?: Date; |
| | | items?: SaleQuotationItem[]; |
| | | quotationType?: string; // æ¥ä»·ç±»åï¼general/power/energyï¼ |
| | | capacity?: number; // åå容éï¼kVAï¼ |
| | | maxDemand?: number; // æå¤§ééï¼kWï¼ |
| | | unitPrice?: number; // çµåº¦åä»·ï¼å
/kWhï¼ |
| | | tariffMode?: string; // çµä»·æ¨¡å¼ |
| | | powerFactorFee?: number; // åè°çµè´¹ï¼å
ï¼ |
| | | billingCycle?: number; // ç»ç®å¨æï¼æï¼ |
| | | } |
| | | |
| | | /** æ¥ä»·åç©ææç» */ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace ErpPurchasePlanApi { |
| | | /** éè´è®¡åä¿¡æ¯ */ |
| | | export interface PurchasePlan { |
| | | id?: number; // ç¼å· |
| | | no?: string; // 计ååå· |
| | | name?: string; // 计ååç§° |
| | | status?: number; // ç¶æï¼0æªæäº¤ 10审æ¹ä¸ 20å®¡æ ¸éè¿ 30å®¡æ ¸ä¸éè¿ 40已忶 |
| | | processInstanceId?: string; // 工使µç¼å· |
| | | demandSource?: number; // éæ±æ¥æºï¼1åºåé¢è¦ 2è¿ç»´/ç产计å |
| | | demandSourceName?: string; // éæ±æ¥æºåç§° |
| | | planDate?: string; // è®¡åæ¥æ |
| | | planUserId?: number; // ç¼å¶äººç¼å· |
| | | planUserName?: string; // ç¼å¶äººå§å |
| | | planDeptId?: number; // ç¼å¶é¨é¨ç¼å· |
| | | planDeptName?: string; // ç¼å¶é¨é¨åç§° |
| | | supplierId?: number; // ä¾åºåç¼å· |
| | | supplierName?: string; // ä¾åºååç§° |
| | | totalCount?: number; // å计æ°é |
| | | generatedFlag?: boolean; // æ¯å¦å·²çæéè´ç³è¯· |
| | | remark?: string; // 夿³¨ |
| | | productNames?: string; // 产ååç§°å表 |
| | | items?: PurchasePlanItem[]; // 计åæç» |
| | | createTime?: string; // å建æ¶é´ |
| | | } |
| | | |
| | | /** éè´è®¡åæç» */ |
| | | export interface PurchasePlanItem { |
| | | id?: number; // ç¼å· |
| | | productId: number; // 产åç¼å· |
| | | productName?: string; // 产ååç§° |
| | | productUnitId?: number; // 产ååä½ç¼å· |
| | | productUnitName?: string; // 产ååä½åç§° |
| | | count: number; // 计åéè´æ°é |
| | | demandTime?: string; // éæ±æ¥æ |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | } |
| | | |
| | | /** å建éè´è®¡å */ |
| | | export function createPurchasePlan(data: ErpPurchasePlanApi.PurchasePlan) { |
| | | return requestClient.post('/erp/purchase-plan/create', data); |
| | | } |
| | | |
| | | /** æ´æ°éè´è®¡å */ |
| | | export function updatePurchasePlan(data: ErpPurchasePlanApi.PurchasePlan) { |
| | | return requestClient.put('/erp/purchase-plan/update', data); |
| | | } |
| | | |
| | | /** æäº¤éè´è®¡åå®¡æ¹ */ |
| | | export function submitPurchasePlan(id: number, processDefinitionKey: string) { |
| | | return requestClient.put('/erp/purchase-plan/submit', null, { |
| | | params: { id, processDefinitionKey }, |
| | | }); |
| | | } |
| | | |
| | | /** æ ¹æ®åºåé¢è¦çæéè´è®¡å */ |
| | | export function generatePlanFromStockAlert(params: { name?: string; supplierId?: number }) { |
| | | return requestClient.post<number>('/erp/purchase-plan/generate-from-stock-alert', null, { |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | /** çæéè´ç³è¯· */ |
| | | export function generatePurchaseRequests(id: number) { |
| | | return requestClient.post<number[]>('/erp/purchase-plan/generate-requests', null, { |
| | | params: { id }, |
| | | }); |
| | | } |
| | | |
| | | /** å é¤éè´è®¡å */ |
| | | export function deletePurchasePlan(ids: number[]) { |
| | | return requestClient.delete('/erp/purchase-plan/delete', { params: { ids: ids.join(',') } }); |
| | | } |
| | | |
| | | /** è·åéè´è®¡å详æ
*/ |
| | | export function getPurchasePlan(id: number) { |
| | | return requestClient.get<ErpPurchasePlanApi.PurchasePlan>( |
| | | `/erp/purchase-plan/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åéè´è®¡åå页 */ |
| | | export function getPurchasePlanPage(params: PageParam) { |
| | | return requestClient.get<PageResult<ErpPurchasePlanApi.PurchasePlan>>( |
| | | '/erp/purchase-plan/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** 导åºéè´è®¡å Excel */ |
| | | export function exportPurchasePlan(params: any) { |
| | | return requestClient.download('/erp/purchase-plan/export-excel', { params }); |
| | | } |
| | | |
| | | /** è·åå®¡æ¹æµç¨å表 */ |
| | | export function getApproveProcessList(id: number) { |
| | | return requestClient.get(`/erp/purchase-plan/approve-process-list`, { params: { id } }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace HrmCertificateApi { |
| | | /** è¯ä¹¦èµè´¨ */ |
| | | export interface Certificate { |
| | | id?: number; |
| | | employeeId?: number; // åå·¥ç¼å· |
| | | employeeName?: string; // åå·¥å§å |
| | | certType?: number; // è¯ä¹¦ç±»åï¼1ç¹ç§ä½ä¸ 2ä»ä¸èµè´¨ 3æè½ç级 4å®å
¨å¹è® |
| | | certTypeName?: string; // è¯ä¹¦ç±»ååç§° |
| | | certName?: string; // è¯ä¹¦åç§° |
| | | certNo?: string; // è¯ä¹¦ç¼å· |
| | | issueOrg?: string; // åè¯æºæ |
| | | issueDate?: string; // åè¯æ¥æ |
| | | expireDate?: string; // æææè³ |
| | | expireStatus?: number; // å°æç¶æï¼0ææ 1å³å°å°æ 2å·²è¿æ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** å建è¯ä¹¦èµè´¨ */ |
| | | export function createCertificate(data: HrmCertificateApi.Certificate) { |
| | | return requestClient.post('/hrm/certificate/create', data); |
| | | } |
| | | |
| | | /** æ´æ°è¯ä¹¦èµè´¨ */ |
| | | export function updateCertificate(data: HrmCertificateApi.Certificate) { |
| | | return requestClient.put('/hrm/certificate/update', data); |
| | | } |
| | | |
| | | /** å é¤è¯ä¹¦èµè´¨ */ |
| | | export function deleteCertificate(ids: number[]) { |
| | | return requestClient.delete('/hrm/certificate/delete', { params: { ids: ids.join(',') } }); |
| | | } |
| | | |
| | | /** è·åè¯ä¹¦èµè´¨è¯¦æ
*/ |
| | | export function getCertificate(id: number) { |
| | | return requestClient.get<HrmCertificateApi.Certificate>( |
| | | `/hrm/certificate/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åè¯ä¹¦èµè´¨å页 */ |
| | | export function getCertificatePage(params: PageParam) { |
| | | return requestClient.get<PageResult<HrmCertificateApi.Certificate>>( |
| | | '/hrm/certificate/page', |
| | | { params }, |
| | | ); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 }, |
| | | ); |
| | | } |
| | |
| | | export interface Machinery { |
| | | id?: number; // 设å¤ç¼å· |
| | | code?: string; // 设å¤ç¼ç |
| | | tbDeviceId?: string; // æ°é设å¤IDï¼å¤é¨æ°é tbDeviceIdï¼ |
| | | name?: string; // 设å¤åç§° |
| | | brand?: string; // åç |
| | | specification?: string; // è§æ ¼åå· |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesDvStatisticsApi { |
| | | /** 设å¤ç»è®¡æ±æ» */ |
| | | export interface StatisticsSummary { |
| | | totalMachinery?: number; // è®¾å¤æ»æ° |
| | | producingCount?: number; // çäº§ä¸ |
| | | stoppedCount?: number; // åæº |
| | | maintainingCount?: number; // ç»´æ¤ä¸ |
| | | faultRate?: number; // æ
éç% |
| | | totalRepair?: number; // ç»´ä¿®åæ»æ° |
| | | processingRepair?: number; // ç»´ä¿®ä¸/å¾
éªæ¶ |
| | | finishedRepair?: number; // 已确认维修 |
| | | totalMainten?: number; // ä¿å
»è®°å½æ»æ° |
| | | finishedMainten?: number; // 已宿ä¿å
» |
| | | typeDistribution?: TypeItem[]; // æè®¾å¤ç±»ååå¸ |
| | | } |
| | | |
| | | /** 设å¤ç±»ååå¸é¡¹ */ |
| | | export interface TypeItem { |
| | | machineryTypeId?: number; |
| | | machineryTypeName?: string; |
| | | count?: number; |
| | | } |
| | | } |
| | | |
| | | /** 设å¤ç»è®¡æ±æ» */ |
| | | export function getDvStatisticsSummary() { |
| | | return requestClient.get<MesDvStatisticsApi.StatisticsSummary>( |
| | | '/mes/dv/statistics/summary', |
| | | ); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesDvTelemetryApi { |
| | | /** MES è®¾å¤æ°é饿µæ°æ® */ |
| | | export interface Telemetry { |
| | | id?: number; // ç¼å· |
| | | tbDeviceId?: string; // æ°é设å¤ID |
| | | deviceName?: string; // 设å¤åç§° |
| | | paramName?: string; // ä¿¡å·åç§° |
| | | paramKeyName?: string; // 设å¤KEYåç§° |
| | | standardValue?: string; // æ åå¼ |
| | | timelyValue?: string; // ä¿¡å·å¼(宿¶) |
| | | avgValue?: string; // åå¼ |
| | | maxValue?: string; // æå¤§å¼ |
| | | minValue?: string; // æå°å¼ |
| | | whetherAnomaly?: boolean; // æ¯å¦å¼å¸¸ |
| | | telemetryDataTime?: Date; // 饿µæ°æ®æ¶é´ |
| | | billNo?: string; // åæ®å· |
| | | shiftName?: string; // çæ¬¡ |
| | | pullTime?: Date; // æåæ¶é´ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | |
| | | /** æåæ°éæ°æ®ç»æ */ |
| | | export interface PullRespVO { |
| | | recordCount?: number; // æ¬æ¬¡æåæ°å¢æ°æ®æ¡æ° |
| | | deviceCount?: number; // æ¬æ¬¡æåæ¶åè®¾å¤æ° |
| | | pullTime?: Date; // æåæ¶é´ |
| | | } |
| | | |
| | | /** æ°é设å¤ä¸æé¡¹ */ |
| | | export interface DeviceItem { |
| | | tbDeviceId?: string; |
| | | deviceName?: string; |
| | | } |
| | | |
| | | /** ææ°é¥æµæ¥è¯¢åæ° */ |
| | | export interface LatestReq { |
| | | tbDeviceId?: string; |
| | | deviceName?: string; |
| | | } |
| | | |
| | | /** ä¾çµåæ°æ¯å¯¹æ ¡éªç»è®¡ï¼æè®¾å¤èåå¼å¸¸çï¼ */ |
| | | export interface CheckSummary { |
| | | tbDeviceId?: string; // æ°é设å¤ID |
| | | deviceName?: string; // 设å¤åç§° |
| | | totalCount?: number; // åæ°æ»æ° |
| | | normalCount?: number; // æ£å¸¸æ°é |
| | | anomalyCount?: number; // å¼å¸¸æ°é |
| | | anomalyRate?: number; // å¼å¸¸ç(%) |
| | | } |
| | | } |
| | | |
| | | /** æåæ°éæ°æ®å¹¶å
¥åºï¼è¿ 5 åéè®¾å¤æ°æ®ï¼ */ |
| | | export function pullTelemetry() { |
| | | return requestClient.get<MesDvTelemetryApi.PullRespVO>('/mes/dv/telemetry/pull'); |
| | | } |
| | | |
| | | /** å页æ¥è¯¢æ°é饿µæ°æ®ï¼åå²è®°å½ï¼ */ |
| | | export function getTelemetryPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesDvTelemetryApi.Telemetry>>( |
| | | '/mes/dv/telemetry/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢åè®¾å¤ææ°é¥æµæ°æ®ï¼å®æ¶çæ§ï¼ */ |
| | | export function getLatestTelemetry(params: MesDvTelemetryApi.LatestReq = {}) { |
| | | return requestClient.get<MesDvTelemetryApi.Telemetry[]>( |
| | | '/mes/dv/telemetry/latest', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢æ°é设å¤ä¸æå表 */ |
| | | export function getTelemetryDeviceList() { |
| | | return requestClient.get<MesDvTelemetryApi.DeviceItem[]>( |
| | | '/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 }, |
| | | ); |
| | | } |
| | |
| | | safeStockFlag?: boolean; // æ¯å¦å¯ç¨å®å
¨åºå |
| | | minStock?: number; // æä½åºåé |
| | | maxStock?: number; // æé«åºåé |
| | | materialCategory?: number; // çµåç©èµåç±» |
| | | highValue?: boolean; // æ¯å¦é«å¼ç©æ |
| | | batchFlag?: boolean; // æ¯å¦å¯ç¨æ¹æ¬¡ç®¡ç |
| | | remark?: string; // 夿³¨ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesPdBaseDataApi { |
| | | /** ç¨çµç±»å */ |
| | | export interface ElectricityType { |
| | | id?: number; // ç¼å· |
| | | code?: string; // ç±»åç¼ç |
| | | name?: string; // ç±»ååç§° |
| | | description?: string; // ç±»å说æ |
| | | sort?: number; // æåº |
| | | status?: number; // å¯ç¨ç¶æï¼1å¯ç¨/0ç¦ç¨ï¼ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | |
| | | /** çµåç级 */ |
| | | export interface VoltageLevel { |
| | | id?: number; // ç¼å· |
| | | code?: string; // ç级ç¼ç |
| | | name?: string; // ç级åç§° |
| | | voltageValue?: number; // çµåæ°å¼ï¼kVï¼ |
| | | sort?: number; // æåº |
| | | status?: number; // å¯ç¨ç¶æï¼1å¯ç¨/0ç¦ç¨ï¼ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | |
| | | /** æå¡å¥é¤ */ |
| | | export interface ServicePackage { |
| | | id?: number; // ç¼å· |
| | | code?: string; // å¥é¤ç¼ç |
| | | name?: string; // å¥é¤åç§° |
| | | description?: string; // å¥é¤è¯´æ |
| | | price?: number; // å¥é¤ä»·æ ¼ |
| | | effectiveDate?: Date; // çææ¥æ |
| | | expireDate?: Date; // å¤±ææ¥æ |
| | | status?: number; // å¯ç¨ç¶æï¼1å¯ç¨/0ç¦ç¨ï¼ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | |
| | | /** å®ä»·æ å */ |
| | | export interface PriceStandard { |
| | | id?: number; // ç¼å· |
| | | objectType?: number; // å®ä»·å¯¹è±¡ç±»åï¼1æå¡å¥é¤/2çµåç级/3ç¨çµç±»åï¼ |
| | | objectTypeName?: string; // å®ä»·å¯¹è±¡ç±»ååç§° |
| | | objectId?: number; // å®ä»·å¯¹è±¡ID |
| | | objectName?: string; // å®ä»·å¯¹è±¡åç§° |
| | | price?: number; // ä»·æ ¼ |
| | | unit?: string; // 计价åä½ |
| | | effectiveDate?: Date; // çææ¥æ |
| | | status?: number; // å¯ç¨ç¶æï¼1å¯ç¨/0ç¦ç¨ï¼ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | |
| | | /** 伿 çç¥ */ |
| | | export interface DiscountPolicy { |
| | | id?: number; // ç¼å· |
| | | name?: string; // çç¥åç§° |
| | | policyType?: number; // çç¥ç±»åï¼1ææ£/2ç«åï¼ |
| | | policyTypeName?: string; // çç¥ç±»ååç§° |
| | | policyValue?: number; // çç¥å¼ï¼ææ£ï¼0-1 æ¯ä¾ï¼ç«åï¼éé¢ï¼ |
| | | targetType?: number; // éç¨å¯¹è±¡ç±»åï¼1æå¡å¥é¤/2çµåç级/3ç¨çµç±»åï¼å¯ç©º=å
¨å±ï¼ |
| | | targetId?: number; // éç¨å¯¹è±¡ID |
| | | startDate?: Date; // çææ¥æ |
| | | endDate?: Date; // å¤±ææ¥æ |
| | | status?: number; // å¯ç¨ç¶æï¼1å¯ç¨/0ç¦ç¨ï¼ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | |
| | | /** é¶æ¢¯çµä»· */ |
| | | export interface TieredPrice { |
| | | id?: number; // ç¼å· |
| | | packageId?: number; // æå¡å¥é¤ID |
| | | voltageLevelId?: number; // çµåç级ID |
| | | startValue?: number; // æ¡£ä½ä¸éï¼å«ï¼kWhï¼ |
| | | endValue?: number; // æ¡£ä½ä¸éï¼ä¸å«ï¼kWhï¼å¯ç©º=ä¸ä¸å°é¡¶ï¼ |
| | | pricePerUnit?: number; // æ¡£ä½åä»· |
| | | status?: number; // å¯ç¨ç¶æï¼1å¯ç¨/0ç¦ç¨ï¼ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | } |
| | | |
| | | // ==================== ç¨çµç±»å ==================== |
| | | |
| | | /** æ¥è¯¢ç¨çµç±»åå页 */ |
| | | export function getElectricityTypePage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdBaseDataApi.ElectricityType>>( |
| | | '/mes/pd/electricity-type/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢ç¨çµç±»å详æ
*/ |
| | | export function getElectricityType(id: number) { |
| | | return requestClient.get<MesPdBaseDataApi.ElectricityType>( |
| | | `/mes/pd/electricity-type/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢ç¨çµç±»åå表 */ |
| | | export function getElectricityTypeList() { |
| | | return requestClient.get<MesPdBaseDataApi.ElectricityType[]>( |
| | | '/mes/pd/electricity-type/list', |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢ç¨çµç±»å */ |
| | | export function createElectricityType(data: MesPdBaseDataApi.ElectricityType) { |
| | | return requestClient.post('/mes/pd/electricity-type/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹ç¨çµç±»å */ |
| | | export function updateElectricityType(data: MesPdBaseDataApi.ElectricityType) { |
| | | return requestClient.put('/mes/pd/electricity-type/update', data); |
| | | } |
| | | |
| | | /** å é¤ç¨çµç±»å */ |
| | | export function deleteElectricityType(id: number) { |
| | | return requestClient.delete(`/mes/pd/electricity-type/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºç¨çµç±»å Excel */ |
| | | export function exportElectricityType(params: any) { |
| | | return requestClient.download('/mes/pd/electricity-type/export-excel', { |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // ==================== çµåç级 ==================== |
| | | |
| | | /** æ¥è¯¢çµåç级å页 */ |
| | | export function getVoltageLevelPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdBaseDataApi.VoltageLevel>>( |
| | | '/mes/pd/voltage-level/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢çµåç级详æ
*/ |
| | | export function getVoltageLevel(id: number) { |
| | | return requestClient.get<MesPdBaseDataApi.VoltageLevel>( |
| | | `/mes/pd/voltage-level/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢çµåç级å表 */ |
| | | export function getVoltageLevelList() { |
| | | return requestClient.get<MesPdBaseDataApi.VoltageLevel[]>( |
| | | '/mes/pd/voltage-level/list', |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢çµåç级 */ |
| | | export function createVoltageLevel(data: MesPdBaseDataApi.VoltageLevel) { |
| | | return requestClient.post('/mes/pd/voltage-level/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹çµåç级 */ |
| | | export function updateVoltageLevel(data: MesPdBaseDataApi.VoltageLevel) { |
| | | return requestClient.put('/mes/pd/voltage-level/update', data); |
| | | } |
| | | |
| | | /** å é¤çµåç级 */ |
| | | export function deleteVoltageLevel(id: number) { |
| | | return requestClient.delete(`/mes/pd/voltage-level/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºçµåç级 Excel */ |
| | | export function exportVoltageLevel(params: any) { |
| | | return requestClient.download('/mes/pd/voltage-level/export-excel', { params }); |
| | | } |
| | | |
| | | // ==================== æå¡å¥é¤ ==================== |
| | | |
| | | /** æ¥è¯¢æå¡å¥é¤å页 */ |
| | | export function getServicePackagePage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdBaseDataApi.ServicePackage>>( |
| | | '/mes/pd/service-package/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢æå¡å¥é¤è¯¦æ
*/ |
| | | export function getServicePackage(id: number) { |
| | | return requestClient.get<MesPdBaseDataApi.ServicePackage>( |
| | | `/mes/pd/service-package/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢æå¡å¥é¤å表 */ |
| | | export function getServicePackageList() { |
| | | return requestClient.get<MesPdBaseDataApi.ServicePackage[]>( |
| | | '/mes/pd/service-package/list', |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢æå¡å¥é¤ */ |
| | | export function createServicePackage(data: MesPdBaseDataApi.ServicePackage) { |
| | | return requestClient.post('/mes/pd/service-package/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹æå¡å¥é¤ */ |
| | | export function updateServicePackage(data: MesPdBaseDataApi.ServicePackage) { |
| | | return requestClient.put('/mes/pd/service-package/update', data); |
| | | } |
| | | |
| | | /** å 餿å¡å¥é¤ */ |
| | | export function deleteServicePackage(id: number) { |
| | | return requestClient.delete(`/mes/pd/service-package/delete?id=${id}`); |
| | | } |
| | | |
| | | /** å¯¼åºæå¡å¥é¤ Excel */ |
| | | export function exportServicePackage(params: any) { |
| | | return requestClient.download('/mes/pd/service-package/export-excel', { |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // ==================== å®ä»·æ å ==================== |
| | | |
| | | /** æ¥è¯¢å®ä»·æ åå页 */ |
| | | export function getPriceStandardPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdBaseDataApi.PriceStandard>>( |
| | | '/mes/pd/price-standard/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢å®ä»·æ å详æ
*/ |
| | | export function getPriceStandard(id: number) { |
| | | return requestClient.get<MesPdBaseDataApi.PriceStandard>( |
| | | `/mes/pd/price-standard/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢å®ä»·æ å */ |
| | | export function createPriceStandard(data: MesPdBaseDataApi.PriceStandard) { |
| | | return requestClient.post('/mes/pd/price-standard/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹å®ä»·æ å */ |
| | | export function updatePriceStandard(data: MesPdBaseDataApi.PriceStandard) { |
| | | return requestClient.put('/mes/pd/price-standard/update', data); |
| | | } |
| | | |
| | | /** å é¤å®ä»·æ å */ |
| | | export function deletePriceStandard(id: number) { |
| | | return requestClient.delete(`/mes/pd/price-standard/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºå®ä»·æ å Excel */ |
| | | export function exportPriceStandard(params: any) { |
| | | return requestClient.download('/mes/pd/price-standard/export-excel', { params }); |
| | | } |
| | | |
| | | // ==================== 伿 çç¥ ==================== |
| | | |
| | | /** æ¥è¯¢ä¼æ çç¥å页 */ |
| | | export function getDiscountPolicyPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdBaseDataApi.DiscountPolicy>>( |
| | | '/mes/pd/discount-policy/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢ä¼æ çç¥è¯¦æ
*/ |
| | | export function getDiscountPolicy(id: number) { |
| | | return requestClient.get<MesPdBaseDataApi.DiscountPolicy>( |
| | | `/mes/pd/discount-policy/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢ä¼æ çç¥ */ |
| | | export function createDiscountPolicy(data: MesPdBaseDataApi.DiscountPolicy) { |
| | | return requestClient.post('/mes/pd/discount-policy/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹ä¼æ çç¥ */ |
| | | export function updateDiscountPolicy(data: MesPdBaseDataApi.DiscountPolicy) { |
| | | return requestClient.put('/mes/pd/discount-policy/update', data); |
| | | } |
| | | |
| | | /** å é¤ä¼æ çç¥ */ |
| | | export function deleteDiscountPolicy(id: number) { |
| | | return requestClient.delete(`/mes/pd/discount-policy/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºä¼æ çç¥ Excel */ |
| | | export function exportDiscountPolicy(params: any) { |
| | | return requestClient.download('/mes/pd/discount-policy/export-excel', { |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | // ==================== é¶æ¢¯çµä»· ==================== |
| | | |
| | | /** æ¥è¯¢é¶æ¢¯çµä»·å页 */ |
| | | export function getTieredPricePage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdBaseDataApi.TieredPrice>>( |
| | | '/mes/pd/tiered-price/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢é¶æ¢¯çµä»·è¯¦æ
*/ |
| | | export function getTieredPrice(id: number) { |
| | | return requestClient.get<MesPdBaseDataApi.TieredPrice>( |
| | | `/mes/pd/tiered-price/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢é¶æ¢¯çµä»· */ |
| | | export function createTieredPrice(data: MesPdBaseDataApi.TieredPrice) { |
| | | return requestClient.post('/mes/pd/tiered-price/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹é¶æ¢¯çµä»· */ |
| | | export function updateTieredPrice(data: MesPdBaseDataApi.TieredPrice) { |
| | | return requestClient.put('/mes/pd/tiered-price/update', data); |
| | | } |
| | | |
| | | /** å é¤é¶æ¢¯çµä»· */ |
| | | export function deleteTieredPrice(id: number) { |
| | | return requestClient.delete(`/mes/pd/tiered-price/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºé¶æ¢¯çµä»· Excel */ |
| | | export function exportTieredPrice(params: any) { |
| | | return requestClient.download('/mes/pd/tiered-price/export-excel', { params }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesPdCtcReelApi { |
| | | /** æ¢ä½å¯¼çº¿çå
·è®¡ç® */ |
| | | export interface CtcReel { |
| | | id?: number; // ç¼å· |
| | | ctcId?: number; // æ¢ä½å¯¼çº¿äº§åID |
| | | ctcOrderSpec?: string; // æ¢ä½å¯¼çº¿è®¢è´§è§æ ¼ |
| | | reelType?: string; // çå
፱Ȍ |
| | | reelOuterDiameter?: number; // çå
·å¤å¾ mm |
| | | reelInnerDiameter?: number; // çè¯å
å¾ mm |
| | | reelWidth?: number; // ç宽 mm |
| | | maxCapacity?: number; // æå¤§å®¹é km |
| | | recommendedLength?: number; // æ¨èæ¯çé¿åº¦ km |
| | | netWeight?: number; // åé kg |
| | | grossWeight?: number; // æ¯é kg |
| | | calcFormula?: string; // 计ç®å
¬å¼ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢çå
·è®¡ç®å页 */ |
| | | export function getCtcReelPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdCtcReelApi.CtcReel>>( |
| | | '/mes/pd/ctc-reel/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢çå
·è®¡ç®è¯¦æ
*/ |
| | | export function getCtcReel(id: number) { |
| | | return requestClient.get<MesPdCtcReelApi.CtcReel>( |
| | | `/mes/pd/ctc-reel/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢æå®æ¢ä½å¯¼çº¿äº§åççå
·å表 */ |
| | | export function getCtcReelListByCtcId(ctcId: number) { |
| | | return requestClient.get<MesPdCtcReelApi.CtcReel[]>( |
| | | `/mes/pd/ctc-reel/list-by-ctc?ctcId=${ctcId}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢çå
·è®¡ç®è®°å½ */ |
| | | export function createCtcReel(data: MesPdCtcReelApi.CtcReel) { |
| | | return requestClient.post('/mes/pd/ctc-reel/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹çå
·è®¡ç®è®°å½ */ |
| | | export function updateCtcReel(data: MesPdCtcReelApi.CtcReel) { |
| | | return requestClient.put('/mes/pd/ctc-reel/update', data); |
| | | } |
| | | |
| | | /** å é¤çå
·è®¡ç®è®°å½ */ |
| | | export function deleteCtcReel(id: number) { |
| | | return requestClient.delete(`/mes/pd/ctc-reel/delete?id=${id}`); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesPdCtcApi { |
| | | /** æ¢ä½å¯¼çº¿äº§åææ¯åæ° */ |
| | | export interface Ctc { |
| | | id?: number; // ç¼å· |
| | | itemId?: number; // ç©æID |
| | | itemName?: string; // ç©æåç§° |
| | | itemCode?: string; // ç©æç¼ç |
| | | orderSpec?: string; // è®¢è´§è§æ ¼ |
| | | bareConductorSpec?: string; // è£¸å¯¼çº¿è§æ ¼ |
| | | enameledSpec?: string; // æ¼å
çº¿è§æ ¼ |
| | | transpositionSpec?: string; // æ¢ä½è§æ ¼ |
| | | conductorType?: string; // 导ä½ç±»å |
| | | crossSection?: number; // æªé¢ç§¯ mm² |
| | | outerDiameter?: number; // å¤å¾ mm |
| | | resistance20c?: number; // 20âç´æµçµé» Ω/km |
| | | wrappingAngle?: number; // ç»å
è§åº¦ ° |
| | | insulationThickness?: number; // ç»ç¼å度 mm |
| | | ratedVoltage?: string; // é¢å®çµå kV |
| | | testVoltage?: string; // è¯éªçµå kV |
| | | minBendingRadius?: number; // æå°å¼¯æ²åå¾ mm |
| | | weightPerKm?: number; // æ¯å
¬ééé kg/km |
| | | standardCode?: string; // æ§è¡æ åä»£å· |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | |
| | | /** ç»å
è§åº¦è®¡ç® */ |
| | | export interface AngleCalcReq { |
| | | conductorThickness?: number; // 导ä½å度 mm |
| | | conductorWidth?: number; // 导ä½å®½åº¦ mm |
| | | cornerRadius?: number; // åè§åå¾ mm |
| | | insulationThickness?: number; // ç»ç¼å度 mm |
| | | tapeWidth?: number; // 纸带宽度 mm |
| | | overlapWidth?: number; // éå 宽度 mm |
| | | } |
| | | |
| | | /** ç»å
è§åº¦è®¡ç®ç»æ */ |
| | | export interface AngleCalcResp { |
| | | circumference?: number; // 导线å¨é¿ mm |
| | | wrappingAngle?: number; // ç»å
è§åº¦ ° |
| | | wrappingPitch?: number; // ç»å
èè· mm |
| | | } |
| | | |
| | | /** ç´æµçµé»è®¡ç® */ |
| | | export interface ResistanceCalcReq { |
| | | conductorThickness?: number; // 导ä½å度 mm |
| | | conductorWidth?: number; // 导ä½å®½åº¦ mm |
| | | temperature?: number; // è®¡ç®æ¸©åº¦ â |
| | | material?: string; // å¯¼ä½æè´¨ |
| | | } |
| | | |
| | | /** ç´æµçµé»è®¡ç®ç»æ */ |
| | | export interface ResistanceCalcResp { |
| | | crossSection?: number; // æªé¢ç§¯ mm² |
| | | resistance20cKm?: number; // 20âç´æµçµé» Ω/km |
| | | resistanceAtTemp?: number; // æå®æ¸©åº¦ç´æµçµé» Ω/m |
| | | resistanceAtTempKm?: number; // æå®æ¸©åº¦ç´æµçµé» Ω/km |
| | | } |
| | | |
| | | /** çå
·å®¹éè®¡ç® */ |
| | | export interface ReelCalcReq { |
| | | reelOuterDiameter?: number; // çå
·å¤å¾ d1 mm |
| | | reelInnerDiameter?: number; // çè¯å
å¾ d2 mm |
| | | reelWidth?: number; // ç宽 l2 mm |
| | | ctcThickness?: number; // CTC æå¤§é«åº¦ mm |
| | | ctcWidth?: number; // CTC æå¤§å®½åº¦ mm |
| | | } |
| | | |
| | | /** çå
·å®¹é计ç®ç»æ */ |
| | | export interface ReelCalcResp { |
| | | capacityMeter?: number; // æ¯çé¿åº¦ m |
| | | capacityKm?: number; // æ¯çé¿åº¦ km |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢æ¢ä½å¯¼çº¿äº§åææ¯åæ°å页 */ |
| | | export function getCtcPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdCtcApi.Ctc>>('/mes/pd/ctc/page', { |
| | | params, |
| | | }); |
| | | } |
| | | |
| | | /** æ¥è¯¢æ¢ä½å¯¼çº¿äº§åææ¯åæ°è¯¦æ
*/ |
| | | export function getCtc(id: number) { |
| | | return requestClient.get<MesPdCtcApi.Ctc>(`/mes/pd/ctc/get?id=${id}`); |
| | | } |
| | | |
| | | /** æ°å¢æ¢ä½å¯¼çº¿äº§åææ¯åæ° */ |
| | | export function createCtc(data: MesPdCtcApi.Ctc) { |
| | | return requestClient.post('/mes/pd/ctc/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹æ¢ä½å¯¼çº¿äº§åææ¯åæ° */ |
| | | export function updateCtc(data: MesPdCtcApi.Ctc) { |
| | | return requestClient.put('/mes/pd/ctc/update', data); |
| | | } |
| | | |
| | | /** å 餿¢ä½å¯¼çº¿äº§åææ¯åæ° */ |
| | | export function deleteCtc(id: number) { |
| | | return requestClient.delete(`/mes/pd/ctc/delete?id=${id}`); |
| | | } |
| | | |
| | | /** å¯¼åºæ¢ä½å¯¼çº¿äº§åææ¯åæ° */ |
| | | export function exportCtc(params: any) { |
| | | return requestClient.download('/mes/pd/ctc/export-excel', { params }); |
| | | } |
| | | |
| | | /** ç»å
è§åº¦è®¡ç® */ |
| | | export function calcCtcAngle(data: MesPdCtcApi.AngleCalcReq) { |
| | | return requestClient.post<MesPdCtcApi.AngleCalcResp>('/mes/pd/ctc/calc-angle', data); |
| | | } |
| | | |
| | | /** ç´æµçµé»è®¡ç® */ |
| | | export function calcCtcResistance(data: MesPdCtcApi.ResistanceCalcReq) { |
| | | return requestClient.post<MesPdCtcApi.ResistanceCalcResp>( |
| | | '/mes/pd/ctc/calc-resistance', |
| | | data, |
| | | ); |
| | | } |
| | | |
| | | /** çå
·å®¹éè®¡ç® */ |
| | | export function calcCtcReel(data: MesPdCtcApi.ReelCalcReq) { |
| | | return requestClient.post<MesPdCtcApi.ReelCalcResp>('/mes/pd/ctc/calc-reel', data); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesPdPriceApi { |
| | | /** é¶æ¢¯çµä»·æµç®è¯·æ± */ |
| | | export interface PriceCalcReq { |
| | | calcType: number; // æµç®ç±»åï¼1æå¡å¥é¤/2çµåççº§ï¼ |
| | | packageId?: number; // æå¡å¥é¤IDï¼calcType=1 æ¶å¿
å¡«ï¼ |
| | | voltageLevelId?: number; // çµåç级IDï¼calcType=2 æ¶å¿
å¡«ï¼ |
| | | quantity: number; // ç¨çµéï¼kWhï¼ |
| | | } |
| | | |
| | | /** é¶æ¢¯çµä»·æµç®åæ¡£æç» */ |
| | | export interface PriceCalcDetail { |
| | | startValue?: number; // æ¡£ä½ä¸éï¼å«ï¼kWhï¼ |
| | | endValue?: number; // æ¡£ä½ä¸éï¼ä¸å«ï¼kWhï¼å¯ç©º=ä¸ä¸å°é¡¶ï¼ |
| | | tierQuantity?: number; // è½å
¥æ¬æ¡£çµéï¼kWhï¼ |
| | | pricePerUnit?: number; // æ¡£ä½åä»· |
| | | subtotal?: number; // å°è®¡ |
| | | } |
| | | |
| | | /** é¶æ¢¯çµä»·æµç®ååº */ |
| | | export interface PriceCalcResp { |
| | | packageName?: string; // æå¡å¥é¤åç§° |
| | | voltageLevelName?: string; // çµåç级åç§° |
| | | originalPrice?: number; // åä»·ï¼æªä¼æ ï¼ |
| | | discountAmount?: number; // 伿 éé¢ |
| | | finalPrice?: number; // 伿 忻价 |
| | | details?: PriceCalcDetail[]; // åæ¡£æç» |
| | | } |
| | | } |
| | | |
| | | /** é¶æ¢¯çµä»·æµç® */ |
| | | export function calcPrice(data: MesPdPriceApi.PriceCalcReq) { |
| | | return requestClient.post<MesPdPriceApi.PriceCalcResp>( |
| | | '/mes/pd/price/calc', |
| | | data, |
| | | ); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesPdProductApi { |
| | | /** äº§åæ¡£æ¡ */ |
| | | export interface Product { |
| | | id?: number; // ç¼å· |
| | | archiveType?: number; // æ¡£æ¡ç±»åï¼1çµåæå¡/2ä¾çµäº§å/3å¢å¼è½æºäº§åï¼ |
| | | archiveTypeName?: string; // æ¡£æ¡ç±»ååç§° |
| | | code?: string; // æ¡£æ¡ç¼ç |
| | | name?: string; // æ¡£æ¡åç§° |
| | | spec?: string; // è§æ ¼åå· |
| | | unit?: string; // åä½ |
| | | price?: number; // åºåä»·æ ¼ |
| | | auditStatus?: number; // 审æ¹ç¶æï¼0æªæäº¤/10审æ¹ä¸/20å®¡æ ¸éè¿/30å®¡æ ¸ä¸éè¿ï¼ |
| | | auditStatusName?: string; // 审æ¹ç¶æåç§° |
| | | auditRemark?: string; // å®¡æ¹æè§ |
| | | auditTime?: Date; // å®¡æ¹æ¶é´ |
| | | status?: number; // å¯ç¨ç¶æï¼1å¯ç¨/0ç¦ç¨ï¼ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | |
| | | /** äº§åæ¡£æ¡å®¡æ¹åæ° */ |
| | | export interface ProductAudit { |
| | | id: number; // ç¼å· |
| | | pass: boolean; // 审æ¹ç»æï¼true=éè¿ / false=驳å |
| | | auditRemark?: string; // å®¡æ¹æè§ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢äº§åæ¡£æ¡å页 */ |
| | | export function getProductPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdProductApi.Product>>( |
| | | '/mes/pd/product/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢äº§åæ¡£æ¡è¯¦æ
*/ |
| | | export function getProduct(id: number) { |
| | | return requestClient.get<MesPdProductApi.Product>( |
| | | `/mes/pd/product/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢äº§åæ¡£æ¡ */ |
| | | export function createProduct(data: MesPdProductApi.Product) { |
| | | return requestClient.post('/mes/pd/product/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹äº§åæ¡£æ¡ */ |
| | | export function updateProduct(data: MesPdProductApi.Product) { |
| | | return requestClient.put('/mes/pd/product/update', data); |
| | | } |
| | | |
| | | /** å é¤äº§åæ¡£æ¡ */ |
| | | export function deleteProduct(id: number) { |
| | | return requestClient.delete(`/mes/pd/product/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºäº§åæ¡£æ¡ Excel */ |
| | | export function exportProduct(params: any) { |
| | | return requestClient.download('/mes/pd/product/export-excel', { params }); |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ï¼æªæäº¤/驳å â 审æ¹ä¸ï¼ */ |
| | | export function submitProduct(id: number) { |
| | | return requestClient.put(`/mes/pd/product/submit?id=${id}`); |
| | | } |
| | | |
| | | /** 审æ¹äº§åæ¡£æ¡ï¼å®¡æ¹ä¸ â éè¿/驳åï¼ */ |
| | | export function auditProduct(data: MesPdProductApi.ProductAudit) { |
| | | return requestClient.put('/mes/pd/product/audit', data); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesPdTechConfirmApi { |
| | | /** äº§åææ¯ç¡®è®¤å½ */ |
| | | export interface TechConfirm { |
| | | id?: number; // ç¼å· |
| | | confirmNo?: string; // 确认å½ç¼å· |
| | | customerId?: number; // 客æ·ID |
| | | customerName?: string; // 客æ·åç§° |
| | | productName?: string; // 产ååç§° |
| | | specification?: string; // è§æ ¼åå· |
| | | techParams?: string; // ææ¯åæ° JSON |
| | | templateType?: string; // 模æ¿ç±»å |
| | | drafterId?: number; // ç¼å¶äººID |
| | | drafterName?: string; // ç¼å¶äººå§å |
| | | draftTime?: Date; // ç¼å¶æ¶é´ |
| | | reviewerId?: number; // å®¡æ ¸äººID |
| | | reviewerName?: string; // å®¡æ ¸äººå§å |
| | | reviewTime?: Date; // å®¡æ ¸æ¶é´ |
| | | customerConfirmUser?: string; // 客æ·ç¡®è®¤äºº |
| | | customerConfirmTime?: Date; // 客æ·ç¡®è®¤æ¶é´ |
| | | status?: number; // ç¶æ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢äº§åææ¯ç¡®è®¤å½å页 */ |
| | | export function getTechConfirmPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesPdTechConfirmApi.TechConfirm>>( |
| | | '/mes/pd/tech-confirm/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢äº§åææ¯ç¡®è®¤å½è¯¦æ
*/ |
| | | export function getTechConfirm(id: number) { |
| | | return requestClient.get<MesPdTechConfirmApi.TechConfirm>( |
| | | `/mes/pd/tech-confirm/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** æ°å¢äº§åææ¯ç¡®è®¤å½ */ |
| | | export function createTechConfirm(data: MesPdTechConfirmApi.TechConfirm) { |
| | | return requestClient.post('/mes/pd/tech-confirm/create', data); |
| | | } |
| | | |
| | | /** ä¿®æ¹äº§åææ¯ç¡®è®¤å½ */ |
| | | export function updateTechConfirm(data: MesPdTechConfirmApi.TechConfirm) { |
| | | return requestClient.put('/mes/pd/tech-confirm/update', data); |
| | | } |
| | | |
| | | /** å é¤äº§åææ¯ç¡®è®¤å½ */ |
| | | export function deleteTechConfirm(id: number) { |
| | | return requestClient.delete(`/mes/pd/tech-confirm/delete?id=${id}`); |
| | | } |
| | | |
| | | /** 导åºäº§åææ¯ç¡®è®¤å½ */ |
| | | export function exportTechConfirm(params: any) { |
| | | return requestClient.download('/mes/pd/tech-confirm/export-excel', { 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 MesProEmergencyPlanApi { |
| | | /** åºæ¥å¤ç½®é¢æ¡ */ |
| | | export interface EmergencyPlan { |
| | | id?: number; |
| | | code?: string; // 颿¡ç¼ç |
| | | name?: string; // 颿¡åç§° |
| | | planType?: number; // 颿¡ç±»åï¼1åçµ 2è®¾å¤æ
é 3ç«ç¾ 4èªç¶ç¾å®³ 5å
¶ä» |
| | | content?: string; // å¤ç½®å
容 |
| | | orgUnit?: string; // 责任åä½ |
| | | status?: number; // ç¶æï¼0è稿 10å·²åå¸ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** åå»ºåºæ¥å¤ç½®é¢æ¡ */ |
| | | export function createPlan(data: MesProEmergencyPlanApi.EmergencyPlan) { |
| | | return requestClient.post('/mes/pro/emergency-plan/create', data); |
| | | } |
| | | |
| | | /** æ´æ°åºæ¥å¤ç½®é¢æ¡ */ |
| | | export function updatePlan(data: MesProEmergencyPlanApi.EmergencyPlan) { |
| | | return requestClient.put('/mes/pro/emergency-plan/update', data); |
| | | } |
| | | |
| | | /** å é¤åºæ¥å¤ç½®é¢æ¡ */ |
| | | export function deletePlan(ids: number[]) { |
| | | return requestClient.delete('/mes/pro/emergency-plan/delete', { params: { ids: ids.join(',') } }); |
| | | } |
| | | |
| | | /** è·ååºæ¥å¤ç½®é¢æ¡ */ |
| | | export function getPlan(id: number) { |
| | | return requestClient.get<MesProEmergencyPlanApi.EmergencyPlan>( |
| | | `/mes/pro/emergency-plan/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·ååºæ¥å¤ç½®é¢æ¡å页 */ |
| | | export function getPlanPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesProEmergencyPlanApi.EmergencyPlan>>( |
| | | '/mes/pro/emergency-plan/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** åå¸åºæ¥å¤ç½®é¢æ¡ */ |
| | | export function publishPlan(id: number) { |
| | | return requestClient.put(`/mes/pro/emergency-plan/publish?id=${id}`); |
| | | } |
| | |
| | | `/mes/pro/feedback/list-by-task?taskId=${taskId}`, |
| | | ); |
| | | } |
| | | |
| | | /** ä½ä¸æææ±æ»é¡¹ï¼æå·¥åç±»åï¼ */ |
| | | export interface WorkResultTypeItem { |
| | | workOrderType?: number; |
| | | workOrderTypeName?: string; |
| | | feedbackQuantity?: number; |
| | | qualifiedQuantity?: number; |
| | | feedbackCount?: number; |
| | | } |
| | | |
| | | /** ä½ä¸æææ±æ»é¡¹ï¼ææ¥æï¼ */ |
| | | export interface WorkResultDateItem { |
| | | date?: string; |
| | | feedbackQuantity?: number; |
| | | qualifiedQuantity?: number; |
| | | feedbackCount?: number; |
| | | } |
| | | |
| | | /** ä½ä¸æææ±æ» */ |
| | | export interface WorkResultSummary { |
| | | totalFeedbackQuantity?: number; |
| | | totalQualifiedQuantity?: number; |
| | | totalFeedbackCount?: number; |
| | | byType?: WorkResultTypeItem[]; |
| | | byDate?: WorkResultDateItem[]; |
| | | } |
| | | |
| | | /** ä½ä¸æææ±æ»ç»è®¡ */ |
| | | export function getFeedbackSummary(params: { |
| | | beginDate?: string; |
| | | endDate?: string; |
| | | workOrderType?: number; |
| | | }) { |
| | | return requestClient.get<WorkResultSummary>('/mes/pro/feedback/summary', { |
| | | params, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesProMaintenancePlanApi { |
| | | /** çµåä½ä¸è®¡å */ |
| | | export interface MaintenancePlan { |
| | | id?: number; // ç¼å· |
| | | code?: string; // 计åç¼ç |
| | | name?: string; // 计ååç§° |
| | | planType?: number; // 计åç±»åï¼1çµç½è¿è¡ 2åçµ 3åçµè¿ç»´ 4线路æ£ä¿® |
| | | station?: string; // ç«ç¹ |
| | | line?: string; // 线路 |
| | | teamId?: number; // çç»ç¼å· |
| | | teamName?: string; // çç»åç§° |
| | | planDate?: string; // è®¡åæ¥æ |
| | | status?: number; // ç¶æï¼0è稿 10å·²ä¸å 20已宿 30已忶 |
| | | workOrderId?: number; // çæçå·¥åç¼å· |
| | | workOrderCode?: string; // çæçå·¥åç¼ç |
| | | remark?: string; // 夿³¨ |
| | | items?: PlanItem[]; // å¤ä»¶éæ±æç» |
| | | createTime?: string; // å建æ¶é´ |
| | | } |
| | | |
| | | /** å¤ä»¶éæ±æç» */ |
| | | export interface PlanItem { |
| | | id?: number; // ç¼å· |
| | | itemId: number; // ç©æ/å¤ä»¶ç¼å· |
| | | itemName?: string; // ç©æåç§° |
| | | itemCode?: string; // ç©æç¼ç |
| | | unitMeasureName?: string; // 计éåä½åç§° |
| | | quantity: number; // éæ±æ°é |
| | | remark?: string; // 夿³¨ |
| | | } |
| | | } |
| | | |
| | | /** å建çµåä½ä¸è®¡å */ |
| | | export function createPlan(data: MesProMaintenancePlanApi.MaintenancePlan) { |
| | | return requestClient.post('/mes/pro/maintenance-plan/create', data); |
| | | } |
| | | |
| | | /** æ´æ°çµåä½ä¸è®¡å */ |
| | | export function updatePlan(data: MesProMaintenancePlanApi.MaintenancePlan) { |
| | | return requestClient.put('/mes/pro/maintenance-plan/update', data); |
| | | } |
| | | |
| | | /** å é¤çµåä½ä¸è®¡å */ |
| | | export function deletePlan(ids: number[]) { |
| | | return requestClient.delete('/mes/pro/maintenance-plan/delete', { params: { ids: ids.join(',') } }); |
| | | } |
| | | |
| | | /** è·åçµåä½ä¸è®¡å详æ
*/ |
| | | export function getPlan(id: number) { |
| | | return requestClient.get<MesProMaintenancePlanApi.MaintenancePlan>( |
| | | `/mes/pro/maintenance-plan/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åçµåä½ä¸è®¡åå页 */ |
| | | export function getPlanPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesProMaintenancePlanApi.MaintenancePlan>>( |
| | | '/mes/pro/maintenance-plan/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** 导åºçµåä½ä¸è®¡å */ |
| | | export function exportPlan(params: any) { |
| | | return requestClient.download('/mes/pro/maintenance-plan/export-excel', { params }); |
| | | } |
| | | |
| | | /** ä¸åçµåä½ä¸è®¡å */ |
| | | export function issuePlan(id: number) { |
| | | return requestClient.put(`/mes/pro/maintenance-plan/issue?id=${id}`); |
| | | } |
| | | |
| | | /** 宿çµåä½ä¸è®¡å */ |
| | | export function finishPlan(id: number) { |
| | | return requestClient.put(`/mes/pro/maintenance-plan/finish?id=${id}`); |
| | | } |
| | | |
| | | /** æ ¹æ®å¤ä»¶éæ±çæéè´è®¡å */ |
| | | export function generatePurchasePlan(id: number) { |
| | | return requestClient.post<number>(`/mes/pro/maintenance-plan/generate-purchase-plan?id=${id}`); |
| | | } |
| | | |
| | | /** çæè¿ç»´/åçµ/æ£ä¿®å·¥å */ |
| | | export function generateWorkOrder(id: number) { |
| | | return requestClient.post<number>(`/mes/pro/maintenance-plan/generate-work-order?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}`); |
| | | } |
| | |
| | | id?: number; |
| | | code?: string; |
| | | name?: string; |
| | | type?: number; // çµåå·¥èºåç±» |
| | | attention?: string; |
| | | status?: number; |
| | | remark?: string; |
| | |
| | | workstationId?: number; // å·¥ä½ç«ç¼å· |
| | | workstationCode?: string; // å·¥ä½ç«ç¼ç |
| | | workstationName?: string; // å·¥ä½ç«åç§° |
| | | teamId?: number; // çç»ç¼å· |
| | | teamName?: string; // çç»åç§° |
| | | routeId?: number; // å·¥èºè·¯çº¿ç¼å· |
| | | processId?: number; // å·¥åºç¼å· |
| | | processName?: string; // å·¥åºåç§° |
| | |
| | | clientId?: number; // 客æ·ç¼å· |
| | | clientCode?: string; // 客æ·ç¼ç |
| | | clientName?: string; // 客æ·åç§° |
| | | saleOrderId?: number; // å
³èè¥é订åï¼ERPéå®è®¢åï¼ç¼å· |
| | | saleOrderNo?: string; // å
³èè¥é订åï¼ERPéå®è®¢åï¼åå· |
| | | vendorId?: number; // ä¾åºåç¼å· |
| | | vendorName?: string; // ä¾åºååç§° |
| | | vendorCode?: string; // ä¾åºåç¼ç |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '@vben/request'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesQcOutsourceTestApi { |
| | | /** å§å¤æ£æµ/è¯éªå */ |
| | | export interface OutsourceTest { |
| | | id?: number; |
| | | code?: string; // åæ®ç¼ç |
| | | name?: string; // åæ®åç§° |
| | | testType?: number; // æ£æµç±»åï¼1ç¬¬ä¸æ¹æ£æµ 2å§å¤è¯éª |
| | | itemId?: number; // æ£æµå¯¹è±¡ç¼å· |
| | | itemName?: string; // æ£æµå¯¹è±¡åç§° |
| | | testOrg?: string; // æ£æµæºæ |
| | | testDate?: string; // æ£æµæ¥æ |
| | | result?: number; // æ£æµç»æï¼1åæ ¼ 2ä¸åæ ¼ |
| | | reportNo?: string; // æ£æµæ¥åç¼å· |
| | | status?: number; // ç¶æï¼0è稿 10å·²æäº¤ 20已宿 |
| | | remark?: string; // 夿³¨ |
| | | blobIds?: number[]; // éä»¶ |
| | | attachmentList?: { id: number; name?: string; url?: string }[]; |
| | | createTime?: string; |
| | | } |
| | | } |
| | | |
| | | /** å建å§å¤æ£æµ/è¯éªå */ |
| | | export function createTest(data: MesQcOutsourceTestApi.OutsourceTest) { |
| | | return requestClient.post('/mes/qc/outsource-test/create', data); |
| | | } |
| | | |
| | | /** æ´æ°å§å¤æ£æµ/è¯éªå */ |
| | | export function updateTest(data: MesQcOutsourceTestApi.OutsourceTest) { |
| | | return requestClient.put('/mes/qc/outsource-test/update', data); |
| | | } |
| | | |
| | | /** å é¤å§å¤æ£æµ/è¯éªå */ |
| | | export function deleteTest(ids: number[]) { |
| | | return requestClient.delete('/mes/qc/outsource-test/delete', { params: { ids: ids.join(',') } }); |
| | | } |
| | | |
| | | /** è·åå§å¤æ£æµ/è¯éªå详æ
*/ |
| | | export function getTest(id: number) { |
| | | return requestClient.get<MesQcOutsourceTestApi.OutsourceTest>( |
| | | `/mes/qc/outsource-test/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** è·åå§å¤æ£æµ/è¯éªåå页 */ |
| | | export function getTestPage(params: PageParam) { |
| | | return requestClient.get<PageResult<MesQcOutsourceTestApi.OutsourceTest>>( |
| | | '/mes/qc/outsource-test/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æäº¤å§å¤æ£æµ/è¯éªå */ |
| | | export function submitTest(id: number) { |
| | | return requestClient.put(`/mes/qc/outsource-test/submit?id=${id}`); |
| | | } |
| | | |
| | | /** 宿å§å¤æ£æµ/è¯éªå */ |
| | | export function finishTest(id: number) { |
| | | return requestClient.put(`/mes/qc/outsource-test/finish?id=${id}`); |
| | | } |
| | |
| | | clientCode?: string; // 客æ·ç¼ç |
| | | clientName?: string; // 客æ·åç§° |
| | | requiredTime?: number; // éæ±æ¶é´ |
| | | issueType?: number; // åæ®ç±»åï¼1=æ£å¸¸é¢æï¼2=ç´§æ¥è¡¥æï¼ |
| | | status?: number; // åæ®ç¶æ |
| | | remark?: string; // 夿³¨ |
| | | createTime?: Date; // å建æ¶é´ |
| | |
| | | >('/mes/wm/stocktaking-task-line/page', { params }); |
| | | } |
| | | |
| | | /** ä¸è½½çç¹å®çæ°å¯¼å
¥æ¨¡æ¿ */ |
| | | export function importStockTakingQuantityTemplate() { |
| | | return requestClient.download('/mes/wm/stocktaking-task-line/get-import-template'); |
| | | } |
| | | |
| | | /** 导å
¥çç¹å®çæ°é */ |
| | | export function importStockTakingQuantity(taskId: number, file: File) { |
| | | return requestClient.upload<{ |
| | | successCount?: number; |
| | | failureCount?: number; |
| | | failureMessages?: string[]; |
| | | }>(`/mes/wm/stocktaking-task-line/import-excel?taskId=${taskId}`, { file }); |
| | | } |
| | | |
| | | /** æ¥è¯¢çç¹ä»»å¡è¡ç²¾ç®å表 */ |
| | | export function getStockTakingTaskLineSimpleList(taskId: number) { |
| | | return requestClient.get<WlsStockTakingTaskLineApi.StockTakingTaskLine[]>( |
| | |
| | | id?: number; // åºä½ç¼å· |
| | | code?: string; // åºä½ç¼ç |
| | | name?: string; // åºä½åç§° |
| | | type?: number; // åºåºç±»å |
| | | warehouseId?: number; // ä»åºç¼å· |
| | | warehouseName?: string; // ä»åºåç§° |
| | | locationId?: number; // åºåºç¼å· |
| | |
| | | id?: number; // ä»åºç¼å· |
| | | code?: string; // ä»åºç¼ç |
| | | name?: string; // ä»åºåç§° |
| | | type?: number; // ä»åºç±»å |
| | | address?: string; // å°å |
| | | area?: number; // é¢ç§¯ |
| | | chargeUserId?: number; // è´è´£äºº |
| | |
| | | CLOSE: 'lucide:x', |
| | | BOOK: 'lucide:book', |
| | | AUDIT: 'lucide:file-check', |
| | | CALC: 'lucide:calculator', |
| | | }; |
| | |
| | | SELF: 1, // èªè¡ç产 |
| | | OUTSOURCE: 2, // 代工 |
| | | PURCHASE: 3, // éè´ |
| | | POWER_OPERATION: 4, // çµåè¿ç»´ |
| | | POWER_GENERATION: 5, // åçµ |
| | | POWER_MAINTENANCE: 6, // æ£ä¿® |
| | | } as const; |
| | | |
| | | /** MES 工忥æºç±»åæä¸¾ */ |
| | | export const MesProWorkOrderSourceTypeEnum = { |
| | | ORDER: 1, // 客æ·è®¢å |
| | | STORE: 2, // åºåå¤è´§ |
| | | MAINTENANCE_PLAN: 3, // è¿ç»´è®¡å |
| | | } as const; |
| | | |
| | | /** MES ç产任å¡ç¶ææä¸¾ */ |
| | |
| | | CRM_SALES_TARGET_PERIOD_TYPE: 'crm_sales_target_period_type', // CRM éå®ç®æ å¨æç±»å |
| | | CRM_SALES_TARGET_DIMENSION: 'crm_sales_target_dimension', // CRM éå®ç®æ 维度 |
| | | CRM_SALES_TARGET_STATUS: 'crm_sales_target_status', // CRM éå®ç®æ ç¶æ |
| | | CRM_CUSTOMER_TYPE: 'crm_customer_type', // CRM 客æ·åç±»ï¼çµåï¼ |
| | | CRM_CREDIT_LEVEL: 'crm_credit_level', // CRM 客æ·ä¿¡ç¨ç级 |
| | | CRM_QUOTATION_TYPE: 'crm_quotation_type', // CRM æ¥ä»·åç±»å |
| | | CRM_TARIFF_MODE: 'crm_tariff_mode', // CRM çµä»·æ¨¡å¼ |
| | | CRM_CONTRACT_TYPE: 'crm_contract_type', // CRM ååç±»å |
| | | CRM_VOLTAGE_LEVEL: 'crm_voltage_level', // CRM çµåç级 |
| | | CRM_ELECTRICITY_TYPE: 'crm_electricity_type', // CRM ç¨çµç±»å |
| | | CRM_METER_MODE: 'crm_meter_mode', // CRM è®¡éæ¹å¼ |
| | | CRM_COMMAND_TYPE: 'crm_command_type', // CRM ååæä»¤ç±»å |
| | | CRM_COMMAND_STATUS: 'crm_command_status', // CRM ååæä»¤ç¶æ |
| | | CRM_REFUND_TYPE: 'crm_refund_type', // CRM é费类å |
| | | CRM_CANCELLATION_TYPE: 'crm_cancellation_type', // CRM éæ·ç±»å |
| | | } as const; |
| | | |
| | | /** ========== AFTERSALES - å®åæå¡æ¨¡å ========== */ |
| | |
| | | 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 - å¶é æ§è¡æ¨¡å ========== */ |
| | | const MES_DICT = { |
| | | MES_MD_ITEM_OR_PRODUCT: 'mes_md_item_or_product', // MES ç©æ/äº§åæ è¯ |
| | | MES_MATERIAL_CATEGORY: 'mes_material_category', // MES çµåç©èµåç±» |
| | | MES_PRO_MAINTENANCE_PLAN_TYPE: 'mes_pro_maintenance_plan_type', // MES çµåä½ä¸è®¡åç±»å |
| | | MES_PRO_MAINTENANCE_PLAN_STATUS: 'mes_pro_maintenance_plan_status', // MES çµåä½ä¸è®¡åç¶æ |
| | | MES_QC_OUTSOURCE_TEST_TYPE: 'mes_qc_outsource_test_type', // MES å§å¤æ£æµç±»å |
| | | 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 ç¼ç è§ååæ®µç±»å |
| | |
| | | MES_PD_VERSION_CHANGE_TYPE: 'mes_pd_version_change_type', // MES çæ¬åæ´ç±»å |
| | | MES_PD_DOCUMENT_TYPE: 'mes_pd_document_type', // MES è®¾è®¡èµæç±»å |
| | | MES_PD_ARCHIVE_STATUS: 'mes_pd_archive_status', // MES è®¾è®¡å½æ¡£ç¶æ |
| | | PRODUCT_CONDUCTOR_TYPE: 'product_conductor_type', // æ¢ä½å¯¼çº¿å¯¼ä½ç±»å |
| | | PRODUCT_REEL_TYPE: 'product_reel_type', // æ¢ä½å¯¼çº¿çå
፱Ȍ |
| | | PRODUCT_TECH_CONFIRM_TEMPLATE: 'product_tech_confirm_template', // ææ¯ç¡®è®¤å½æ¨¡æ¿ç±»å |
| | | PRODUCT_TECH_CONFIRM_STATUS: 'product_tech_confirm_status', // ææ¯ç¡®è®¤å½ç¶æ |
| | | MES_PRO_FEEDBACK_CHANNEL: 'mes_pro_feedback_channel', // MES ç产æ¥å·¥éå¾ |
| | | MES_PRO_ANDON_STATUS: 'mes_pro_andon_status', // MES å®ç¯å¤ç½®ç¶æ |
| | | MES_PRO_ANDON_LEVEL: 'mes_pro_andon_level', // MES å®ç¯çº§å« |
| | |
| | | MES_WM_BARCODE_FORMAT: 'mes_wm_barcode_format', // MES æ¡ç æ ¼å¼ |
| | | MES_WM_PACKAGE_STATUS: 'mes_wm_package_status', // MES è£
ç®±åç¶æ |
| | | MES_WM_PRODUCT_SALES_STATUS: 'mes_wm_product_sales_status', // MES éå®åºåºåç¶æ |
| | | MES_WM_WAREHOUSE_TYPE: 'mes_wm_warehouse_type', // MES ä»åº/åºåºç±»å |
| | | MES_WM_QUALITY_STATUS: 'mes_wm_quality_status', // MES è´¨éç¶æ |
| | | MES_WM_MISC_ISSUE_TYPE: 'mes_wm_misc_issue_type', // MES æé¡¹åºåºç±»å |
| | | MES_WM_MISC_ISSUE_STATUS: 'mes_wm_misc_issue_status', // MES æé¡¹åºåºåç¶æ |
| | |
| | | MES_WM_RETURN_ISSUE_STATUS: 'mes_wm_return_issue_status', // MES ç产éæåç¶æ |
| | | MES_WM_RETURN_ISSUE_TYPE: 'mes_wm_return_issue_type', // MES éæç±»å |
| | | MES_WM_PRODUCT_ISSUE_STATUS: 'mes_wm_product_issue_status', // MES 颿åºåºåç¶æ |
| | | MES_WM_PRODUCT_ISSUE_TYPE: 'mes_wm_product_issue_type', // MES çäº§é¢æåæ®ç±»å |
| | | MES_WM_PRODUCT_RECEIPT_STATUS: 'mes_wm_product_receipt_status', // MES 产åå
¥åºåç¶æ |
| | | MES_WM_STOCK_TAKING_TYPE: 'mes_wm_stock_taking_type', // MES çç¹ç±»å |
| | | MES_WM_STOCK_TAKING_TASK_STATUS: 'mes_wm_stock_taking_task_status', // MES çç¹ä»»å¡ç¶æ |
| | |
| | | MES_PD_TEMPLATE_TYPE: 'mes_pd_template_type', // MES å·¥èºæ¨¡æ¿ç±»å |
| | | MES_NCR_STATUS: 'mes_ncr_status', // MES ä¸åæ ¼åå¤çåç¶æ |
| | | MES_NCR_DISPOSITION: 'mes_ncr_disposition', // MES ä¸åæ ¼åå¤ç½®æ¹å¼ |
| | | MES_PD_PRODUCT_ARCHIVE_TYPE: 'mes_pd_product_archive_type', // MES äº§åæ¡£æ¡ç±»å |
| | | MES_PD_AUDIT_STATUS: 'mes_pd_audit_status', // MES 产å设计审æ¹ç¶æ |
| | | MES_PD_PRODUCT_STATUS: 'mes_pd_product_status', // MES äº§åæ¡£æ¡å¯ç¨ç¶æ |
| | | MES_PD_PRICE_OBJECT_TYPE: 'mes_pd_price_object_type', // MES å®ä»·å¯¹è±¡ç±»å |
| | | MES_PD_DISCOUNT_POLICY_TYPE: 'mes_pd_discount_policy_type', // MES 伿 çç¥ç±»å |
| | | MES_PD_TIERED_PRICE_STATUS: 'mes_pd_tiered_price_status', // MES é¶æ¢¯çµä»·å¯ç¨ç¶æ |
| | | } as const; |
| | | |
| | | /** ========== WMS - ä»å¨ç®¡ç模å ========== */ |
| | |
| | | const HRM_DICT = { |
| | | HRM_DEGREE: 'hrm_degree', // HRM å¦å |
| | | HRM_EMPLOYEE_STATUS: 'hrm_employee_status', // HRM åå·¥ç¶æ |
| | | HRM_CERTIFICATE_TYPE: 'hrm_certificate_type', // HRM è¯ä¹¦èµè´¨ç±»å |
| | | HRM_GENDER: 'hrm_gender', // HRM æ§å« |
| | | HRM_AUDIT_STATUS: 'hrm_audit_status', // HRM 审æ¹ç¶æ |
| | | HRM_LEAVE_TYPE: 'hrm_leave_type', // HRM 请åç±»å |
| | |
| | | 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> |
| | |
| | | { label: 'ä¸è¬é®é¢', value: 1 }, |
| | | { label: 'ç»´ä¿®é®é¢', value: 2 }, |
| | | { label: 'éè´§é®é¢', value: 3 }, |
| | | { label: 'åçµæ¥ä¿®', value: 4 }, |
| | | { label: 'ç¨çµæ
é', value: 5 }, |
| | | { label: 'æå¡æè¯', value: 6 }, |
| | | { label: 'ä¾çµè´¨éå¼è®®', value: 7 }, |
| | | ]; |
| | | |
| | | /** 严éç¨åº¦é项 */ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { DecisionAlertApi } from '#/api/bi/decision/alert'; |
| | | |
| | | import { onMounted, reactive, ref } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { confirmAlert, getAlertPage, getUnprocessedCount, handleAlert } from '#/api/bi/decision/alert'; |
| | | |
| | | defineOptions({ name: 'DecisionAlertCenter' }); |
| | | |
| | | const unprocessedCount = ref(0); |
| | | const queryParams = reactive<Record<string, unknown>>({}); |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: [ |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å
¨é¨', value: '' }, |
| | | { label: 'æªå¤ç', value: 0 }, |
| | | { label: '已确认', value: 1 }, |
| | | { label: 'å·²å¤ç', value: 2 }, |
| | | ], |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'severity', |
| | | label: '严é级å«', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å
¨é¨', value: '' }, |
| | | { label: 'è¦å', value: 1 }, |
| | | { label: '严é', value: 2 }, |
| | | { label: 'ç´§æ¥', value: 3 }, |
| | | ], |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | ], |
| | | }, |
| | | gridOptions: { |
| | | columns: [ |
| | | { field: 'id', title: 'ID', width: 60 }, |
| | | { field: 'kpiName', title: 'KPIåç§°', width: 140 }, |
| | | { |
| | | field: 'severity', title: '严é级å«', width: 90, |
| | | slots: { default: 'severitySlot' }, |
| | | }, |
| | | { |
| | | field: 'actualValue', title: 'å®é
å¼', width: 100, |
| | | formatter: ({ cellValue }: any) => cellValue?.toLocaleString(), |
| | | }, |
| | | { |
| | | field: 'thresholdValue', title: 'éå¼', width: 100, |
| | | formatter: ({ cellValue }: any) => cellValue?.toLocaleString(), |
| | | }, |
| | | { field: 'alertMessage', title: 'é¢è¦æ¶æ¯', minWidth: 200 }, |
| | | { |
| | | field: 'status', title: 'ç¶æ', width: 80, |
| | | slots: { default: 'statusSlot' }, |
| | | }, |
| | | { field: 'triggeredTime', title: 'è§¦åæ¶é´', width: 170 }, |
| | | { field: 'handleRemark', title: 'å¤ç夿³¨', width: 140 }, |
| | | { |
| | | title: 'æä½', width: 160, |
| | | slots: { default: 'actionSlot' }, |
| | | fixed: 'right', |
| | | }, |
| | | ], |
| | | height: 'auto', |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page, form }: any) => { |
| | | const res = await getAlertPage({ ...page, ...form, ...queryParams }); |
| | | return res; |
| | | }, |
| | | }, |
| | | }, |
| | | } as VxeTableGridOptions<DecisionAlertApi.AlertRecord>, |
| | | }); |
| | | |
| | | async function loadUnprocessed() { |
| | | try { |
| | | unprocessedCount.value = await getUnprocessedCount(); |
| | | } catch { |
| | | // ignore |
| | | } |
| | | } |
| | | |
| | | async function handleConfirm(row: DecisionAlertApi.AlertRecord) { |
| | | const hide = message.loading('确认ä¸...', 0); |
| | | try { |
| | | await confirmAlert(row.id); |
| | | message.success('已确认'); |
| | | gridApi.query(); |
| | | loadUnprocessed(); |
| | | } finally { |
| | | hide(); |
| | | } |
| | | } |
| | | |
| | | async function handleProcess(row: DecisionAlertApi.AlertRecord) { |
| | | const hide = message.loading('å¤çä¸...', 0); |
| | | try { |
| | | await handleAlert(row.id, 'å·²å¤ç'); |
| | | message.success('å·²å¤ç'); |
| | | gridApi.query(); |
| | | loadUnprocessed(); |
| | | } finally { |
| | | hide(); |
| | | } |
| | | } |
| | | |
| | | function severityColor(severity: number): string { |
| | | if (severity === 3) return 'red'; |
| | | if (severity === 2) return 'orange'; |
| | | return 'blue'; |
| | | } |
| | | |
| | | function severityText(severity: number): string { |
| | | if (severity === 3) return 'ç´§æ¥'; |
| | | if (severity === 2) return '严é'; |
| | | return 'è¦å'; |
| | | } |
| | | |
| | | function statusColor(status: number): string { |
| | | if (status === 0) return 'red'; |
| | | if (status === 1) return 'blue'; |
| | | return 'green'; |
| | | } |
| | | |
| | | function statusText(status: number): string { |
| | | if (status === 0) return 'æªå¤ç'; |
| | | if (status === 1) return '已确认'; |
| | | return 'å·²å¤ç'; |
| | | } |
| | | |
| | | onMounted(() => { |
| | | loadUnprocessed(); |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page :auto-content-height="true"> |
| | | <div class="mb-3"> |
| | | <span class="text-lg font-bold">é¢è¦ä¸å¿</span> |
| | | <Tag v-if="unprocessedCount > 0" color="red" class="ml-2"> |
| | | {{ unprocessedCount }} æ¡æªå¤ç |
| | | </Tag> |
| | | </div> |
| | | <Grid> |
| | | <template #severitySlot="{ row }"> |
| | | <Tag :color="severityColor(row.severity)">{{ severityText(row.severity) }}</Tag> |
| | | </template> |
| | | <template #statusSlot="{ row }"> |
| | | <Tag :color="statusColor(row.status)">{{ statusText(row.status) }}</Tag> |
| | | </template> |
| | | <template #actionSlot="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { label: '确认', type: 'link', icon: ACTION_ICON.AUDIT, ifShow: row.status === 0, onClick: handleConfirm.bind(null, row) }, |
| | | { label: 'å¤ç', type: 'link', icon: ACTION_ICON.EDIT, ifShow: row.status < 2, onClick: handleProcess.bind(null, row) }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { field: 'id', title: 'ID', width: 60 }, |
| | | { field: 'name', title: 'è§ååç§°', width: 150 }, |
| | | { |
| | | field: 'severity', title: '严é级å«', width: 90, |
| | | slots: { default: 'severitySlot' }, |
| | | }, |
| | | { field: 'comparisonOperator', title: 'è¿ç®ç¬¦', width: 70 }, |
| | | { field: 'thresholdValue', title: 'éå¼', width: 100 }, |
| | | { |
| | | field: 'enabled', title: 'å¯ç¨', width: 70, |
| | | slots: { default: 'enabledSlot' }, |
| | | }, |
| | | { |
| | | title: 'æä½', width: 160, |
| | | slots: { default: 'actionSlot' }, |
| | | fixed: 'right', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'enabled', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å
¨é¨', value: '' }, |
| | | { label: 'å¯ç¨', value: 1 }, |
| | | { label: 'ç¦ç¨', value: 0 }, |
| | | ], |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function useFormSchema(kpiOptions: { label: string; value: number }[]): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'kpiId', |
| | | label: 'å
³èKPI', |
| | | component: 'Select', |
| | | componentProps: { options: kpiOptions }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'è§ååç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥è§ååç§°' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'severity', |
| | | label: '严é级å«', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'è¦å', value: 1 }, |
| | | { label: '严é', value: 2 }, |
| | | { label: 'ç´§æ¥', value: 3 }, |
| | | ], |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'comparisonOperator', |
| | | label: 'æ¯è¾è¿ç®ç¬¦', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å¤§äº >', value: '>' }, |
| | | { label: 'å°äº <', value: '<' }, |
| | | { label: '大äºçäº >=', value: '>=' }, |
| | | { label: 'å°äºçäº <=', value: '<=' }, |
| | | ], |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'thresholdValue', label: 'éå¼', component: 'InputNumber', |
| | | componentProps: { placeholder: 'éå¼', style: 'width:100%' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'enabled', |
| | | label: 'å¯ç¨', |
| | | component: 'Switch', |
| | | componentProps: { checkedValue: 1, unCheckedValue: 0 }, |
| | | }, |
| | | { |
| | | fieldName: 'notificationType', label: 'éç¥æ¹å¼', component: 'Input', |
| | | componentProps: { placeholder: 'system/message/email' }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', label: '夿³¨', component: 'InputTextArea', |
| | | componentProps: { placeholder: '夿³¨', rows: 2 }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function severityColor(severity: number): string { |
| | | if (severity === 3) return 'red'; |
| | | if (severity === 2) return 'orange'; |
| | | return 'blue'; |
| | | } |
| | | |
| | | export function severityText(severity: number): string { |
| | | if (severity === 3) return 'ç´§æ¥'; |
| | | if (severity === 2) return '严é'; |
| | | return 'è¦å'; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { DecisionAlertApi } from '#/api/bi/decision/alert'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteAlertRule, getAlertRulePage } from '#/api/bi/decision/alert'; |
| | | |
| | | import { severityColor, severityText, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | defineOptions({ name: 'DecisionAlertRule' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page, form }: any) => { |
| | | const res = await getAlertRulePage({ ...page, ...form }); |
| | | return res; |
| | | }, |
| | | }, |
| | | }, |
| | | } as VxeTableGridOptions<DecisionAlertApi.AlertRule>, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData(null).open(); |
| | | } |
| | | |
| | | function handleEdit(row: DecisionAlertApi.AlertRule) { |
| | | formModalApi.setData(row).open(); |
| | | } |
| | | |
| | | async function handleDelete(row: DecisionAlertApi.AlertRule) { |
| | | const hide = message.loading(`æ£å¨å é¤ã${row.name}ã...`, 0); |
| | | try { |
| | | await deleteAlertRule(row.id!); |
| | | message.success('å 餿å'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hide(); |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Page :auto-content-height="true"> |
| | | <div class="mb-3 flex items-center justify-between"> |
| | | <span class="text-lg font-bold">é¢è¦è§åé
ç½®</span> |
| | | <a-button type="primary" @click="handleCreate">æ°å¢</a-button> |
| | | </div> |
| | | <Grid> |
| | | <template #severitySlot="{ row }"> |
| | | <Tag :color="severityColor(row.severity)">{{ severityText(row.severity) }}</Tag> |
| | | </template> |
| | | <template #enabledSlot="{ row }"> |
| | | <Tag :color="row.enabled === 1 ? 'green' : 'default'">{{ row.enabled === 1 ? 'å¯ç¨' : 'ç¦ç¨' }}</Tag> |
| | | </template> |
| | | <template #actionSlot="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { label: 'ç¼è¾', type: 'link', icon: ACTION_ICON.EDIT, onClick: handleEdit.bind(null, row) }, |
| | | { label: 'å é¤', type: 'link', danger: true, icon: ACTION_ICON.DELETE, onClick: handleDelete.bind(null, row) }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | <FormModal /> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { DecisionAlertApi } from '#/api/bi/decision/alert'; |
| | | |
| | | import { computed, onMounted, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createAlertRule, getAlertRule, updateAlertRule } from '#/api/bi/decision/alert'; |
| | | import { getKpiDefinitionList } from '#/api/bi/decision/kpi'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<DecisionAlertApi.AlertRule>(); |
| | | const kpiOptions = ref<{ label: string; value: number }[]>([]); |
| | | |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id ? 'ç¼è¾é¢è¦è§å' : 'æ°å¢é¢è¦è§å'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { class: 'w-full' }, |
| | | formItemClass: 'col-span-2', |
| | | labelWidth: 120, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useFormSchema([]), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | async function refreshKpiOptions() { |
| | | try { |
| | | const res: any = await getKpiDefinitionList(); |
| | | const list = res?.list || []; |
| | | kpiOptions.value = list.map((k: any) => ({ label: `${k.name}(${k.code})`, value: k.id })); |
| | | formApi.setState((prev) => ({ |
| | | ...prev, |
| | | schema: useFormSchema(kpiOptions.value), |
| | | })); |
| | | } catch { |
| | | // ignore |
| | | } |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as DecisionAlertApi.AlertRule; |
| | | try { |
| | | await (formData.value?.id ? updateAlertRule(data) : createAlertRule(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æä½æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | await refreshKpiOptions(); |
| | | const data = modalApi.getData<DecisionAlertApi.AlertRule>(); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getAlertRule(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | |
| | | onMounted(() => { |
| | | refreshKpiOptions(); |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { onMounted, ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { DatePicker, message, Select, Spin } from 'ant-design-vue'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | generateForecastWorkOrder, |
| | | getForecastList, |
| | | } from '#/api/bi/decision/forecast'; |
| | | |
| | | defineOptions({ name: 'DecisionForecastAnalysis' }); |
| | | |
| | | const loading = ref(false); |
| | | const forecastData = ref<any[]>([]); |
| | | const forecastCode = ref('load_forecast_daily'); |
| | | |
| | | const FORECAST_OPTIONS = [ |
| | | { 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() { |
| | | loading.value = true; |
| | | try { |
| | | forecastData.value = await getForecastList(forecastCode.value); |
| | | } catch { |
| | | forecastData.value = []; |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | 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> |
| | | <Select |
| | | v-model:value="forecastCode" |
| | | :options="FORECAST_OPTIONS" |
| | | style="width: 180px" |
| | | @change="loadData" |
| | | /> |
| | | </div> |
| | | |
| | | <Spin :spinning="loading"> |
| | | <div v-if="forecastData.length === 0" class="py-12 text-center text-gray-400"> |
| | | ææ é¢æµæ°æ®ï¼è¯·å
é
ç½® 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 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> |
| | | <div class="mb-1 text-2xl font-bold"> |
| | | {{ item.forecastValue ?? '-' }} |
| | | <span class="ml-1 text-sm font-normal text-gray-400">(颿µ)</span> |
| | | </div> |
| | | <div v-if="item.actualValue != null" class="text-sm text-gray-500"> |
| | | å®é
å¼: {{ item.actualValue }} |
| | | <span v-if="item.lowerBound != null" class="ml-2"> |
| | | 置信åºé´: [{{ item.lowerBound }} ~ {{ item.upperBound }}] |
| | | </span> |
| | | </div> |
| | | <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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { field: 'code', title: 'KPIç¼ç ', width: 140 }, |
| | | { field: 'name', title: 'KPIåç§°', width: 150 }, |
| | | { |
| | | field: 'category', title: 'åç±»', width: 100, |
| | | slots: { default: 'categorySlot' }, |
| | | }, |
| | | { field: 'unit', title: 'åä½', width: 70 }, |
| | | { field: 'chartType', title: 'å¾è¡¨ç±»å', width: 90 }, |
| | | { |
| | | field: 'status', title: 'ç¶æ', width: 70, |
| | | slots: { default: 'statusSlot' }, |
| | | }, |
| | | { field: 'sort', title: 'æåº', width: 60 }, |
| | | { |
| | | title: 'æä½', width: 160, |
| | | slots: { default: 'actionSlot' }, |
| | | fixed: 'right', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'name', |
| | | label: 'KPIåç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥KPIåç§°' }, |
| | | }, |
| | | { |
| | | fieldName: 'category', |
| | | label: 'åç±»', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å
¨é¨', value: '' }, |
| | | { label: 'ä¾çµé', value: 'power_supply' }, |
| | | { label: '设å¤è¿è¡', value: 'device_operation' }, |
| | | { label: 'ç产', value: 'production' }, |
| | | { label: 'è´¨é', value: 'quality' }, |
| | | { label: 'éè´', value: 'procurement' }, |
| | | { label: 'å®å
¨', value: 'safety' }, |
| | | ], |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å
¨é¨', value: '' }, |
| | | { label: 'å¯ç¨', value: 1 }, |
| | | { label: 'ç¦ç¨', value: 0 }, |
| | | ], |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: 'KPIç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥KPIç¼ç ï¼å¯ä¸ï¼' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'KPIåç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥KPIåç§°' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'category', |
| | | label: 'åç±»', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'ä¾çµé', value: 'power_supply' }, |
| | | { label: '设å¤è¿è¡', value: 'device_operation' }, |
| | | { label: 'ç产', value: 'production' }, |
| | | { label: 'è´¨é', value: 'quality' }, |
| | | { label: 'éè´', value: 'procurement' }, |
| | | { label: 'å®å
¨', value: 'safety' }, |
| | | ], |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { fieldName: 'unit', label: 'åä½', component: 'Input', componentProps: { placeholder: 'åä½' } }, |
| | | { |
| | | fieldName: 'chartType', |
| | | label: 'å¾è¡¨ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'æ°åå¡ç', value: 'number' }, |
| | | { label: 'æ±ç¶å¾', value: 'bar' }, |
| | | { label: 'æçº¿å¾', value: 'line' }, |
| | | { label: '饼å¾', value: 'pie' }, |
| | | ], |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'comparisonOperator', |
| | | label: 'æ¯è¾è¿ç®ç¬¦', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å¤§äº >', value: '>' }, |
| | | { label: 'å°äº <', value: '<' }, |
| | | { label: '大äºçäº >=', value: '>=' }, |
| | | { label: 'å°äºçäº <=', value: '<=' }, |
| | | ], |
| | | }, |
| | | }, |
| | | { fieldName: 'thresholdWarn', label: 'è¦åéå¼', component: 'InputNumber', componentProps: { placeholder: 'è¦åéå¼', style: 'width:100%' } }, |
| | | { fieldName: 'thresholdCritical', label: '严ééå¼', component: 'InputNumber', componentProps: { placeholder: '严ééå¼', style: 'width:100%' } }, |
| | | { fieldName: 'refreshInterval', label: 'å·æ°é´é(ç§)', component: 'InputNumber', componentProps: { placeholder: 'é»è®¤300', style: 'width:100%' } }, |
| | | { fieldName: 'sort', label: 'æåº', component: 'InputNumber', componentProps: { placeholder: 'æåºå·', style: 'width:100%' } }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'å¯ç¨', value: 1 }, |
| | | { label: 'ç¦ç¨', value: 0 }, |
| | | ], |
| | | }, |
| | | }, |
| | | { fieldName: 'querySql', label: 'æ¥è¯¢SQL', component: 'InputTextArea', componentProps: { placeholder: 'SELECT SUM(...) FROM ...', rows: 3 } }, |
| | | { fieldName: 'remark', label: '夿³¨', component: 'InputTextArea', componentProps: { placeholder: '夿³¨', rows: 2 } }, |
| | | ]; |
| | | } |
| | | |
| | | export const CATEGORY_MAP: Record<string, string> = { |
| | | power_supply: 'ä¾çµé', |
| | | device_operation: '设å¤è¿è¡', |
| | | production: 'ç产', |
| | | quality: 'è´¨é', |
| | | procurement: 'éè´', |
| | | safety: 'å®å
¨', |
| | | }; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { DecisionKpiApi } from '#/api/bi/decision/kpi'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteKpiDefinition, getKpiDefinitionPage } from '#/api/bi/decision/kpi'; |
| | | |
| | | import { CATEGORY_MAP, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | defineOptions({ name: 'DecisionKpiConfig' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page, form }: any) => { |
| | | const res = await getKpiDefinitionPage({ ...page, ...form }); |
| | | return res; |
| | | }, |
| | | }, |
| | | }, |
| | | } as VxeTableGridOptions<DecisionKpiApi.KpiDefinition>, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData(null).open(); |
| | | } |
| | | |
| | | function handleEdit(row: DecisionKpiApi.KpiDefinition) { |
| | | formModalApi.setData(row).open(); |
| | | } |
| | | |
| | | async function handleDelete(row: DecisionKpiApi.KpiDefinition) { |
| | | const hide = message.loading(`æ£å¨å é¤ã${row.name}ã...`, 0); |
| | | try { |
| | | await deleteKpiDefinition(row.id!); |
| | | message.success(`å é¤ã${row.name}ãæå`); |
| | | handleRefresh(); |
| | | } finally { |
| | | hide(); |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Page :auto-content-height="true"> |
| | | <div class="mb-3 flex items-center justify-between"> |
| | | <span class="text-lg font-bold">KPIææ å®ä¹</span> |
| | | <a-button type="primary" @click="handleCreate">æ°å¢</a-button> |
| | | </div> |
| | | <Grid> |
| | | <template #categorySlot="{ row }"> |
| | | <Tag color="blue">{{ CATEGORY_MAP[row.category] || row.category }}</Tag> |
| | | </template> |
| | | <template #statusSlot="{ row }"> |
| | | <Tag :color="row.status === 1 ? 'green' : 'default'">{{ row.status === 1 ? 'å¯ç¨' : 'ç¦ç¨' }}</Tag> |
| | | </template> |
| | | <template #actionSlot="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { label: 'ç¼è¾', type: 'link', icon: ACTION_ICON.EDIT, onClick: handleEdit.bind(null, row) }, |
| | | { label: 'å é¤', type: 'link', danger: true, icon: ACTION_ICON.DELETE, onClick: handleDelete.bind(null, row) }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | <FormModal /> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { DecisionKpiApi } from '#/api/bi/decision/kpi'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createKpiDefinition, getKpiDefinition, updateKpiDefinition } from '#/api/bi/decision/kpi'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<DecisionKpiApi.KpiDefinition>(); |
| | | |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id ? 'ç¼è¾KPIææ ' : 'æ°å¢KPIææ '; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { class: 'w-full' }, |
| | | formItemClass: 'col-span-2', |
| | | labelWidth: 120, |
| | | }, |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as DecisionKpiApi.KpiDefinition; |
| | | try { |
| | | await (formData.value?.id ? updateKpiDefinition(data) : createKpiDefinition(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æä½æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<DecisionKpiApi.KpiDefinition>(); |
| | | if (!data || !data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getKpiDefinition(data.id); |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { onMounted, ref } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | |
| | | import { Spin, Tag } from 'ant-design-vue'; |
| | | |
| | | import { getKpiOverview } from '#/api/bi/decision/kpi'; |
| | | import type { DecisionKpiApi } from '#/api/bi/decision/kpi'; |
| | | |
| | | defineOptions({ name: 'DecisionKpiDashboard' }); |
| | | |
| | | const loading = ref(true); |
| | | const overview = ref<DecisionKpiApi.KpiOverview>({ kpis: [], total: 0 }); |
| | | |
| | | async function loadData() { |
| | | loading.value = true; |
| | | try { |
| | | overview.value = await getKpiOverview(); |
| | | } catch { |
| | | // ignore |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | const CATEGORY_MAP: Record<string, string> = { |
| | | power_supply: 'ä¾çµé', |
| | | device_operation: '设å¤è¿è¡', |
| | | production: 'ç产', |
| | | quality: 'è´¨é', |
| | | procurement: 'éè´', |
| | | safety: 'å®å
¨', |
| | | }; |
| | | |
| | | const CATEGORY_COLORS: Record<string, string> = { |
| | | power_supply: '#1677ff', |
| | | device_operation: '#722ed1', |
| | | production: '#13c2c2', |
| | | quality: '#52c41a', |
| | | procurement: '#fa8c16', |
| | | safety: '#ff4d4f', |
| | | }; |
| | | |
| | | function alertStatusColor(status: string): string { |
| | | if (status === 'critical') return 'red'; |
| | | if (status === 'warn') return 'orange'; |
| | | return 'green'; |
| | | } |
| | | |
| | | function alertStatusText(status: string): string { |
| | | if (status === 'critical') return '严é'; |
| | | if (status === 'warn') return 'è¦å'; |
| | | return 'æ£å¸¸'; |
| | | } |
| | | |
| | | onMounted(loadData); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page :auto-content-height="true"> |
| | | <Spin :spinning="loading"> |
| | | <div class="p-4"> |
| | | <h2 class="mb-4 text-lg font-bold">çµå KPI çæ¿</h2> |
| | | <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> |
| | | <div |
| | | v-for="kpi in overview.kpis" |
| | | :key="kpi.code" |
| | | class="rounded-lg border p-4 shadow-sm transition-shadow hover:shadow-md" |
| | | :style="{ borderTop: `3px solid ${CATEGORY_COLORS[kpi.category] || '#1677ff'}` }" |
| | | > |
| | | <div class="mb-1 flex items-center justify-between"> |
| | | <span class="text-xs text-gray-500">{{ CATEGORY_MAP[kpi.category] || kpi.category }}</span> |
| | | <Tag :color="alertStatusColor(kpi.alertStatus)"> |
| | | {{ alertStatusText(kpi.alertStatus) }} |
| | | </Tag> |
| | | </div> |
| | | <div class="mb-1 text-2xl font-bold"> |
| | | {{ kpi.value ?? '-' }} |
| | | </div> |
| | | <div class="flex items-center justify-between"> |
| | | <span class="text-sm font-medium">{{ kpi.name }}</span> |
| | | <span class="text-xs text-gray-400">{{ kpi.unit || '' }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </Spin> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { onMounted, ref } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | |
| | | import { Select, Spin, Table } from 'ant-design-vue'; |
| | | |
| | | import { getKpiOverview } from '#/api/bi/decision/kpi'; |
| | | |
| | | defineOptions({ name: 'DecisionTrendAnalysis' }); |
| | | |
| | | const loading = ref(false); |
| | | const trendData = ref<any[]>([]); |
| | | const selectedCategory = ref<string>(''); |
| | | |
| | | const CATEGORY_OPTIONS = [ |
| | | { label: 'å
¨é¨', value: '' }, |
| | | { label: 'ä¾çµé', value: 'power_supply' }, |
| | | { label: '设å¤è¿è¡', value: 'device_operation' }, |
| | | { label: 'ç产', value: 'production' }, |
| | | { label: 'è´¨é', value: 'quality' }, |
| | | { label: 'éè´', value: 'procurement' }, |
| | | { label: 'å®å
¨', value: 'safety' }, |
| | | ]; |
| | | |
| | | const columns = [ |
| | | { title: 'KPIåç§°', dataIndex: 'name', key: 'name', width: 150 }, |
| | | { title: 'åç±»', dataIndex: 'category', key: 'category', width: 100 }, |
| | | { title: 'å½åå¼', dataIndex: 'value', key: 'value', width: 120 }, |
| | | { title: 'åä½', dataIndex: 'unit', key: 'unit', width: 80 }, |
| | | { title: 'é¢è¦ç¶æ', dataIndex: 'alertStatus', key: 'alertStatus', width: 100 }, |
| | | ]; |
| | | |
| | | async function loadData() { |
| | | loading.value = true; |
| | | try { |
| | | const overview = await getKpiOverview(); |
| | | let list = overview.kpis || []; |
| | | if (selectedCategory.value) { |
| | | list = list.filter((k) => k.category === selectedCategory.value); |
| | | } |
| | | trendData.value = list; |
| | | } catch { |
| | | trendData.value = []; |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | onMounted(loadData); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page :auto-content-height="true"> |
| | | <div class="p-4"> |
| | | <div class="mb-4 flex items-center gap-4"> |
| | | <h2 class="text-lg font-bold">è¶å¿åæ</h2> |
| | | <Select |
| | | v-model:value="selectedCategory" |
| | | :options="CATEGORY_OPTIONS" |
| | | style="width: 160px" |
| | | allow-clear |
| | | @change="loadData" |
| | | /> |
| | | </div> |
| | | |
| | | <Spin :spinning="loading"> |
| | | <Table |
| | | :columns="columns" |
| | | :data-source="trendData" |
| | | :pagination="{ pageSize: 20 }" |
| | | row-key="code" |
| | | bordered |
| | | size="middle" |
| | | > |
| | | <template #bodyCell="{ column, record }"> |
| | | <template v-if="column.key === 'alertStatus'"> |
| | | <span v-if="record.alertStatus === 'critical'" class="text-red-500">严é</span> |
| | | <span v-else-if="record.alertStatus === 'warn'" class="text-orange-500">è¦å</span> |
| | | <span v-else class="text-green-500">æ£å¸¸</span> |
| | | </template> |
| | | </template> |
| | | </Table> |
| | | </Spin> |
| | | </div> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmCancellationApi } from '#/api/crm/cancellation'; |
| | | import type { CrmCustomerApi } from '#/api/crm/customer'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹éæ·ç³è¯· */ |
| | | export function useFormSchema( |
| | | formApi?: VbenFormApi, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'cancellationNo', |
| | | label: 'éæ·ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ä¸å¡«åèªå¨çæ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | onChange: (item?: CrmCustomerApi.Customer) => { |
| | | formApi?.setFieldValue('customerName', item?.name); |
| | | }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'customerName', |
| | | label: '客æ·åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '客æ·åç§°', |
| | | readonly: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'cancellationType', |
| | | label: 'éæ·ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.CRM_CANCELLATION_TYPE), |
| | | placeholder: 'è¯·éæ©éæ·ç±»å', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'contractNo', |
| | | label: 'ååç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å
³èååç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'expectedCancelDate', |
| | | label: 'é¢è®¡éæ·æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'actualCancelDate', |
| | | label: 'å®é
éæ·æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'refundAmount', |
| | | label: 'éè´¹éé¢', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥éè´¹éé¢ï¼å
ï¼', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'applyUserName', |
| | | label: 'ç³è¯·äºº', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç³è¯·äººå§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'applyTime', |
| | | label: 'ç³è¯·æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'cancelReason', |
| | | label: 'éæ·åå ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥éæ·åå ', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'cancellationNo', |
| | | label: 'éæ·ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥éæ·ç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'cancellationType', |
| | | label: 'éæ·ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.CRM_CANCELLATION_TYPE), |
| | | placeholder: 'è¯·éæ©éæ·ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'auditStatus', |
| | | label: '审æ¹ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.CRM_AUDIT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å®¡æ¹ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<CrmCancellationApi.Cancellation>['columns'] { |
| | | return [ |
| | | { field: 'cancellationNo', title: 'éæ·ç¼å·', minWidth: 170 }, |
| | | { field: 'customerName', title: '客æ·åç§°', minWidth: 180 }, |
| | | { |
| | | field: 'cancellationType', |
| | | title: 'éæ·ç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_CANCELLATION_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'contractNo', title: 'ååç¼å·', minWidth: 140 }, |
| | | { |
| | | field: 'expectedCancelDate', |
| | | title: 'é¢è®¡éæ·æ¥æ', |
| | | width: 120, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'actualCancelDate', |
| | | title: 'å®é
éæ·æ¥æ', |
| | | width: 120, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { field: 'refundAmount', title: 'éè´¹éé¢', width: 120 }, |
| | | { field: 'applyUserName', title: 'ç³è¯·äºº', width: 100 }, |
| | | { |
| | | field: 'auditStatus', |
| | | title: '审æ¹ç¶æ', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_AUDIT_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 200, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmCancellationApi } from '#/api/crm/cancellation'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | auditCancellation, |
| | | deleteCancellation, |
| | | exportCancellation, |
| | | getCancellationPage, |
| | | } from '#/api/crm/cancellation'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const formValues = await gridApi.formApi.getValues(); |
| | | const data = await exportCancellation(formValues); |
| | | downloadFileFromBlobPart({ fileName: 'éæ·ç³è¯·.xls', source: data }); |
| | | } |
| | | |
| | | /** æ°å¢ */ |
| | | function handleCreate() { |
| | | formModalApi.setData(null).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ */ |
| | | function handleEdit(row: CrmCancellationApi.Cancellation) { |
| | | formModalApi.setData({ cancellation: row }).open(); |
| | | } |
| | | |
| | | /** å é¤ */ |
| | | async function handleDelete(row: CrmCancellationApi.Cancellation) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.cancellationNo ?? '']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteCancellation(row.id!); |
| | | message.success( |
| | | $t('ui.actionMessage.deleteSuccess', [row.cancellationNo ?? '']), |
| | | ); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 审æ¹ï¼éè¿/驳åï¼ */ |
| | | async function handleAudit( |
| | | row: CrmCancellationApi.Cancellation, |
| | | pass: boolean, |
| | | ) { |
| | | const hideLoading = message.loading({ |
| | | content: pass ? '审æ¹éè¿ä¸...' : '审æ¹é©³åä¸...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await auditCancellation({ id: row.id!, auditStatus: pass ? 20 : 30 }); |
| | | message.success(pass ? '审æ¹éè¿' : '审æ¹é©³å'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getCancellationPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<CrmCancellationApi.Cancellation>, |
| | | }); |
| | | </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: ['crm:cancellation:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['crm:cancellation:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'éè¿', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['crm:cancellation:update'], |
| | | ifShow: row.auditStatus === 0 || row.auditStatus === 10, |
| | | popConfirm: { |
| | | title: '确认审æ¹éè¿è¯¥éæ·ç³è¯·ï¼', |
| | | confirm: handleAudit.bind(null, row, true), |
| | | }, |
| | | }, |
| | | { |
| | | label: '驳å', |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['crm:cancellation:update'], |
| | | ifShow: row.auditStatus === 0 || row.auditStatus === 10, |
| | | popConfirm: { |
| | | title: '确认驳åè¯¥éæ·ç³è¯·ï¼', |
| | | confirm: handleAudit.bind(null, row, false), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['crm:cancellation:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['crm:cancellation:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [ |
| | | row.cancellationNo ?? '', |
| | | ]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { CrmCancellationApi } from '#/api/crm/cancellation'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenForm, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | createCancellation, |
| | | getCancellation, |
| | | updateCancellation, |
| | | } from '#/api/crm/cancellation'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | type CancellationFormModalData = { |
| | | cancellation?: Pick<CrmCancellationApi.Cancellation, 'id'>; |
| | | }; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<Partial<CrmCancellationApi.Cancellation>>(); |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id |
| | | ? $t('ui.actionTitle.edit', ['éæ·ç³è¯·']) |
| | | : $t('ui.actionTitle.create', ['éæ·ç³è¯·']); |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 110, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | 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 CrmCancellationApi.Cancellation; |
| | | if (formData.value?.id) { |
| | | data.id = formData.value.id; |
| | | } |
| | | try { |
| | | await (formData.value?.id |
| | | ? updateCancellation(data) |
| | | : createCancellation(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; |
| | | } |
| | | formApi.setState({ schema: useFormSchema(formApi) }); |
| | | await formApi.resetForm(); |
| | | const data = modalApi.getData() as null | CancellationFormModalData; |
| | | if (!data?.cancellation?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getCancellation(data.cancellation.id); |
| | | if (formData.value) { |
| | | await formApi.setValues(formData.value as any); |
| | | } |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ååæä»¤ */ |
| | | export function useFormSchema( |
| | | formApi?: VbenFormApi, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'contractId', |
| | | label: 'ååID', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | precision: 0, |
| | | placeholder: 'ååID', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'contractNo', |
| | | label: 'ååç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ååç¼å·', |
| | | readonly: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'commandType', |
| | | label: 'æä»¤ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_COMMAND_TYPE), |
| | | placeholder: 'è¯·éæ©æä»¤ç±»å', |
| | | allowClear: true, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'targetModule', |
| | | label: 'ç®æ 模å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'MES å·¥å', value: 'mes-work-order' }, |
| | | { label: 'MES æè¡¨', value: 'mes-metering' }, |
| | | { label: 'SRM æå¡å', value: 'srm-service' }, |
| | | ], |
| | | placeholder: 'è¯·éæ©ç®æ 模å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'bizNo', |
| | | label: 'ä¸å¡åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ä¸å¡åå·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'operatorName', |
| | | label: 'æä½äºº', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æä½äººå§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'operateTime', |
| | | label: 'æä½æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { CrmContractCommandApi } from '#/api/crm/contract-command'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenForm, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | createContractCommand, |
| | | updateContractCommand, |
| | | } from '#/api/crm/contract-command'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from './command-form-data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const formData = ref<Partial<CrmContractCommandApi.ContractCommand>>(); |
| | | const getTitle = () => { |
| | | return formData.value?.id |
| | | ? $t('ui.actionTitle.edit', ['æä»¤']) |
| | | : $t('ui.actionTitle.create', ['æä»¤']); |
| | | }; |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 110, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | 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 CrmContractCommandApi.ContractCommand; |
| | | if (formData.value?.id) { |
| | | data.id = formData.value.id; |
| | | } |
| | | try { |
| | | await (formData.value?.id |
| | | ? updateContractCommand(data) |
| | | : createContractCommand(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; |
| | | } |
| | | formApi.setState({ schema: useFormSchema(formApi) }); |
| | | await formApi.resetForm(); |
| | | const data = modalApi.getData() as null | { |
| | | command?: CrmContractCommandApi.ContractCommand; |
| | | contractNo?: string; |
| | | }; |
| | | if (data?.contractNo) { |
| | | await formApi.setFieldValue('contractNo', data.contractNo); |
| | | } |
| | | if (!data?.command?.id) { |
| | | return; |
| | | } |
| | | formData.value = data.command; |
| | | await formApi.setValues(formData.value as any); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle()" class="w-2/3"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmContractCommandApi } from '#/api/crm/contract-command'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | |
| | | /** ååæä»¤è¯¦æ
åè¡¨åæ®µ */ |
| | | export function useDetailListColumns(): VxeTableGridOptions<CrmContractCommandApi.ContractCommand>['columns'] { |
| | | return [ |
| | | { |
| | | field: 'commandType', |
| | | title: 'æä»¤ç±»å', |
| | | width: 120, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_COMMAND_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'targetModule', |
| | | title: 'ç®æ 模å', |
| | | width: 130, |
| | | }, |
| | | { |
| | | field: 'bizNo', |
| | | title: 'ä¸å¡åå·', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'operatorName', |
| | | title: 'æä½äºº', |
| | | width: 100, |
| | | }, |
| | | { |
| | | field: 'operateTime', |
| | | title: 'æä½æ¶é´', |
| | | minWidth: 160, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_COMMAND_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 220, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <!-- ååæä»¤ä¸åå表ï¼ç¨äºãååã详æ
Tabï¼å±ç¤ºå¹¶ä¸å/æ§è¡/åæ¶ååæä»¤ --> |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmContractCommandApi } from '#/api/crm/contract-command'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | cancelContractCommand, |
| | | deleteContractCommand, |
| | | executeContractCommand, |
| | | getContractCommandPage, |
| | | } from '#/api/crm/contract-command'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import CommandForm from './command-form.vue'; |
| | | import { useDetailListColumns } from './command-list-data'; |
| | | |
| | | const props = defineProps<{ |
| | | contractId?: number; // ååç¼å· |
| | | contractNo?: string; // åååå· |
| | | }>(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: CommandForm, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** å建æä»¤ */ |
| | | function handleCreate() { |
| | | formModalApi |
| | | .setData({ contractNo: props.contractNo }) |
| | | .open(); |
| | | } |
| | | |
| | | /** ç¼è¾æä»¤ */ |
| | | function handleEdit(row: CrmContractCommandApi.ContractCommand) { |
| | | formModalApi.setData({ command: row }).open(); |
| | | } |
| | | |
| | | /** æ§è¡æä»¤ */ |
| | | async function handleExecute(row: CrmContractCommandApi.ContractCommand) { |
| | | const hideLoading = message.loading({ content: 'æä»¤æ§è¡ä¸...', duration: 0 }); |
| | | try { |
| | | await executeContractCommand(row.id!); |
| | | message.success('æä»¤æ§è¡å®æ'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** åæ¶æä»¤ */ |
| | | async function handleCancel(row: CrmContractCommandApi.ContractCommand) { |
| | | const hideLoading = message.loading({ content: 'æä»¤åæ¶ä¸...', duration: 0 }); |
| | | try { |
| | | await cancelContractCommand(row.id!); |
| | | message.success('æä»¤å·²åæ¶'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** å 餿令 */ |
| | | async function handleDelete(row: CrmContractCommandApi.ContractCommand) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.bizNo ?? '']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteContractCommand(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.bizNo ?? ''])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: useDetailListColumns(), |
| | | height: 400, |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }) => { |
| | | if (!props.contractId) { |
| | | return { list: [], total: 0 }; |
| | | } |
| | | return await getContractCommandPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | contractId: props.contractId, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: false, |
| | | }, |
| | | } as VxeTableGridOptions<CrmContractCommandApi.ContractCommand>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <div> |
| | | <FormModal @success="handleRefresh" /> |
| | | <Grid> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'ä¸åæä»¤', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['crm:contract-command:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ§è¡', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['crm:contract-command:update'], |
| | | ifShow: row.status === 0 || row.status === 10, |
| | | popConfirm: { |
| | | title: '确认æ§è¡è¯¥æä»¤ï¼', |
| | | confirm: handleExecute.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'åæ¶', |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.CLOSE, |
| | | auth: ['crm:contract-command:update'], |
| | | ifShow: row.status === 0 || row.status === 10, |
| | | popConfirm: { |
| | | title: 'ç¡®è®¤åæ¶è¯¥æä»¤ï¼', |
| | | confirm: handleCancel.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['crm:contract-command:update'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['crm:contract-command:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [ |
| | | row.bizNo ?? '', |
| | | ]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </div> |
| | | </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'; |
| | | import { useUserStore } from '@vben/stores'; |
| | | import { erpPriceInputFormatter, erpPriceMultiply } from '@vben/utils'; |
| | | |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'contractType', |
| | | label: 'ååç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_CONTRACT_TYPE), |
| | | placeholder: 'è¯·éæ©ååç±»å', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'voltageLevel', |
| | | label: 'çµåç级', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_VOLTAGE_LEVEL), |
| | | placeholder: 'è¯·éæ©çµåç级', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'electricityType', |
| | | label: 'ç¨çµç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_ELECTRICITY_TYPE), |
| | | placeholder: 'è¯·éæ©ç¨çµç±»å', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'contractCapacity', |
| | | label: 'åå容éï¼kVAï¼', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥åå容é', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'tariffMode', |
| | | label: 'çµä»·æ¨¡å¼', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_TARIFF_MODE), |
| | | placeholder: 'è¯·éæ©çµä»·æ¨¡å¼', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'meterMode', |
| | | label: 'è®¡éæ¹å¼', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_METER_MODE), |
| | | placeholder: 'è¯·éæ©è®¡éæ¹å¼', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'payCycle', |
| | | label: 'ç¼´è´¹å¨æï¼æï¼', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 1, |
| | | precision: 0, |
| | | placeholder: '请è¾å
¥ç¼´è´¹å¨æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | |
| | | slots: { default: 'businessName' }, |
| | | }, |
| | | { |
| | | title: 'ååç±»å', |
| | | field: 'contractType', |
| | | minWidth: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_CONTRACT_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | title: 'çµåç级', |
| | | field: 'voltageLevel', |
| | | minWidth: 90, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_VOLTAGE_LEVEL }, |
| | | }, |
| | | }, |
| | | { |
| | | title: 'åå容é', |
| | | field: 'contractCapacity', |
| | | minWidth: 100, |
| | | }, |
| | | { |
| | | title: 'æä»¤æ°', |
| | | field: 'commandCount', |
| | | minWidth: 80, |
| | | }, |
| | | { |
| | | title: 'ååéé¢ï¼å
ï¼', |
| | | field: 'totalPrice', |
| | | minWidth: 140, |
| | |
| | | import Form from '../modules/form.vue'; |
| | | import { useDetailSchema } from './data'; |
| | | import ContractDetailsInfo from './modules/info.vue'; |
| | | import CommandDetailsList from '../components/command-list.vue'; |
| | | |
| | | const props = defineProps<{ id?: number }>(); |
| | | |
| | |
| | | <Tabs.TabPane tab="æä½æ¥å¿" key="6" :force-render="true"> |
| | | <OperateLog :log-list="logList" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane tab="æä»¤ä¸å" key="7" :force-render="true"> |
| | | <CommandDetailsList |
| | | v-if="contractId" |
| | | :contract-id="contractId" |
| | | :contract-no="contract.no" |
| | | /> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | </Card> |
| | | </Page> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmCustomerApi } from '#/api/crm/customer'; |
| | | import type { CrmCreditRatingApi } from '#/api/crm/credit-rating'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹ä¿¡ç¨è¯çº§ */ |
| | | export function useFormSchema( |
| | | formApi?: VbenFormApi, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | onChange: (item?: CrmCustomerApi.Customer) => { |
| | | formApi?.setFieldValue('customerName', item?.name); |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerName', |
| | | label: '客æ·åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '客æ·åç§°', |
| | | readonly: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'level', |
| | | label: 'ä¿¡ç¨ç级', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.CRM_CREDIT_LEVEL), |
| | | placeholder: 'è¯·éæ©ä¿¡ç¨ç级', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'score', |
| | | label: 'ä¿¡ç¨å', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | min: 0, |
| | | max: 100, |
| | | precision: 0, |
| | | placeholder: '0-100', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'evalType', |
| | | label: 'è¯å®ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'åè¯', value: 'INITIAL' }, |
| | | { label: 'å¤è¯', value: 'REVIEW' }, |
| | | { label: 'å¨æè°æ´', value: 'ADJUST' }, |
| | | ], |
| | | placeholder: 'è¯·éæ©è¯å®ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'evalReason', |
| | | label: 'è¯å®çç±', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è¯å®çç±', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'operateTime', |
| | | label: 'è¯å®æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'validUntil', |
| | | label: 'æææè³', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'operatorName', |
| | | label: 'è¯å®äºº', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è¯å®äººå§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'level', |
| | | label: 'ä¿¡ç¨ç级', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.CRM_CREDIT_LEVEL), |
| | | placeholder: 'è¯·éæ©ä¿¡ç¨ç级', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'evalType', |
| | | label: 'è¯å®ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'åè¯', value: 'INITIAL' }, |
| | | { label: 'å¤è¯', value: 'REVIEW' }, |
| | | { label: 'å¨æè°æ´', value: 'ADJUST' }, |
| | | ], |
| | | placeholder: 'è¯·éæ©è¯å®ç±»å', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<CrmCreditRatingApi.CreditRating>['columns'] { |
| | | return [ |
| | | { field: 'customerName', title: '客æ·åç§°', minWidth: 180 }, |
| | | { |
| | | field: 'level', |
| | | title: 'ä¿¡ç¨ç级', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_CREDIT_LEVEL }, |
| | | }, |
| | | }, |
| | | { field: 'score', title: 'ä¿¡ç¨å', width: 90 }, |
| | | { |
| | | field: 'evalType', |
| | | title: 'è¯å®ç±»å', |
| | | width: 120, |
| | | slots: { default: 'evalType' }, |
| | | }, |
| | | { field: 'evalReason', title: 'è¯å®çç±', minWidth: 200, showOverflow: true }, |
| | | { field: 'operatorName', title: 'è¯å®äºº', width: 100 }, |
| | | { |
| | | field: 'operateTime', |
| | | title: 'è¯å®æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'validUntil', |
| | | title: 'æææè³', |
| | | width: 120, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 160, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmCreditRatingApi } from '#/api/crm/credit-rating'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteCreditRating, |
| | | exportCreditRating, |
| | | getCreditRatingPage, |
| | | } from '#/api/crm/credit-rating'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const formValues = await gridApi.formApi.getValues(); |
| | | const data = await exportCreditRating(formValues); |
| | | downloadFileFromBlobPart({ fileName: '客æ·ä¿¡ç¨è¯çº§.xls', source: data }); |
| | | } |
| | | |
| | | /** æ°å¢ */ |
| | | function handleCreate() { |
| | | formModalApi.setData(null).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ */ |
| | | function handleEdit(row: CrmCreditRatingApi.CreditRating) { |
| | | formModalApi.setData({ creditRating: row }).open(); |
| | | } |
| | | |
| | | /** å é¤ */ |
| | | async function handleDelete(row: CrmCreditRatingApi.CreditRating) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.customerName ?? '']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteCreditRating(row.id!); |
| | | message.success( |
| | | $t('ui.actionMessage.deleteSuccess', [row.customerName ?? '']), |
| | | ); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getCreditRatingPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<CrmCreditRatingApi.CreditRating>, |
| | | }); |
| | | </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: ['crm:credit-rating:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['crm:credit-rating:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #evalType="{ row }"> |
| | | {{ |
| | | row.evalType === 'INITIAL' |
| | | ? 'åè¯' |
| | | : row.evalType === 'REVIEW' |
| | | ? 'å¤è¯' |
| | | : 'å¨æè°æ´' |
| | | }} |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['crm:credit-rating:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['crm:credit-rating:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [ |
| | | row.customerName ?? '', |
| | | ]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { CrmCreditRatingApi } from '#/api/crm/credit-rating'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenForm, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | createCreditRating, |
| | | getCreditRating, |
| | | updateCreditRating, |
| | | } from '#/api/crm/credit-rating'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | type CreditRatingFormModalData = { |
| | | creditRating?: Pick<CrmCreditRatingApi.CreditRating, 'id'>; |
| | | }; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<Partial<CrmCreditRatingApi.CreditRating>>(); |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id |
| | | ? $t('ui.actionTitle.edit', ['ä¿¡ç¨è¯çº§']) |
| | | : $t('ui.actionTitle.create', ['ä¿¡ç¨è¯çº§']); |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 110, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | 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 CrmCreditRatingApi.CreditRating; |
| | | if (formData.value?.id) { |
| | | data.id = formData.value.id; |
| | | } |
| | | try { |
| | | await (formData.value?.id |
| | | ? updateCreditRating(data) |
| | | : createCreditRating(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; |
| | | } |
| | | formApi.setState({ schema: useFormSchema(formApi) }); |
| | | await formApi.resetForm(); |
| | | const data = modalApi.getData() as null | CreditRatingFormModalData; |
| | | if (!data?.creditRating?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getCreditRating(data.creditRating.id); |
| | | if (formData.value) { |
| | | await formApi.setValues(formData.value as any); |
| | | } |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerType', |
| | | label: '客æ·åç±»', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_TYPE), |
| | | placeholder: 'è¯·éæ©å®¢æ·åç±»', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'mobile', |
| | | label: 'ææº', |
| | | component: 'Input', |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerType', |
| | | label: '客æ·åç±»', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_TYPE), |
| | | placeholder: 'è¯·éæ©å®¢æ·åç±»', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'creditLevel', |
| | | label: 'ä¿¡ç¨ç级', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_CREDIT_LEVEL), |
| | | placeholder: 'è¯·éæ©ä¿¡ç¨ç级', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'createTime', |
| | | label: 'å建æ¶é´', |
| | | component: 'RangePicker', |
| | |
| | | }, |
| | | }, |
| | | { |
| | | field: 'customerType', |
| | | title: '客æ·åç±»', |
| | | minWidth: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_CUSTOMER_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'creditLevel', |
| | | title: 'ä¿¡ç¨ç级', |
| | | minWidth: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_CREDIT_LEVEL }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'creditScore', |
| | | title: 'ä¿¡ç¨å', |
| | | minWidth: 90, |
| | | }, |
| | | { |
| | | field: 'overdueAmount', |
| | | title: 'æ¬ è´¹éé¢', |
| | | minWidth: 110, |
| | | formatter: 'formatAmount2', |
| | | }, |
| | | { |
| | | field: 'overdueCount', |
| | | title: 'æ¬ è´¹ç¬æ°', |
| | | minWidth: 90, |
| | | }, |
| | | { |
| | | field: 'mobile', |
| | | title: 'ææº', |
| | | minWidth: 120, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmCustomerApi } from '#/api/crm/customer'; |
| | | import type { CrmRefundApi } from '#/api/crm/refund'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹éè´¹å */ |
| | | export function useFormSchema( |
| | | formApi?: VbenFormApi, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'refundNo', |
| | | label: 'éè´¹åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ä¸å¡«åèªå¨çæ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | onChange: (item?: CrmCustomerApi.Customer) => { |
| | | formApi?.setFieldValue('customerName', item?.name); |
| | | }, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'customerName', |
| | | label: '客æ·åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '客æ·åç§°', |
| | | readonly: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'refundType', |
| | | label: 'é费类å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.CRM_REFUND_TYPE), |
| | | placeholder: 'è¯·éæ©é费类å', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'refundAmount', |
| | | label: 'éè´¹éé¢', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: 'w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥éè´¹éé¢ï¼å
ï¼', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'contractNo', |
| | | label: 'ååç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å
³èååç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'applyUserName', |
| | | label: 'ç³è¯·äºº', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç³è¯·äººå§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'applyTime', |
| | | label: 'ç³è¯·æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'refundReason', |
| | | label: 'éè´¹åå ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥éè´¹åå ', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'refundNo', |
| | | label: 'éè´¹åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥éè´¹åå·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'refundType', |
| | | label: 'é费类å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.CRM_REFUND_TYPE), |
| | | placeholder: 'è¯·éæ©é费类å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'auditStatus', |
| | | label: '审æ¹ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.CRM_AUDIT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å®¡æ¹ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<CrmRefundApi.Refund>['columns'] { |
| | | return [ |
| | | { field: 'refundNo', title: 'éè´¹åå·', minWidth: 170 }, |
| | | { field: 'customerName', title: '客æ·åç§°', minWidth: 180 }, |
| | | { |
| | | field: 'refundType', |
| | | title: 'é费类å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_REFUND_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'refundAmount', title: 'éè´¹éé¢', width: 120 }, |
| | | { field: 'contractNo', title: 'ååç¼å·', minWidth: 140 }, |
| | | { field: 'applyUserName', title: 'ç³è¯·äºº', width: 100 }, |
| | | { |
| | | field: 'applyTime', |
| | | title: 'ç³è¯·æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'auditStatus', |
| | | title: '审æ¹ç¶æ', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_AUDIT_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 200, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmRefundApi } from '#/api/crm/refund'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | auditRefund, |
| | | deleteRefund, |
| | | exportRefund, |
| | | getRefundPage, |
| | | } from '#/api/crm/refund'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const formValues = await gridApi.formApi.getValues(); |
| | | const data = await exportRefund(formValues); |
| | | downloadFileFromBlobPart({ fileName: 'éè´¹å.xls', source: data }); |
| | | } |
| | | |
| | | /** æ°å¢ */ |
| | | function handleCreate() { |
| | | formModalApi.setData(null).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ */ |
| | | function handleEdit(row: CrmRefundApi.Refund) { |
| | | formModalApi.setData({ refund: row }).open(); |
| | | } |
| | | |
| | | /** å é¤ */ |
| | | async function handleDelete(row: CrmRefundApi.Refund) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.refundNo ?? '']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteRefund(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.refundNo ?? ''])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** 审æ¹ï¼éè¿/驳åï¼ */ |
| | | async function handleAudit(row: CrmRefundApi.Refund, pass: boolean) { |
| | | const hideLoading = message.loading({ |
| | | content: pass ? '审æ¹éè¿ä¸...' : '审æ¹é©³åä¸...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await auditRefund({ id: row.id!, auditStatus: pass ? 20 : 30 }); |
| | | message.success(pass ? '审æ¹éè¿' : '审æ¹é©³å'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getRefundPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<CrmRefundApi.Refund>, |
| | | }); |
| | | </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: ['crm:refund:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['crm:refund:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'éè¿', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['crm:refund:update'], |
| | | ifShow: row.auditStatus === 0 || row.auditStatus === 10, |
| | | popConfirm: { |
| | | title: '确认审æ¹éè¿è¯¥éè´¹åï¼', |
| | | confirm: handleAudit.bind(null, row, true), |
| | | }, |
| | | }, |
| | | { |
| | | label: '驳å', |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['crm:refund:update'], |
| | | ifShow: row.auditStatus === 0 || row.auditStatus === 10, |
| | | popConfirm: { |
| | | title: '确认驳å该éè´¹åï¼', |
| | | confirm: handleAudit.bind(null, row, false), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['crm:refund:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['crm:refund:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [ |
| | | row.refundNo ?? '', |
| | | ]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { CrmRefundApi } from '#/api/crm/refund'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenForm, useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | createRefund, |
| | | getRefund, |
| | | updateRefund, |
| | | } from '#/api/crm/refund'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | type RefundFormModalData = { |
| | | refund?: Pick<CrmRefundApi.Refund, 'id'>; |
| | | }; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<Partial<CrmRefundApi.Refund>>(); |
| | | const getTitle = computed(() => { |
| | | return formData.value?.id |
| | | ? $t('ui.actionTitle.edit', ['éè´¹å']) |
| | | : $t('ui.actionTitle.create', ['éè´¹å']); |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | labelWidth: 110, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | 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 CrmRefundApi.Refund; |
| | | if (formData.value?.id) { |
| | | data.id = formData.value.id; |
| | | } |
| | | try { |
| | | await (formData.value?.id ? updateRefund(data) : createRefund(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; |
| | | } |
| | | formApi.setState({ schema: useFormSchema(formApi) }); |
| | | await formApi.resetForm(); |
| | | const data = modalApi.getData() as null | RefundFormModalData; |
| | | if (!data?.refund?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getRefund(data.refund.id); |
| | | if (formData.value) { |
| | | await formApi.setValues(formData.value as any); |
| | | } |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </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'; |
| | | |
| | | import { erpPriceMultiply } from '#/packages/utils/src'; |
| | | |
| | | import { z } from '#/adapter/form'; |
| | |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'quotationType', |
| | | label: 'æ¥ä»·ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_QUOTATION_TYPE), |
| | | placeholder: 'è¯·éæ©æ¥ä»·ç±»å', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'capacity', |
| | | label: 'åå容éï¼kVAï¼', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥åå容é', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'maxDemand', |
| | | label: 'æå¤§ééï¼kWï¼', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥æå¤§éé', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'unitPrice', |
| | | label: 'çµåº¦åä»·ï¼å
/kWhï¼', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 4, |
| | | placeholder: '请è¾å
¥çµåº¦åä»·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'tariffMode', |
| | | label: 'çµä»·æ¨¡å¼', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.CRM_TARIFF_MODE), |
| | | placeholder: 'è¯·éæ©çµä»·æ¨¡å¼', |
| | | allowClear: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'powerFactorFee', |
| | | label: 'åè°çµè´¹ï¼å
ï¼', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥åè°çµè´¹', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'billingCycle', |
| | | label: 'ç»ç®å¨æï¼æï¼', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 1, |
| | | precision: 0, |
| | | placeholder: '请è¾å
¥ç»ç®å¨æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | |
| | | fixed: 'left', |
| | | }, |
| | | { |
| | | field: 'quotationType', |
| | | title: 'æ¥ä»·ç±»å', |
| | | minWidth: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.CRM_QUOTATION_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'customerName', |
| | | title: '客æ·åç§°', |
| | | minWidth: 120, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { getSupplierSimpleList } from '#/api/srm/supplier'; |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** ç¶æå¸¸é */ |
| | | export const PURCHASE_PLAN_STATUS = { |
| | | DRAFT: 0, // æªæäº¤ï¼èç¨¿ï¼ |
| | | PROCESSING: 10, // 审æ¹ä¸ |
| | | APPROVED: 20, // å®¡æ ¸éè¿ |
| | | REJECTED: 30, // å®¡æ ¸ä¸éè¿ |
| | | CANCELLED: 40, // 已忶 |
| | | }; |
| | | |
| | | /** éæ±æ¥æºå¸¸é */ |
| | | export const DEMAND_SOURCE = { |
| | | STOCK_ALERT: 1, // åºåé¢è¦ |
| | | PLAN: 2, // è¿ç»´/ç产计å |
| | | }; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | const isReadonly = formType === 'detail'; |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'no', |
| | | label: '计ååå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'ç³»ç»èªå¨çæ', |
| | | disabled: true, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: '计ååç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è®¡ååç§°', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'planDate', |
| | | label: 'è®¡åæ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'éæ©è®¡åæ¥æ', |
| | | showTime: true, |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'demandSource', |
| | | label: 'éæ±æ¥æº', |
| | | component: 'Select', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©éæ±æ¥æº', |
| | | allowClear: true, |
| | | options: getDictOptions('erp_purchase_plan_demand_source', 'number'), |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | label: 'ä¾åºå', |
| | | fieldName: 'supplierId', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©ä¾åºå', |
| | | allowClear: true, |
| | | showSearch: true, |
| | | api: getSupplierSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: isReadonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'items', |
| | | label: '计åæç»', |
| | | component: 'Input', |
| | | formItemClass: 'col-span-3', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'no', |
| | | label: '计ååå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è®¡ååå·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: '计ååç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è®¡ååç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'demandSource', |
| | | label: 'éæ±æ¥æº', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions('erp_purchase_plan_demand_source', 'number'), |
| | | placeholder: 'è¯·éæ©éæ±æ¥æº', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'æªæäº¤', value: PURCHASE_PLAN_STATUS.DRAFT }, |
| | | { label: '审æ¹ä¸', value: PURCHASE_PLAN_STATUS.PROCESSING }, |
| | | { label: 'å®¡æ ¸éè¿', value: PURCHASE_PLAN_STATUS.APPROVED }, |
| | | { label: 'å®¡æ ¸ä¸éè¿', value: PURCHASE_PLAN_STATUS.REJECTED }, |
| | | { label: '已忶', value: PURCHASE_PLAN_STATUS.CANCELLED }, |
| | | ], |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'planDate', |
| | | label: 'è®¡åæ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: getRangePickerDefaultProps(), |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çè¡¨æ ¼å */ |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { type: 'seq', title: 'åºå·', width: 60 }, |
| | | { |
| | | field: 'no', |
| | | title: '计ååå·', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'name', |
| | | title: '计ååç§°', |
| | | minWidth: 180, |
| | | }, |
| | | { |
| | | field: 'demandSourceName', |
| | | title: 'éæ±æ¥æº', |
| | | width: 110, |
| | | }, |
| | | { |
| | | field: 'planUserName', |
| | | title: 'ç¼å¶äºº', |
| | | width: 110, |
| | | }, |
| | | { |
| | | field: 'planDeptName', |
| | | title: 'ç¼å¶é¨é¨', |
| | | width: 120, |
| | | }, |
| | | { |
| | | field: 'supplierName', |
| | | title: 'ä¾åºå', |
| | | minWidth: 130, |
| | | }, |
| | | { |
| | | field: 'productNames', |
| | | title: '产å', |
| | | minWidth: 180, |
| | | }, |
| | | { |
| | | field: 'totalCount', |
| | | title: 'å计æ°é', |
| | | width: 100, |
| | | formatter: 'formatAmount3', |
| | | }, |
| | | { |
| | | field: 'planDate', |
| | | title: 'è®¡åæ¥æ', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 280, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { ErpPurchasePlanApi } from '#/api/erp/purchase/plan'; |
| | | |
| | | import { ref } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { confirm, Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart, isEmpty } from '@vben/utils'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deletePurchasePlan, |
| | | exportPurchasePlan, |
| | | generatePlanFromStockAlert, |
| | | generatePurchaseRequests, |
| | | getApproveProcessList, |
| | | getPurchasePlanPage, |
| | | } from '#/api/erp/purchase/plan'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | import ProcessSelectModal from './modules/process-select-modal.vue'; |
| | | |
| | | /** ERP éè´è®¡åå表 */ |
| | | defineOptions({ name: 'ErpPurchasePlan' }); |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [ProcessModal, processModalApi] = useVbenModal({ |
| | | connectedComponent: ProcessSelectModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** 导åºè¡¨æ ¼ */ |
| | | async function handleExport() { |
| | | const data = await exportPurchasePlan(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'éè´è®¡å.xls', source: data }); |
| | | } |
| | | |
| | | /** æ°å¢éè´è®¡å */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾éè´è®¡å */ |
| | | function handleEdit(row: ErpPurchasePlanApi.PurchasePlan) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | /** å é¤éè´è®¡å */ |
| | | async function handleDelete(ids: number[]) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting'), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deletePurchasePlan(ids); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** åºåé¢è¦çæéè´è®¡å */ |
| | | async function handleGenerateFromStockAlert() { |
| | | const hideLoading = message.loading({ |
| | | content: 'æ£å¨æ ¹æ®åºåé¢è¦çæéè´è®¡å...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const planId = await generatePlanFromStockAlert({}); |
| | | hideLoading(); |
| | | message.success(`å·²çæéè´è®¡åï¼ç¼å· ${planId}ï¼ï¼è¯·æ ¸å¯¹æç»åæäº¤å®¡æ¹`); |
| | | handleRefresh(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ - å
è·åæµç¨å表 */ |
| | | async function handleSubmit(row: ErpPurchasePlanApi.PurchasePlan) { |
| | | const hideLoading = message.loading({ |
| | | content: 'è·åå®¡æ¹æµç¨...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const processList = await getApproveProcessList(row.id!); |
| | | hideLoading(); |
| | | if (!processList || processList.length === 0) { |
| | | message.warning('ææ å¯ç¨å®¡æ¹æµç¨ï¼è¯·å
é
ç½® BPM æµç¨'); |
| | | return; |
| | | } |
| | | processModalApi.setData({ id: row.id, processList }).open(); |
| | | } catch { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** çæéè´ç³è¯· */ |
| | | function handleGenerateRequests(row: ErpPurchasePlanApi.PurchasePlan) { |
| | | confirm({ |
| | | content: `ç¡®è®¤æ ¹æ®è®¡åã${row.name}ãçæéè´ç³è¯·ï¼`, |
| | | }).then(() => { |
| | | const hideLoading = message.loading({ |
| | | content: 'æ£å¨çæéè´ç³è¯·...', |
| | | duration: 0, |
| | | }); |
| | | generatePurchaseRequests(row.id!) |
| | | .then((requestIds) => { |
| | | message.success(`å·²çæéè´ç³è¯·ï¼ç¼å· ${requestIds.join(', ')}ï¼`); |
| | | handleRefresh(); |
| | | }) |
| | | .finally(() => { |
| | | hideLoading(); |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: ErpPurchasePlanApi.PurchasePlan[]; |
| | | }) { |
| | | checkedIds.value = records.map((item) => item.id!); |
| | | } |
| | | |
| | | /** æ¥ç详æ
*/ |
| | | function handleDetail(row: ErpPurchasePlanApi.PurchasePlan) { |
| | | formModalApi.setData({ formType: 'detail', id: row.id }).open(); |
| | | } |
| | | |
| | | /** æ¥ç审æ¹è¯¦æ
*/ |
| | | function handleViewProcess(row: ErpPurchasePlanApi.PurchasePlan) { |
| | | if (!row.processInstanceId) { |
| | | message.warning('该计åå°æªæäº¤å®¡æ¹'); |
| | | return; |
| | | } |
| | | router.push({ name: 'BpmProcessInstanceDetail', query: { id: row.processInstanceId } }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => { |
| | | return await getPurchasePlanPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<ErpPurchasePlanApi.PurchasePlan>, |
| | | gridEvents: { |
| | | checkboxAll: handleRowCheckboxChange, |
| | | checkboxChange: handleRowCheckboxChange, |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <ProcessModal @success="handleRefresh" /> |
| | | <Grid table-title="éè´è®¡åå表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['éè´è®¡å']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['erp:purchase-plan:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'åºåé¢è¦çæ', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['erp:purchase-plan:create'], |
| | | popConfirm: { |
| | | title: 'æ ¹æ®å®å
¨åºå缺éèªå¨çæè稿éè´è®¡åï¼', |
| | | confirm: handleGenerateFromStockAlert, |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['erp:purchase-plan:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['erp:purchase-plan:delete'], |
| | | popConfirm: { |
| | | title: `æ¯å¦å 餿é䏿°æ®ï¼`, |
| | | confirm: handleDelete.bind(null, checkedIds), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <Tag v-if="row.status === 0" color="default">æªæäº¤</Tag> |
| | | <Tag v-else-if="row.status === 10" color="processing">审æ¹ä¸</Tag> |
| | | <Tag v-else-if="row.status === 20" color="success">å®¡æ ¸éè¿</Tag> |
| | | <Tag v-else-if="row.status === 30" color="error">å®¡æ ¸ä¸éè¿</Tag> |
| | | <Tag v-else-if="row.status === 40" color="warning">已忶</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['erp:purchase-plan:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['erp:purchase-plan:update'], |
| | | ifShow: () => row.status === 0, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤', |
| | | type: 'link', |
| | | ifShow: () => row.status === 0 || row.status === 30, |
| | | onClick: handleSubmit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'çæç³è¯·', |
| | | type: 'link', |
| | | auth: ['erp:purchase-plan:generate-request'], |
| | | ifShow: () => row.status === 20 && !row.generatedFlag, |
| | | onClick: handleGenerateRequests.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æ¥ç审æ¹', |
| | | type: 'link', |
| | | ifShow: () => row.status !== 0, |
| | | onClick: handleViewProcess.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['erp:purchase-plan:delete'], |
| | | ifShow: () => row.status === 0, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.no]), |
| | | confirm: handleDelete.bind(null, [row.id!]), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { ErpPurchasePlanApi } from '#/api/erp/purchase/plan'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { Button, DatePicker, Input, InputNumber, message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createPurchasePlan, |
| | | getPurchasePlan, |
| | | updatePurchasePlan, |
| | | } from '#/api/erp/purchase/plan'; |
| | | import { MdmItemSelect } from '#/views/basicData/mdm/components'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<ErpPurchasePlanApi.PurchasePlan>(); |
| | | const formType = ref<FormType>('create'); |
| | | const items = ref<ErpPurchasePlanApi.PurchasePlanItem[]>([]); |
| | | |
| | | 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, |
| | | }); |
| | | |
| | | function handleAddItem() { |
| | | items.value.push({ |
| | | productId: undefined as unknown as number, |
| | | productName: '', |
| | | productUnitId: undefined as unknown as number, |
| | | productUnitName: '', |
| | | count: 1, |
| | | demandTime: undefined, |
| | | remark: '', |
| | | }); |
| | | } |
| | | |
| | | function handleRemoveItem(index: number) { |
| | | items.value.splice(index, 1); |
| | | } |
| | | |
| | | function handleItemSelect(index: number, item: any) { |
| | | const row = items.value[index]; |
| | | if (!row) { |
| | | return; |
| | | } |
| | | row.productId = item?.id; |
| | | row.productName = item?.name; |
| | | row.productUnitId = item?.unitMeasureId; |
| | | row.productUnitName = item?.unitMeasureName; |
| | | } |
| | | |
| | | /** åå»ºææ´æ°éè´è®¡å */ |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | if (!items.value.length) { |
| | | message.error('请è³å°æ·»å 䏿¡è®¡åæç»'); |
| | | return; |
| | | } |
| | | if (items.value.some((item) => !item.productId || !item.count)) { |
| | | message.error('请å®å计åæç»ç产å䏿°é'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = |
| | | (await formApi.getValues()) as ErpPurchasePlanApi.PurchasePlan; |
| | | data.items = items.value.map((item) => ({ |
| | | productId: item.productId, |
| | | productUnitId: item.productUnitId, |
| | | count: item.count, |
| | | demandTime: item.demandTime, |
| | | remark: item.remark, |
| | | })); |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createPurchasePlan(data) |
| | | : updatePurchasePlan(data)); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | formData.value = undefined; |
| | | items.value = []; |
| | | 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 getPurchasePlan(data.id); |
| | | await formApi.setValues(formData.value); |
| | | items.value = (formData.value.items ?? []).map((item) => ({ |
| | | ...item, |
| | | })); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal |
| | | :title="getTitle" |
| | | class="w-3/4" |
| | | :show-confirm-button="formType !== 'detail'" |
| | | > |
| | | <Form class="mx-3"> |
| | | <template #items> |
| | | <div class="mt-2"> |
| | | <div v-if="formType !== 'detail'" class="mb-2"> |
| | | <Button type="dashed" @click="handleAddItem">+ æ·»å æç»</Button> |
| | | </div> |
| | | <table class="w-full border-collapse border border-gray-200 text-sm"> |
| | | <thead> |
| | | <tr class="bg-gray-100"> |
| | | <th class="border border-gray-200 px-2 py-1 w-12">åºå·</th> |
| | | <th class="border border-gray-200 px-2 py-1">产å</th> |
| | | <th class="border border-gray-200 px-2 py-1 w-20">åä½</th> |
| | | <th class="border border-gray-200 px-2 py-1 w-32">æ°é</th> |
| | | <th class="border border-gray-200 px-2 py-1 w-40">éæ±æ¥æ</th> |
| | | <th class="border border-gray-200 px-2 py-1">夿³¨</th> |
| | | <th |
| | | v-if="formType !== 'detail'" |
| | | class="border border-gray-200 px-2 py-1 w-16" |
| | | > |
| | | æä½ |
| | | </th> |
| | | </tr> |
| | | </thead> |
| | | <tbody> |
| | | <tr v-for="(item, index) in items" :key="index"> |
| | | <td class="border border-gray-200 px-2 py-1 text-center"> |
| | | {{ index + 1 }} |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <MdmItemSelect |
| | | v-if="formType !== 'detail'" |
| | | :model-value="item.productId" |
| | | placeholder="è¯·éæ©äº§å" |
| | | @change="handleItemSelect(index, $event)" |
| | | /> |
| | | <span v-else>{{ item.productName }}</span> |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1 text-center"> |
| | | {{ item.productUnitName || '-' }} |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <InputNumber |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="item.count" |
| | | class="!w-full" |
| | | :min="0.01" |
| | | :precision="2" |
| | | /> |
| | | <span v-else>{{ item.count }}</span> |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <DatePicker |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="item.demandTime" |
| | | class="!w-full" |
| | | value-format="YYYY-MM-DD" |
| | | /> |
| | | <span v-else>{{ item.demandTime || '-' }}</span> |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <Input |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="item.remark" |
| | | placeholder="夿³¨" |
| | | /> |
| | | <span v-else>{{ item.remark || '-' }}</span> |
| | | </td> |
| | | <td |
| | | v-if="formType !== 'detail'" |
| | | class="border border-gray-200 px-2 py-1 text-center" |
| | | > |
| | | <Button type="link" danger @click="handleRemoveItem(index)"> |
| | | å é¤ |
| | | </Button> |
| | | </td> |
| | | </tr> |
| | | <tr v-if="!items.length"> |
| | | <td |
| | | :colspan="formType === 'detail' ? 6 : 7" |
| | | class="border border-gray-200 px-2 py-4 text-center text-gray-400" |
| | | > |
| | | ææ æç»ï¼è¯·æ·»å |
| | | </td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | </div> |
| | | </template> |
| | | </Form> |
| | | </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 { submitPurchasePlan } from '#/api/erp/purchase/plan'; |
| | | |
| | | interface ProcessDefinition { |
| | | id: string; |
| | | name: string; |
| | | version: number; |
| | | key: string; |
| | | } |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const planId = 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 submitPurchasePlan(planId.value!, selectedProcessKey.value); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success('æäº¤æå'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | planId.value = undefined; |
| | | selectedProcessKey.value = ''; |
| | | processList.value = []; |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number; processList: ProcessDefinition[] }>(); |
| | | if (!data) { |
| | | return; |
| | | } |
| | | planId.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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { 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 { HrmEmployeeSelect } from '#/views/hrm/employee/components'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType): VbenFormSchema[] { |
| | | const isReadonly = formType === 'detail'; |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'employeeId', |
| | | label: 'åå·¥', |
| | | component: markRaw(HrmEmployeeSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©åå·¥', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'certType', |
| | | label: 'è¯ä¹¦ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.HRM_CERTIFICATE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©è¯ä¹¦ç±»å', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'certName', |
| | | label: 'è¯ä¹¦åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è¯ä¹¦åç§°', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'certNo', |
| | | label: 'è¯ä¹¦ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è¯ä¹¦ç¼å·', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'issueOrg', |
| | | label: 'åè¯æºæ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åè¯æºæ', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'issueDate', |
| | | label: 'åè¯æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'éæ©åè¯æ¥æ', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'expireDate', |
| | | label: 'æææè³', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'éæ©æææè³', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: isReadonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'employeeId', |
| | | label: 'åå·¥', |
| | | component: markRaw(HrmEmployeeSelect), |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©åå·¥', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'certType', |
| | | label: 'è¯ä¹¦ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.HRM_CERTIFICATE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©è¯ä¹¦ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'certName', |
| | | label: 'è¯ä¹¦åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è¯ä¹¦åç§°', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çè¡¨æ ¼å */ |
| | | export function useGridColumns(): VxeTableGridOptions['columns'] { |
| | | return [ |
| | | { type: 'checkbox', width: 50 }, |
| | | { type: 'seq', title: 'åºå·', width: 60 }, |
| | | { |
| | | field: 'employeeName', |
| | | title: 'åå·¥', |
| | | minWidth: 120, |
| | | }, |
| | | { |
| | | field: 'certType', |
| | | title: 'è¯ä¹¦ç±»å', |
| | | width: 120, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.HRM_CERTIFICATE_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'certName', |
| | | title: 'è¯ä¹¦åç§°', |
| | | minWidth: 200, |
| | | }, |
| | | { |
| | | field: 'certNo', |
| | | title: 'è¯ä¹¦ç¼å·', |
| | | minWidth: 130, |
| | | }, |
| | | { |
| | | field: 'issueOrg', |
| | | title: 'åè¯æºæ', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'expireDate', |
| | | title: 'æææè³', |
| | | width: 120, |
| | | formatter: 'formatDate', |
| | | }, |
| | | { |
| | | field: 'expireStatus', |
| | | title: 'å°æç¶æ', |
| | | width: 100, |
| | | slots: { default: 'expireStatus' }, |
| | | }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 200, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { HrmCertificateApi } from '#/api/hrm/certificate'; |
| | | |
| | | 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 { deleteCertificate, getCertificatePage } from '#/api/hrm/certificate'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** HRM è¯ä¹¦èµè´¨å表 */ |
| | | defineOptions({ name: 'HrmCertificate' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: HrmCertificateApi.Certificate) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleDetail(row: HrmCertificateApi.Certificate) { |
| | | 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 deleteCertificate(ids); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: HrmCertificateApi.Certificate[]; |
| | | }) { |
| | | 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 getCertificatePage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<HrmCertificateApi.Certificate>, |
| | | 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: ['hrm:certificate:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:certificate:delete'], |
| | | popConfirm: { |
| | | title: `æ¯å¦å 餿é䏿°æ®ï¼`, |
| | | confirm: handleDelete.bind(null, checkedIds), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #expireStatus="{ row }"> |
| | | <Tag v-if="row.expireStatus === 0" color="success">ææ</Tag> |
| | | <Tag v-else-if="row.expireStatus === 1" color="warning">å³å°å°æ</Tag> |
| | | <Tag v-else-if="row.expireStatus === 2" color="error">å·²è¿æ</Tag> |
| | | <Tag v-else color="default">-</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['hrm:certificate:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['hrm:certificate:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['hrm:certificate:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.certName]), |
| | | confirm: handleDelete.bind(null, [row.id!]), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { HrmCertificateApi } from '#/api/hrm/certificate'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createCertificate, getCertificate, updateCertificate } from '#/api/hrm/certificate'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<HrmCertificateApi.Certificate>(); |
| | | 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 HrmCertificateApi.Certificate; |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createCertificate(data) |
| | | : updateCertificate(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 getCertificate(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 { 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> |
| | |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'tbDeviceId', |
| | | label: 'æ°é设å¤ID', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ°é设å¤IDï¼å¤é¨æ°é tbDeviceIdï¼', |
| | | }, |
| | | help: '䏿°éç³»ç»è®¾å¤ID对åºï¼ç¨äºå
³è设å¤å®æ¶æ°æ®', |
| | | }, |
| | | { |
| | | fieldName: 'brand', |
| | | label: 'åç', |
| | | component: 'Input', |
| | |
| | | import CheckRecordList from "./check-record-list.vue"; |
| | | import MaintenRecordList from "./mainten-record-list.vue"; |
| | | import RepairList from "./repair-list.vue"; |
| | | import TelemetryList from "./telemetry-list.vue"; |
| | | |
| | | const emit = defineEmits(["success"]); |
| | | const formType = ref<FormType>("create"); // è¡¨åæ¨¡å¼ |
| | |
| | | tab="维修记å½"> |
| | | <RepairList :machinery-id="formData.id" /> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="telemetry" |
| | | tab="æ°éæ°æ®" |
| | | :disabled="!formData.tbDeviceId"> |
| | | <TelemetryList :tb-device-id="formData.tbDeviceId" /> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | <template #prepend-footer> |
| | | <div class="flex flex-auto items-center"> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesDvTelemetryApi } from '#/api/mes/dv/telemetry'; |
| | | |
| | | import { watch } from 'vue'; |
| | | |
| | | import { Tag } from 'ant-design-vue'; |
| | | |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { getLatestTelemetry } from '#/api/mes/dv/telemetry'; |
| | | |
| | | import { useLatestColumns } from '../../telemetry/data'; |
| | | |
| | | const props = defineProps<{ tbDeviceId?: string }>(); |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: useLatestColumns(), |
| | | height: 320, |
| | | keepSource: true, |
| | | // /latest è¿å裸æ°ç»ï¼éå
³éå页ï¼å¦å vxe 伿 {list,total} è§£æå¯¼è´ä¸å±ç¤º |
| | | pagerConfig: { enabled: false }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async () => |
| | | await getLatestTelemetry({ tbDeviceId: props.tbDeviceId }), |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true }, |
| | | } as VxeTableGridOptions<MesDvTelemetryApi.Telemetry>, |
| | | }); |
| | | |
| | | watch( |
| | | () => props.tbDeviceId, |
| | | (value) => { |
| | | if (value) { |
| | | gridApi.query(); |
| | | } |
| | | }, |
| | | ); |
| | | </script> |
| | | |
| | | <template> |
| | | <Grid table-title="æ°é宿¶æ°æ®"> |
| | | <template #anomaly="{ row }"> |
| | | <Tag :color="row.whetherAnomaly ? 'red' : 'green'"> |
| | | {{ row.whetherAnomaly ? 'å¼å¸¸' : 'æ£å¸¸' }} |
| | | </Tag> |
| | | </template> |
| | | </Grid> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesDvStatisticsApi } from '#/api/mes/dv/statistics'; |
| | | |
| | | import { onMounted, ref } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | |
| | | import { Card, Col, Row, Table, Tag } from 'ant-design-vue'; |
| | | |
| | | import { getDvStatisticsSummary } from '#/api/mes/dv/statistics'; |
| | | |
| | | defineOptions({ name: 'MesDvStatistics' }); |
| | | |
| | | const loading = ref(false); |
| | | const summary = ref<MesDvStatisticsApi.StatisticsSummary>({}); |
| | | |
| | | async function loadSummary() { |
| | | loading.value = true; |
| | | try { |
| | | summary.value = await getDvStatisticsSummary(); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | onMounted(loadSummary); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page> |
| | | <Row :gutter="16" class="mb-4"> |
| | | <Col :span="4"> |
| | | <Card :loading="loading"> |
| | | <div class="text-sm text-gray-500">è®¾å¤æ»æ°</div> |
| | | <div class="mt-2 text-2xl font-bold"> |
| | | {{ summary.totalMachinery ?? 0 }} |
| | | </div> |
| | | </Card> |
| | | </Col> |
| | | <Col :span="5"> |
| | | <Card :loading="loading"> |
| | | <div class="text-sm text-gray-500">çäº§ä¸ / åæº / ç»´æ¤ä¸</div> |
| | | <div class="mt-2 text-2xl font-bold"> |
| | | <span class="text-green-600">{{ summary.producingCount ?? 0 }}</span> |
| | | <span class="mx-1 text-gray-400">/</span> |
| | | <span class="text-red-600">{{ summary.stoppedCount ?? 0 }}</span> |
| | | <span class="mx-1 text-gray-400">/</span> |
| | | <span class="text-orange-600">{{ summary.maintainingCount ?? 0 }}</span> |
| | | </div> |
| | | </Card> |
| | | </Col> |
| | | <Col :span="5"> |
| | | <Card :loading="loading"> |
| | | <div class="text-sm text-gray-500">è®¾å¤æ
éç</div> |
| | | <div class="mt-2 text-2xl font-bold text-red-600"> |
| | | {{ (summary.faultRate ?? 0).toFixed(1) }}% |
| | | </div> |
| | | </Card> |
| | | </Col> |
| | | <Col :span="5"> |
| | | <Card :loading="loading"> |
| | | <div class="text-sm text-gray-500">ç»´ä¿®åï¼æ»æ° / å¾
å¤çï¼</div> |
| | | <div class="mt-2 text-2xl font-bold"> |
| | | {{ summary.totalRepair ?? 0 }} |
| | | <span class="text-sm text-gray-400"> |
| | | / {{ summary.processingRepair ?? 0 }} |
| | | </span> |
| | | </div> |
| | | </Card> |
| | | </Col> |
| | | <Col :span="5"> |
| | | <Card :loading="loading"> |
| | | <div class="text-sm text-gray-500">ä¿å
»è®°å½ï¼æ»æ° / 已宿ï¼</div> |
| | | <div class="mt-2 text-2xl font-bold"> |
| | | {{ summary.totalMainten ?? 0 }} |
| | | <span class="text-sm text-gray-400"> |
| | | / {{ summary.finishedMainten ?? 0 }} |
| | | </span> |
| | | </div> |
| | | </Card> |
| | | </Col> |
| | | </Row> |
| | | |
| | | <Card title="æè®¾å¤ç±»ååå¸" :loading="loading"> |
| | | <Table |
| | | row-key="machineryTypeId" |
| | | :data-source="summary.typeDistribution ?? []" |
| | | :pagination="false" |
| | | size="middle" |
| | | > |
| | | <Table.Column title="设å¤ç±»å" data-index="machineryTypeName"> |
| | | <template #default="{ record }"> |
| | | <Tag color="blue">{{ record.machineryTypeName || 'æªåç±»' }}</Tag> |
| | | </template> |
| | | </Table.Column> |
| | | <Table.Column title="è®¾å¤æ°é" data-index="count" align="right" /> |
| | | <Table.Column title="å æ¯" align="right"> |
| | | <template #default="{ record }"> |
| | | {{ |
| | | summary.totalMachinery |
| | | ? ((record.count / summary.totalMachinery) * 100).toFixed(1) |
| | | : 0 |
| | | }}% |
| | | </template> |
| | | </Table.Column> |
| | | </Table> |
| | | </Card> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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 { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesDvTelemetryApi } from '#/api/mes/dv/telemetry'; |
| | | |
| | | /** æ¯å¦å¼å¸¸é项ï¼å¸å°å¼ï¼åç«¯ç´æ¥æ¥æ¶ï¼ */ |
| | | export const ANOMALY_OPTIONS = [ |
| | | { label: 'æ£å¸¸', value: false }, |
| | | { label: 'å¼å¸¸', value: true }, |
| | | ]; |
| | | |
| | | /** åå²è®°å½æç´¢è¡¨å */ |
| | | export function useHistoryGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'deviceName', |
| | | label: '设å¤åç§°', |
| | | component: 'Input', |
| | | componentProps: { allowClear: true, placeholder: '请è¾å
¥è®¾å¤åç§°' }, |
| | | }, |
| | | { |
| | | fieldName: 'paramName', |
| | | label: 'ä¿¡å·åç§°', |
| | | component: 'Input', |
| | | componentProps: { allowClear: true, placeholder: '请è¾å
¥ä¿¡å·åç§°' }, |
| | | }, |
| | | { |
| | | fieldName: 'whetherAnomaly', |
| | | label: 'æ¯å¦å¼å¸¸', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: ANOMALY_OPTIONS, |
| | | placeholder: 'è¯·éæ©æ¯å¦å¼å¸¸', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'telemetryDataTime', |
| | | label: '饿µæ¶é´', |
| | | component: 'RangePicker', |
| | | componentProps: { class: '!w-full', valueFormat: 'YYYY-MM-DD HH:mm:ss' }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** 宿¶æ°æ®å段 */ |
| | | export function useLatestColumns(): VxeTableGridOptions<MesDvTelemetryApi.Telemetry>['columns'] { |
| | | return [ |
| | | { field: 'deviceName', title: '设å¤åç§°', minWidth: 140 }, |
| | | { field: 'paramName', title: 'ä¿¡å·åç§°', minWidth: 140 }, |
| | | { field: 'timelyValue', title: 'ä¿¡å·å¼(宿¶)', width: 120 }, |
| | | { |
| | | field: 'whetherAnomaly', |
| | | title: 'æ¯å¦å¼å¸¸', |
| | | width: 100, |
| | | slots: { default: 'anomaly' }, |
| | | }, |
| | | { field: 'shiftName', title: 'çæ¬¡', width: 100 }, |
| | | { |
| | | field: 'telemetryDataTime', |
| | | title: '饿µæ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { field: 'standardValue', title: 'æ åå¼', width: 110 }, |
| | | { field: 'avgValue', title: 'åå¼', width: 110 }, |
| | | { field: 'maxValue', title: 'æå¤§å¼', width: 110 }, |
| | | { field: 'minValue', title: 'æå°å¼', width: 110 }, |
| | | ]; |
| | | } |
| | | |
| | | /** åå²è®°å½å段 */ |
| | | export function useHistoryColumns(): VxeTableGridOptions<MesDvTelemetryApi.Telemetry>['columns'] { |
| | | return [ |
| | | { field: 'deviceName', title: '设å¤åç§°', minWidth: 140 }, |
| | | { field: 'paramName', title: 'ä¿¡å·åç§°', minWidth: 140 }, |
| | | { field: 'timelyValue', title: 'ä¿¡å·å¼', width: 110 }, |
| | | { |
| | | field: 'whetherAnomaly', |
| | | title: 'æ¯å¦å¼å¸¸', |
| | | width: 100, |
| | | slots: { default: 'anomaly' }, |
| | | }, |
| | | { field: 'shiftName', title: 'çæ¬¡', width: 100 }, |
| | | { field: 'billNo', title: 'åæ®å·', minWidth: 120 }, |
| | | { |
| | | field: 'telemetryDataTime', |
| | | title: '饿µæ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'pullTime', |
| | | title: 'æåæ¶é´', |
| | | width: 180, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesDvTelemetryApi } from '#/api/mes/dv/telemetry'; |
| | | |
| | | import { onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | import { useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | |
| | | import { Button, message, Switch, Tabs, Tag } from 'ant-design-vue'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | import { |
| | | getLatestTelemetry, |
| | | getTelemetryPage, |
| | | pullTelemetry, |
| | | } from '#/api/mes/dv/telemetry'; |
| | | |
| | | import { useHistoryColumns, useHistoryGridFormSchema, useLatestColumns } from './data'; |
| | | |
| | | const activeTab = ref<'latest' | 'history'>('latest'); |
| | | const autoRefresh = ref(false); |
| | | const pulling = ref(false); |
| | | 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: 400, |
| | | keepSource: true, |
| | | // /latest è¿å裸æ°ç»ï¼éå
³éå页ï¼å¦å vxe 伿 {list,total} è§£æå¯¼è´ä¸å±ç¤º |
| | | pagerConfig: { enabled: false }, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async () => await getLatestTelemetry(), |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true }, |
| | | } as VxeTableGridOptions<MesDvTelemetryApi.Telemetry>, |
| | | }); |
| | | |
| | | const [HistoryGrid, historyGridApi] = useVbenVxeGrid({ |
| | | formOptions: { schema: useHistoryGridFormSchema() }, |
| | | gridOptions: { |
| | | columns: useHistoryColumns(), |
| | | height: 400, |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getTelemetryPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { keyField: 'id', isHover: true }, |
| | | toolbarConfig: { refresh: true, search: true }, |
| | | } as VxeTableGridOptions<MesDvTelemetryApi.Telemetry>, |
| | | }); |
| | | |
| | | /** æåææ°æ°éæ°æ®å¹¶å·æ°å½å Tab */ |
| | | async function handlePull() { |
| | | pulling.value = true; |
| | | try { |
| | | const result = await pullTelemetry(); |
| | | lastUpdateTime.value = result.pullTime; |
| | | message.success( |
| | | `æåæåï¼æ°å¢ ${result.recordCount} æ¡ï¼æ¶åè®¾å¤ ${result.deviceCount} å°`, |
| | | ); |
| | | refreshActive(); |
| | | } finally { |
| | | pulling.value = false; |
| | | } |
| | | } |
| | | |
| | | /** å·æ°å½å Tab æ°æ® */ |
| | | function refreshActive() { |
| | | if (activeTab.value === 'latest') { |
| | | latestGridApi.query(); |
| | | } else { |
| | | historyGridApi.query(); |
| | | } |
| | | } |
| | | |
| | | /** 忢宿¶èªå¨å·æ°ï¼æ¯ 30 ç§ï¼ */ |
| | | function toggleAutoRefresh(checked: boolean | string | number) { |
| | | if (checked) { |
| | | refreshTimer = setInterval(() => { |
| | | if (activeTab.value === 'latest') { |
| | | latestGridApi.query(); |
| | | } |
| | | }, 30_000); |
| | | } else if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | refreshTimer = undefined; |
| | | } |
| | | } |
| | | |
| | | onBeforeUnmount(() => { |
| | | resizeObserver?.disconnect(); |
| | | if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <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"> |
| | | æåææ°æ°æ® |
| | | </Button> |
| | | <Switch |
| | | v-model:checked="autoRefresh" |
| | | checked-children="宿¶èªå¨å·æ°" |
| | | un-checked-children="宿¶èªå¨å·æ°" |
| | | @change="toggleAutoRefresh" |
| | | /> |
| | | <span v-if="lastUpdateTime" class="text-sm text-gray-500"> |
| | | æè¿æåï¼{{ dayjs(lastUpdateTime).format('YYYY-MM-DD HH:mm:ss') }} |
| | | </span> |
| | | </div> |
| | | <Tabs v-model:active-key="activeTab" class="min-h-0 flex-1"> |
| | | <Tabs.TabPane key="latest" tab="宿¶æ°æ®"> |
| | | <LatestGrid table-title="宿¶æ°æ®"> |
| | | <template #anomaly="{ row }"> |
| | | <Tag :color="row.whetherAnomaly ? 'red' : 'green'"> |
| | | {{ row.whetherAnomaly ? 'å¼å¸¸' : 'æ£å¸¸' }} |
| | | </Tag> |
| | | </template> |
| | | </LatestGrid> |
| | | </Tabs.TabPane> |
| | | <Tabs.TabPane key="history" tab="åå²è®°å½"> |
| | | <HistoryGrid table-title="åå²è®°å½"> |
| | | <template #anomaly="{ row }"> |
| | | <Tag :color="row.whetherAnomaly ? 'red' : 'green'"> |
| | | {{ row.whetherAnomaly ? 'å¼å¸¸' : 'æ£å¸¸' }} |
| | | </Tag> |
| | | </template> |
| | | </HistoryGrid> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | </div> |
| | | </Page> |
| | | </template> |
| | |
| | | rules: z.number().default(0), |
| | | }, |
| | | { |
| | | fieldName: 'materialCategory', |
| | | label: 'ç©èµåç±»', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_MATERIAL_CATEGORY, 'number'), |
| | | placeholder: 'è¯·éæ©ç©èµåç±»', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | |
| | | }, |
| | | }, |
| | | { |
| | | field: 'materialCategory', |
| | | title: 'ç©èµåç±»', |
| | | width: 120, |
| | | align: 'center', |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_MATERIAL_CATEGORY }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'safeStockFlag', |
| | | title: 'å®å
¨åºå', |
| | | width: 120, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesPdBaseDataApi } from '#/api/mes/pd/basedata'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { |
| | | createDiscountPolicy, |
| | | createElectricityType, |
| | | createPriceStandard, |
| | | createServicePackage, |
| | | createTieredPrice, |
| | | createVoltageLevel, |
| | | deleteDiscountPolicy, |
| | | deleteElectricityType, |
| | | deletePriceStandard, |
| | | deleteServicePackage, |
| | | deleteTieredPrice, |
| | | deleteVoltageLevel, |
| | | exportDiscountPolicy, |
| | | exportElectricityType, |
| | | exportPriceStandard, |
| | | exportServicePackage, |
| | | exportTieredPrice, |
| | | exportVoltageLevel, |
| | | getDiscountPolicy, |
| | | getDiscountPolicyPage, |
| | | getElectricityType, |
| | | getElectricityTypeList, |
| | | getElectricityTypePage, |
| | | getPriceStandard, |
| | | getPriceStandardPage, |
| | | getServicePackage, |
| | | getServicePackageList, |
| | | getServicePackagePage, |
| | | getTieredPrice, |
| | | getTieredPricePage, |
| | | getVoltageLevel, |
| | | getVoltageLevelList, |
| | | getVoltageLevelPage, |
| | | updateDiscountPolicy, |
| | | updateElectricityType, |
| | | updatePriceStandard, |
| | | updateServicePackage, |
| | | updateTieredPrice, |
| | | updateVoltageLevel, |
| | | } from '#/api/mes/pd/basedata'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** å®ä½ API æ å°ï¼æ³å CRUDï¼ */ |
| | | export interface EntityApi { |
| | | page: (params: any) => Promise<any>; |
| | | get: (id: number) => Promise<any>; |
| | | create: (data: any) => Promise<any>; |
| | | update: (data: any) => Promise<any>; |
| | | remove: (id: number) => Promise<any>; |
| | | exportExcel: (params: any) => Promise<any>; |
| | | } |
| | | |
| | | /** åºç¡æ°æ® Tab é
ç½® */ |
| | | export interface BaseDataEntity { |
| | | key: string; |
| | | title: string; |
| | | api: EntityApi; |
| | | columns: VxeTableGridOptions<any>['columns']; |
| | | gridFormSchema: VbenFormSchema[]; |
| | | formSchema: VbenFormSchema[]; |
| | | } |
| | | |
| | | /** å è½½å®ä»·å¯¹è±¡ä¸æï¼1æå¡å¥é¤/2çµåç级/3ç¨çµç±»åï¼ */ |
| | | export async function loadObjectOptions( |
| | | type?: number | null, |
| | | ): Promise<{ label: string; value: number }[]> { |
| | | if (type === 1) { |
| | | const list = await getServicePackageList(); |
| | | return list.map((i) => ({ label: i.name ?? '', value: i.id ?? 0 })); |
| | | } |
| | | if (type === 2) { |
| | | const list = await getVoltageLevelList(); |
| | | return list.map((i) => ({ label: i.name ?? '', value: i.id ?? 0 })); |
| | | } |
| | | if (type === 3) { |
| | | const list = await getElectricityTypeList(); |
| | | return list.map((i) => ({ label: i.name ?? '', value: i.id ?? 0 })); |
| | | } |
| | | return []; |
| | | } |
| | | |
| | | /** 对象类å䏿ï¼1æå¡å¥é¤/2çµåç级/3ç¨çµç±»åï¼ */ |
| | | function useObjectTypeOptions() { |
| | | return getDictOptions(DICT_TYPE.MES_PD_PRICE_OBJECT_TYPE, 'number'); |
| | | } |
| | | |
| | | /** éç¨å¯ç¨ç¶æå */ |
| | | function useStatusColumn(title = 'å¯ç¨ç¶æ') { |
| | | return { |
| | | field: 'status', |
| | | title, |
| | | width: 90, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PD_PRODUCT_STATUS }, |
| | | }, |
| | | } as const; |
| | | } |
| | | |
| | | /** ç¨çµç±»å */ |
| | | export const ElectricityTypeConfig: BaseDataEntity = { |
| | | key: 'electricityType', |
| | | title: 'ç¨çµç±»å', |
| | | api: { |
| | | page: getElectricityTypePage, |
| | | get: getElectricityType, |
| | | create: createElectricityType, |
| | | update: updateElectricityType, |
| | | remove: deleteElectricityType, |
| | | exportExcel: exportElectricityType, |
| | | }, |
| | | columns: [ |
| | | { field: 'code', title: 'ç±»åç¼ç ', width: 140 }, |
| | | { field: 'name', title: 'ç±»ååç§°', minWidth: 160 }, |
| | | { field: 'description', title: 'ç±»å说æ', minWidth: 200 }, |
| | | { field: 'sort', title: 'æåº', width: 80 }, |
| | | useStatusColumn(), |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { title: 'æä½', width: 200, fixed: 'right', slots: { default: 'actions' } }, |
| | | ], |
| | | gridFormSchema: [ |
| | | { |
| | | fieldName: 'name', |
| | | label: 'ç±»ååç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥ç±»ååç§°' }, |
| | | }, |
| | | ], |
| | | formSchema: [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'ç±»åç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥ç±»åç¼ç ' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'ç±»ååç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥ç±»ååç§°' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'description', |
| | | label: 'ç±»å说æ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { placeholder: '请è¾å
¥ç±»å说æ', rows: 2 }, |
| | | }, |
| | | { |
| | | fieldName: 'sort', |
| | | label: 'æåº', |
| | | component: 'InputNumber', |
| | | componentProps: { class: '!w-full', min: 0, precision: 0 }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'å¯ç¨ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_PRODUCT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å¯ç¨ç¶æ', |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | /** çµåç级 */ |
| | | export const VoltageLevelConfig: BaseDataEntity = { |
| | | key: 'voltageLevel', |
| | | title: 'çµåç级', |
| | | api: { |
| | | page: getVoltageLevelPage, |
| | | get: getVoltageLevel, |
| | | create: createVoltageLevel, |
| | | update: updateVoltageLevel, |
| | | remove: deleteVoltageLevel, |
| | | exportExcel: exportVoltageLevel, |
| | | }, |
| | | columns: [ |
| | | { field: 'code', title: 'ç级ç¼ç ', width: 140 }, |
| | | { field: 'name', title: 'ç级åç§°', minWidth: 160 }, |
| | | { field: 'voltageValue', title: 'çµåæ°å¼(kV)', width: 120 }, |
| | | { field: 'sort', title: 'æåº', width: 80 }, |
| | | useStatusColumn(), |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { title: 'æä½', width: 200, fixed: 'right', slots: { default: 'actions' } }, |
| | | ], |
| | | gridFormSchema: [ |
| | | { |
| | | fieldName: 'name', |
| | | label: 'ç级åç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥ç级åç§°' }, |
| | | }, |
| | | ], |
| | | formSchema: [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'ç级ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥ç级ç¼ç ' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'ç级åç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥ç级åç§°' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'voltageValue', |
| | | label: 'çµåæ°å¼(kV)', |
| | | component: 'InputNumber', |
| | | componentProps: { class: '!w-full', min: 0, precision: 2 }, |
| | | }, |
| | | { |
| | | fieldName: 'sort', |
| | | label: 'æåº', |
| | | component: 'InputNumber', |
| | | componentProps: { class: '!w-full', min: 0, precision: 0 }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'å¯ç¨ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_PRODUCT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å¯ç¨ç¶æ', |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | /** æå¡å¥é¤ */ |
| | | export const ServicePackageConfig: BaseDataEntity = { |
| | | key: 'servicePackage', |
| | | title: 'æå¡å¥é¤', |
| | | api: { |
| | | page: getServicePackagePage, |
| | | get: getServicePackage, |
| | | create: createServicePackage, |
| | | update: updateServicePackage, |
| | | remove: deleteServicePackage, |
| | | exportExcel: exportServicePackage, |
| | | }, |
| | | columns: [ |
| | | { field: 'code', title: 'å¥é¤ç¼ç ', width: 150 }, |
| | | { field: 'name', title: 'å¥é¤åç§°', minWidth: 160 }, |
| | | { |
| | | field: 'price', |
| | | title: 'å¥é¤ä»·æ ¼', |
| | | width: 110, |
| | | formatter: 'formatNumber', |
| | | }, |
| | | { |
| | | field: 'effectiveDate', |
| | | title: 'çææ¥æ', |
| | | width: 160, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'expireDate', |
| | | title: 'å¤±ææ¥æ', |
| | | width: 160, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | useStatusColumn(), |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { title: 'æä½', width: 200, fixed: 'right', slots: { default: 'actions' } }, |
| | | ], |
| | | gridFormSchema: [ |
| | | { |
| | | fieldName: 'name', |
| | | label: 'å¥é¤åç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥å¥é¤åç§°' }, |
| | | }, |
| | | ], |
| | | formSchema: [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: 'å¥é¤ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { placeholder: 'ä¿åæ¶èªå¨çæ', disabled: true }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'å¥é¤åç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥å¥é¤åç§°' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'description', |
| | | label: 'å¥é¤è¯´æ', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { placeholder: '请è¾å
¥å¥é¤è¯´æ', rows: 2 }, |
| | | }, |
| | | { |
| | | fieldName: 'price', |
| | | label: 'å¥é¤ä»·æ ¼', |
| | | component: 'InputNumber', |
| | | componentProps: { class: '!w-full', min: 0, precision: 2 }, |
| | | }, |
| | | { |
| | | fieldName: 'effectiveDate', |
| | | label: 'çææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { class: '!w-full', valueFormat: 'YYYY-MM-DD HH:mm:ss' }, |
| | | }, |
| | | { |
| | | fieldName: 'expireDate', |
| | | label: 'å¤±ææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { class: '!w-full', valueFormat: 'YYYY-MM-DD HH:mm:ss' }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'å¯ç¨ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_PRODUCT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å¯ç¨ç¶æ', |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | /** å®ä»·æ å */ |
| | | export const PriceStandardConfig: BaseDataEntity = { |
| | | key: 'priceStandard', |
| | | title: 'å®ä»·æ å', |
| | | api: { |
| | | page: getPriceStandardPage, |
| | | get: getPriceStandard, |
| | | create: createPriceStandard, |
| | | update: updatePriceStandard, |
| | | remove: deletePriceStandard, |
| | | exportExcel: exportPriceStandard, |
| | | }, |
| | | columns: [ |
| | | { |
| | | field: 'objectType', |
| | | title: 'å®ä»·å¯¹è±¡ç±»å', |
| | | width: 120, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PD_PRICE_OBJECT_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'objectName', title: 'å®ä»·å¯¹è±¡', minWidth: 180 }, |
| | | { |
| | | field: 'price', |
| | | title: 'ä»·æ ¼', |
| | | width: 110, |
| | | formatter: 'formatNumber', |
| | | }, |
| | | { field: 'unit', title: '计价åä½', width: 100 }, |
| | | { |
| | | field: 'effectiveDate', |
| | | title: 'çææ¥æ', |
| | | width: 160, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | useStatusColumn(), |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { title: 'æä½', width: 200, fixed: 'right', slots: { default: 'actions' } }, |
| | | ], |
| | | gridFormSchema: [ |
| | | { |
| | | fieldName: 'objectType', |
| | | label: 'å®ä»·å¯¹è±¡ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: useObjectTypeOptions(), |
| | | placeholder: 'è¯·éæ©å®ä»·å¯¹è±¡ç±»å', |
| | | }, |
| | | }, |
| | | ], |
| | | formSchema: [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'objectType', |
| | | label: 'å®ä»·å¯¹è±¡ç±»å', |
| | | component: 'Select', |
| | | rules: 'selectRequired', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: useObjectTypeOptions(), |
| | | placeholder: 'è¯·éæ©å®ä»·å¯¹è±¡ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'objectId', |
| | | label: 'å®ä»·å¯¹è±¡', |
| | | component: 'Select', |
| | | rules: 'selectRequired', |
| | | dependencies: { |
| | | triggerFields: ['objectType'], |
| | | async componentProps(values, form) { |
| | | const options = await loadObjectOptions(values.objectType); |
| | | return { |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©å®ä»·å¯¹è±¡', |
| | | options, |
| | | onChange: (value: number) => { |
| | | const opt = options.find((o) => o.value === value); |
| | | form.setFieldValue('objectName', opt?.label ?? ''); |
| | | }, |
| | | }; |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'objectName', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'price', |
| | | label: 'ä»·æ ¼', |
| | | component: 'InputNumber', |
| | | componentProps: { class: '!w-full', min: 0, precision: 2 }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'unit', |
| | | label: '计价åä½', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥è®¡ä»·åä½' }, |
| | | }, |
| | | { |
| | | fieldName: 'effectiveDate', |
| | | label: 'çææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { class: '!w-full', valueFormat: 'YYYY-MM-DD HH:mm:ss' }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'å¯ç¨ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_PRODUCT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å¯ç¨ç¶æ', |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | /** 伿 çç¥ */ |
| | | export const DiscountPolicyConfig: BaseDataEntity = { |
| | | key: 'discountPolicy', |
| | | title: '伿 çç¥', |
| | | api: { |
| | | page: getDiscountPolicyPage, |
| | | get: getDiscountPolicy, |
| | | create: createDiscountPolicy, |
| | | update: updateDiscountPolicy, |
| | | remove: deleteDiscountPolicy, |
| | | exportExcel: exportDiscountPolicy, |
| | | }, |
| | | columns: [ |
| | | { field: 'name', title: 'çç¥åç§°', minWidth: 160 }, |
| | | { |
| | | field: 'policyType', |
| | | title: 'çç¥ç±»å', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PD_DISCOUNT_POLICY_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'policyValue', |
| | | title: 'çç¥å¼', |
| | | width: 110, |
| | | formatter: 'formatNumber', |
| | | }, |
| | | { |
| | | field: 'targetType', |
| | | title: 'éç¨å¯¹è±¡ç±»å', |
| | | width: 120, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PD_PRICE_OBJECT_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'startDate', |
| | | title: 'çææ¥æ', |
| | | width: 160, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'endDate', |
| | | title: 'å¤±ææ¥æ', |
| | | width: 160, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | useStatusColumn(), |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { title: 'æä½', width: 200, fixed: 'right', slots: { default: 'actions' } }, |
| | | ], |
| | | gridFormSchema: [ |
| | | { |
| | | fieldName: 'name', |
| | | label: 'çç¥åç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥çç¥åç§°' }, |
| | | }, |
| | | { |
| | | fieldName: 'policyType', |
| | | label: 'çç¥ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_DISCOUNT_POLICY_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©çç¥ç±»å', |
| | | }, |
| | | }, |
| | | ], |
| | | formSchema: [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'çç¥åç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥çç¥åç§°' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'policyType', |
| | | label: 'çç¥ç±»å', |
| | | component: 'Select', |
| | | rules: 'selectRequired', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_DISCOUNT_POLICY_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©çç¥ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'policyValue', |
| | | label: 'çç¥å¼', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: 'ææ£å¡«0-1æ¯ä¾ï¼ç«åå¡«éé¢', |
| | | }, |
| | | help: 'ææ£ï¼0-1 ä¹é´çæ¯ä¾ï¼ç«åï¼æéé¢åå
', |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'targetType', |
| | | label: 'éç¨å¯¹è±¡ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: useObjectTypeOptions(), |
| | | placeholder: 'ä¸éåå
¨å±éç¨', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'targetId', |
| | | label: 'éç¨å¯¹è±¡', |
| | | component: 'Select', |
| | | dependencies: { |
| | | triggerFields: ['targetType'], |
| | | disabled: (values) => !values.targetType, |
| | | async componentProps(values) { |
| | | const options = await loadObjectOptions(values.targetType); |
| | | return { |
| | | allowClear: true, |
| | | placeholder: values.targetType ? 'è¯·éæ©éç¨å¯¹è±¡' : 'å
¨å±éç¨', |
| | | options, |
| | | }; |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'startDate', |
| | | label: 'çææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { class: '!w-full', valueFormat: 'YYYY-MM-DD HH:mm:ss' }, |
| | | }, |
| | | { |
| | | fieldName: 'endDate', |
| | | label: 'å¤±ææ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { class: '!w-full', valueFormat: 'YYYY-MM-DD HH:mm:ss' }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'å¯ç¨ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_PRODUCT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å¯ç¨ç¶æ', |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | /** é¶æ¢¯çµä»· */ |
| | | export const TieredPriceConfig: BaseDataEntity = { |
| | | key: 'tieredPrice', |
| | | title: 'é¶æ¢¯çµä»·', |
| | | api: { |
| | | page: getTieredPricePage, |
| | | get: getTieredPrice, |
| | | create: createTieredPrice, |
| | | update: updateTieredPrice, |
| | | remove: deleteTieredPrice, |
| | | exportExcel: exportTieredPrice, |
| | | }, |
| | | columns: [ |
| | | { field: 'packageId', title: 'æå¡å¥é¤ID', width: 110 }, |
| | | { field: 'voltageLevelId', title: 'çµåç级ID', width: 110 }, |
| | | { field: 'startValue', title: 'ä¸é(å«,kWh)', width: 120 }, |
| | | { field: 'endValue', title: 'ä¸é(ä¸å«,kWh)', width: 130 }, |
| | | { |
| | | field: 'pricePerUnit', |
| | | title: 'æ¡£ä½åä»·', |
| | | width: 110, |
| | | formatter: 'formatNumber', |
| | | }, |
| | | useStatusColumn(), |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { title: 'æä½', width: 200, fixed: 'right', slots: { default: 'actions' } }, |
| | | ], |
| | | gridFormSchema: [ |
| | | { |
| | | fieldName: 'packageId', |
| | | label: 'æå¡å¥é¤ID', |
| | | component: 'Input', |
| | | componentProps: { placeholder: '请è¾å
¥æå¡å¥é¤ID' }, |
| | | }, |
| | | ], |
| | | formSchema: [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'packageId', |
| | | label: 'æå¡å¥é¤', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'éæ©æå¡å¥é¤ï¼ä¸çµåç级äºéä¸ï¼', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['voltageLevelId'], |
| | | disabled: (values) => !!values.voltageLevelId, |
| | | async componentProps(values) { |
| | | const list = await getServicePackageList(); |
| | | return { |
| | | allowClear: true, |
| | | options: list.map((i) => ({ label: i.name, value: i.id })), |
| | | placeholder: 'éæ©æå¡å¥é¤ï¼ä¸çµåç级äºéä¸ï¼', |
| | | }; |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'voltageLevelId', |
| | | label: 'çµåç级', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'éæ©çµåç级ï¼ä¸æå¡å¥é¤äºéä¸ï¼', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['packageId'], |
| | | disabled: (values) => !!values.packageId, |
| | | async componentProps() { |
| | | const list = await getVoltageLevelList(); |
| | | return { |
| | | allowClear: true, |
| | | options: list.map((i) => ({ label: i.name, value: i.id })), |
| | | placeholder: 'éæ©çµåç级ï¼ä¸æå¡å¥é¤äºéä¸ï¼', |
| | | }; |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'startValue', |
| | | label: 'æ¡£ä½ä¸é(å«,kWh)', |
| | | component: 'InputNumber', |
| | | componentProps: { class: '!w-full', min: 0, precision: 2 }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'endValue', |
| | | label: 'æ¡£ä½ä¸é(ä¸å«,kWh)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '为空表示ä¸ä¸å°é¡¶', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'pricePerUnit', |
| | | label: 'æ¡£ä½åä»·', |
| | | component: 'InputNumber', |
| | | componentProps: { class: '!w-full', min: 0, precision: 4 }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'å¯ç¨ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_TIERED_PRICE_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å¯ç¨ç¶æ', |
| | | }, |
| | | }, |
| | | ], |
| | | }; |
| | | |
| | | /** å
¨é¨ Tab é
ç½® */ |
| | | export const BaseDataConfigs: BaseDataEntity[] = [ |
| | | ElectricityTypeConfig, |
| | | VoltageLevelConfig, |
| | | ServicePackageConfig, |
| | | PriceStandardConfig, |
| | | DiscountPolicyConfig, |
| | | TieredPriceConfig, |
| | | ]; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | |
| | | import { Tabs } from 'ant-design-vue'; |
| | | |
| | | import { BaseDataConfigs } from './data'; |
| | | import BaseTable from './modules/base-table.vue'; |
| | | |
| | | const activeKey = ref('electricityType'); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <div class="p-4"> |
| | | <Tabs v-model:activeKey="activeKey"> |
| | | <Tabs.TabPane |
| | | v-for="cfg in BaseDataConfigs" |
| | | :key="cfg.key" |
| | | :tab="cfg.title" |
| | | > |
| | | <BaseTable :key="cfg.key" :config="cfg" /> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | </div> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { BaseDataEntity } from '../data'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | |
| | | import Form from './form.vue'; |
| | | import PriceCalc from './price-calc.vue'; |
| | | |
| | | const props = defineProps<{ config: BaseDataEntity }>(); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | const [PriceCalcModal, priceCalcModalApi] = useVbenModal({ |
| | | connectedComponent: PriceCalc, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ°å¢ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ config: props.config, formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥ç */ |
| | | function handleDetail(row: any) { |
| | | formModalApi |
| | | .setData({ config: props.config, formType: 'detail', id: row.id }) |
| | | .open(); |
| | | } |
| | | |
| | | /** ç¼è¾ */ |
| | | function handleEdit(row: any) { |
| | | formModalApi |
| | | .setData({ config: props.config, formType: 'update', id: row.id }) |
| | | .open(); |
| | | } |
| | | |
| | | /** å é¤ */ |
| | | async function handleDelete(row: any) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.name ?? row.code ?? '']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await props.config.api.remove(row.id); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.name ?? row.code ?? ''])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** å¯¼åº */ |
| | | async function handleExport() { |
| | | const data = await props.config.api.exportExcel( |
| | | await gridApi.formApi.getValues(), |
| | | ); |
| | | downloadFileFromBlobPart({ fileName: `${props.config.title}.xls`, source: data }); |
| | | } |
| | | |
| | | /** é¶æ¢¯çµä»·æµç® */ |
| | | function handleCalc() { |
| | | priceCalcModalApi.open(); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: props.config.gridFormSchema, |
| | | }, |
| | | gridOptions: { |
| | | columns: props.config.columns, |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await props.config.api.page({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<any>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <FormModal @success="handleRefresh" /> |
| | | <PriceCalcModal v-if="config.key === 'tieredPrice'" /> |
| | | <Grid :table-title="`${config.title}å表`"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', [config.title]), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['mes:pd-basedata:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'é¶æ¢¯çµä»·æµç®', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.CALC, |
| | | auth: ['mes:pd-basedata:query'], |
| | | ifShow: config.key === 'tieredPrice', |
| | | onClick: handleCalc, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['mes:pd-basedata:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pd-basedata:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pd-basedata:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [ |
| | | row.name ?? row.code ?? '', |
| | | ]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:pd-basedata:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { BaseDataEntity, FormType } from '../data'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const config = ref<BaseDataEntity>(); |
| | | const formType = ref<FormType>('create'); // è¡¨åæ¨¡å¼ |
| | | const formData = ref<any>(); |
| | | const isDetail = computed(() => formType.value === 'detail'); // æ¯å¦æ¥çæ¨¡å¼ |
| | | const getTitle = computed(() => { |
| | | const title = config.value?.title ?? ''; |
| | | if (formType.value === 'detail') { |
| | | return `æ¥ç${title}`; |
| | | } |
| | | return formType.value === 'update' ? `ä¿®æ¹${title}` : `æ°å¢${title}`; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 130, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = await formApi.getValues(); |
| | | try { |
| | | if (data.id) { |
| | | await config.value!.api.update(data); |
| | | } else { |
| | | await config.value!.api.create(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<{ |
| | | config: BaseDataEntity; |
| | | formType: FormType; |
| | | id?: number; |
| | | }>(); |
| | | config.value = data.config; |
| | | formType.value = data.formType; |
| | | formApi.setState({ schema: data.config.formSchema }); |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | modalApi.setState({ showConfirmButton: formType.value !== 'detail' }); |
| | | if (!data.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await data.config.api.get(data.id); |
| | | // è®¾ç½®å° values |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { MesPdPriceApi } from '#/api/mes/pd/price'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { Descriptions, Empty, Table } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { calcPrice } from '#/api/mes/pd/price'; |
| | | import { getServicePackageList, getVoltageLevelList } from '#/api/mes/pd/basedata'; |
| | | |
| | | const result = ref<MesPdPriceApi.PriceCalcResp>(); |
| | | const hasResult = ref(false); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 130, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | /** æµç®è¡¨ååæ®µ */ |
| | | function useCalcSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'calcType', |
| | | label: 'æµç®ç±»å', |
| | | component: 'Select', |
| | | rules: 'selectRequired', |
| | | componentProps: { |
| | | options: [ |
| | | { label: 'æå¡å¥é¤', value: 1 }, |
| | | { label: 'çµåç级', value: 2 }, |
| | | ], |
| | | placeholder: 'è¯·éæ©æµç®ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'packageId', |
| | | label: 'æå¡å¥é¤', |
| | | component: 'Select', |
| | | rules: 'selectRequired', |
| | | dependencies: { |
| | | triggerFields: ['calcType'], |
| | | show: (values) => values.calcType === 1, |
| | | async componentProps(values, form) { |
| | | if (values.calcType !== 1) { |
| | | form.setFieldValue('packageId', undefined); |
| | | return { allowClear: true, options: [], placeholder: 'è¯·éæ©æå¡å¥é¤' }; |
| | | } |
| | | const list = await getServicePackageList(); |
| | | return { |
| | | allowClear: true, |
| | | options: list.map((i) => ({ label: i.name, value: i.id })), |
| | | placeholder: 'è¯·éæ©æå¡å¥é¤', |
| | | }; |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'voltageLevelId', |
| | | label: 'çµåç级', |
| | | component: 'Select', |
| | | rules: 'selectRequired', |
| | | dependencies: { |
| | | triggerFields: ['calcType'], |
| | | show: (values) => values.calcType === 2, |
| | | async componentProps(values, form) { |
| | | if (values.calcType !== 2) { |
| | | form.setFieldValue('voltageLevelId', undefined); |
| | | return { allowClear: true, options: [], placeholder: 'è¯·éæ©çµåç级' }; |
| | | } |
| | | const list = await getVoltageLevelList(); |
| | | return { |
| | | allowClear: true, |
| | | options: list.map((i) => ({ label: i.name, value: i.id })), |
| | | placeholder: 'è¯·éæ©çµåç级', |
| | | }; |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | | label: 'ç¨çµé(kWh)', |
| | | component: 'InputNumber', |
| | | componentProps: { class: '!w-full', min: 0, precision: 2 }, |
| | | rules: 'required', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | const data = (await formApi.getValues()) as MesPdPriceApi.PriceCalcReq; |
| | | result.value = await calcPrice(data); |
| | | hasResult.value = true; |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | result.value = undefined; |
| | | hasResult.value = false; |
| | | formApi.setState({ schema: useCalcSchema() }); |
| | | formApi.resetForm(); |
| | | modalApi.setState({ confirmText: 'æµç®', showConfirmButton: true }); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="'é¶æ¢¯çµä»·æµç®'" class="w-4/5"> |
| | | <Form class="mx-4 mb-4" /> |
| | | <div v-if="hasResult && result" class="mx-4"> |
| | | <Descriptions |
| | | :column="3" |
| | | bordered |
| | | size="small" |
| | | class="mb-4" |
| | | > |
| | | <Descriptions.Item label="åä»·"> |
| | | {{ result.originalPrice ?? 0 }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="伿 éé¢"> |
| | | {{ result.discountAmount ?? 0 }} |
| | | </Descriptions.Item> |
| | | <Descriptions.Item label="伿 忻价"> |
| | | <span class="text-lg font-bold text-primary"> |
| | | {{ result.finalPrice ?? 0 }} |
| | | </span> |
| | | </Descriptions.Item> |
| | | </Descriptions> |
| | | <Table |
| | | :columns="[ |
| | | { title: 'æ¡£ä½ä¸é(å«,kWh)', dataIndex: 'startValue', key: 'startValue' }, |
| | | { title: 'æ¡£ä½ä¸é(ä¸å«,kWh)', dataIndex: 'endValue', key: 'endValue' }, |
| | | { title: 'æ¬æ¡£çµé(kWh)', dataIndex: 'tierQuantity', key: 'tierQuantity' }, |
| | | { title: 'æ¡£ä½åä»·', dataIndex: 'pricePerUnit', key: 'pricePerUnit' }, |
| | | { title: 'å°è®¡', dataIndex: 'subtotal', key: 'subtotal' }, |
| | | ]" |
| | | :data-source="result.details ?? []" |
| | | :pagination="false" |
| | | size="small" |
| | | class="mb-4" |
| | | /> |
| | | </div> |
| | | <div v-else class="mx-4"> |
| | | <Empty description="è¯·éæ©æµç®ç±»åå¹¶è¾å
¥ç¨çµéåç¹å»ãæµç®ã" /> |
| | | </div> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesPdCtcReelApi } from '#/api/mes/pd/ctc-reel'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { PdCtcSelect } from '#/views/mes/pd/ctc/components'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹çå
·è®¡ç®è®°å½ */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'ctcId', |
| | | label: 'æ¢ä½å¯¼çº¿äº§å', |
| | | component: markRaw(PdCtcSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©æ¢ä½å¯¼çº¿äº§å', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'reelType', |
| | | label: 'çå
፱Ȍ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.PRODUCT_REEL_TYPE, 'string'), |
| | | placeholder: 'è¯·éæ©çå
፱Ȍ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reelOuterDiameter', |
| | | label: 'çå
·å¤å¾(mm)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 0, |
| | | placeholder: '请è¾å
¥çå
·å¤å¾ d1', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reelInnerDiameter', |
| | | label: 'çè¯å
å¾(mm)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 0, |
| | | placeholder: '请è¾å
¥çè¯å
å¾ d2', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reelWidth', |
| | | label: 'ç宽(mm)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 0, |
| | | placeholder: '请è¾å
¥ç宽 l2', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'maxCapacity', |
| | | label: 'æå¤§å®¹é(km)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 4, |
| | | placeholder: '请è¾å
¥æå¤§å®¹é', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'recommendedLength', |
| | | label: 'æ¨èæ¯çé¿åº¦(km)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥æ¨èæ¯çé¿åº¦', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'netWeight', |
| | | label: 'åé(kg)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥åé', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'grossWeight', |
| | | label: 'æ¯é(kg)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥æ¯é', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'calcFormula', |
| | | label: '计ç®å
¬å¼', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '((d1-60)+(d2+10))ÃINT(l2/(宽+1))Ã(d1-d2-70)/(é«+0.2)/1274', |
| | | rows: 2, |
| | | }, |
| | | help: '容é计ç®ï¼((d1-60)+(d2+10))ÃINT(l2/(CTC宽+1))Ã(d1-d2-70)/(CTCé«+0.2)/1274', |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'ctcId', |
| | | label: 'æ¢ä½å¯¼çº¿äº§å', |
| | | component: markRaw(PdCtcSelect), |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©æ¢ä½å¯¼çº¿äº§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reelType', |
| | | label: 'çå
፱Ȍ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.PRODUCT_REEL_TYPE, 'string'), |
| | | placeholder: 'è¯·éæ©çå
፱Ȍ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<MesPdCtcReelApi.CtcReel>['columns'] { |
| | | return [ |
| | | { field: 'ctcOrderSpec', title: 'æ¢ä½å¯¼çº¿è§æ ¼', minWidth: 160 }, |
| | | { |
| | | field: 'reelType', |
| | | title: 'çå
፱Ȍ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.PRODUCT_REEL_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'reelOuterDiameter', title: 'çå
·å¤å¾(mm)', width: 110 }, |
| | | { field: 'reelInnerDiameter', title: 'çè¯å
å¾(mm)', width: 110 }, |
| | | { field: 'reelWidth', title: 'ç宽(mm)', width: 100 }, |
| | | { field: 'maxCapacity', title: 'æå¤§å®¹é(km)', width: 120 }, |
| | | { field: 'recommendedLength', title: 'æ¨èé¿åº¦(km)', width: 120 }, |
| | | { field: 'netWeight', title: 'åé(kg)', width: 100 }, |
| | | { field: 'grossWeight', title: 'æ¯é(kg)', width: 100 }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 220, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesPdCtcReelApi } from '#/api/mes/pd/ctc-reel'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteCtcReel, |
| | | getCtcReelPage, |
| | | } from '#/api/mes/pd/ctc-reel'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ°å¢ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥ç */ |
| | | function handleDetail(row: MesPdCtcReelApi.CtcReel) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ */ |
| | | function handleEdit(row: MesPdCtcReelApi.CtcReel) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å é¤ */ |
| | | async function handleDelete(row: MesPdCtcReelApi.CtcReel) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.ctcOrderSpec ?? '']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteCtcReel(row.id!); |
| | | message.success( |
| | | $t('ui.actionMessage.deleteSuccess', [row.ctcOrderSpec ?? '']), |
| | | ); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getCtcReelPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesPdCtcReelApi.CtcReel>, |
| | | }); |
| | | </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:pd-ctc-reel:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #ctcOrderSpec="{ row }"> |
| | | <Button type="link" @click="handleDetail(row)"> |
| | | {{ row.ctcOrderSpec }} |
| | | </Button> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pd-ctc-reel:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pd-ctc-reel:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [ |
| | | row.ctcOrderSpec ?? '', |
| | | ]), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:pd-ctc-reel:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { MesPdCtcReelApi } from '#/api/mes/pd/ctc-reel'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createCtcReel, |
| | | getCtcReel, |
| | | updateCtcReel, |
| | | } from '#/api/mes/pd/ctc-reel'; |
| | | import { CalcModal } from '#/views/mes/pd/ctc/modules'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); // è¡¨åæ¨¡å¼ |
| | | const formData = ref<MesPdCtcReelApi.CtcReel>(); |
| | | const isDetail = computed(() => formType.value === 'detail'); // æ¯å¦æ¥çæ¨¡å¼ |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return 'æ¥ççå
·è®¡ç®'; |
| | | } |
| | | return formType.value === 'update' ? 'ä¿®æ¹çå
·è®¡ç®' : 'æ°å¢çå
·è®¡ç®'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 130, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [CalcModal, calcModalApi] = useVbenModal({ |
| | | connectedComponent: CalcModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** æå¼å®¹é计ç®å·¥å
· */ |
| | | function handleOpenCalc() { |
| | | calcModalApi.open(); |
| | | } |
| | | |
| | | /** åå¡«æå¤§å®¹éï¼kmï¼ */ |
| | | function handleFillReel(value: { capacityKm: number }) { |
| | | formApi.setFieldValue('maxCapacity', value.capacityKm); |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = (await formApi.getValues()) as MesPdCtcReelApi.CtcReel; |
| | | try { |
| | | await (data.id ? updateCtcReel(data) : createCtcReel(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.setState({ schema: useFormSchema() }); |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | modalApi.setState({ showConfirmButton: formType.value !== 'detail' }); |
| | | if (!data?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getCtcReel(data.id); |
| | | // è®¾ç½®å° values |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-4/5"> |
| | | <Form class="mx-4" /> |
| | | <template #prepend-footer> |
| | | <Button |
| | | v-if="!isDetail" |
| | | type="default" |
| | | @click="handleOpenCalc" |
| | | > |
| | | 容éè®¡ç® |
| | | </Button> |
| | | </template> |
| | | <CalcModal @reel="handleFillReel" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | export { default as PdCtcSelect } from './select.vue'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { onMounted, ref } from 'vue'; |
| | | |
| | | import { Select } from 'ant-design-vue'; |
| | | |
| | | import { getCtcPage } from '#/api/mes/pd/ctc'; |
| | | |
| | | defineOptions({ name: 'PdCtcSelect', 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 options = ref<{ label: string; value: number }[]>([]); |
| | | const searchText = ref(''); |
| | | |
| | | async function load() { |
| | | const data = await getCtcPage({ |
| | | pageNo: 1, |
| | | pageSize: 100, |
| | | orderSpec: searchText.value || undefined, |
| | | }); |
| | | options.value = (data.list ?? []).map((item) => ({ |
| | | label: `${item.orderSpec ?? ''}${item.itemName ? `ï¼${item.itemName}ï¼` : ''}`, |
| | | value: item.id!, |
| | | })); |
| | | } |
| | | |
| | | onMounted(load); |
| | | |
| | | function handleSearch(val: string) { |
| | | searchText.value = val; |
| | | load(); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Select |
| | | :model-value="modelValue" |
| | | :options="options" |
| | | :disabled="disabled" |
| | | :placeholder="placeholder" |
| | | :allow-clear="allowClear" |
| | | show-search |
| | | :filter-option="false" |
| | | @update:value="(v: number | undefined) => emit('update:modelValue', v)" |
| | | @search="handleSearch" |
| | | /> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesPdCtcApi } from '#/api/mes/pd/ctc'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹æ¢ä½å¯¼çº¿äº§åææ¯åæ° */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'itemId', |
| | | label: 'ç©æ', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©æ¢ä½å¯¼çº¿ç©æ', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'orderSpec', |
| | | label: 'è®¢è´§è§æ ¼', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è®¢è´§è§æ ¼', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'bareConductorSpec', |
| | | label: 'è£¸å¯¼çº¿è§æ ¼', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è£¸å¯¼çº¿è§æ ¼', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'enameledSpec', |
| | | label: 'æ¼å
çº¿è§æ ¼', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¼å
çº¿è§æ ¼', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'transpositionSpec', |
| | | label: 'æ¢ä½è§æ ¼', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¢ä½è§æ ¼', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'conductorType', |
| | | label: '导ä½ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.PRODUCT_CONDUCTOR_TYPE, 'string'), |
| | | placeholder: 'è¯·éæ©å¯¼ä½ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'crossSection', |
| | | label: 'æªé¢ç§¯(mm²)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥æªé¢ç§¯', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'outerDiameter', |
| | | label: 'å¤å¾(mm)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥å¤å¾', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'resistance20c', |
| | | label: '20âç´æµçµé»(Ω/km)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 6, |
| | | placeholder: '请è¾å
¥20âç´æµçµé»', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'wrappingAngle', |
| | | label: 'ç»å
è§åº¦(°)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥ç»å
è§åº¦', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'insulationThickness', |
| | | label: 'ç»ç¼å度(mm)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥ç»ç¼å度', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'ratedVoltage', |
| | | label: 'é¢å®çµå(kV)', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥é¢å®çµå', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'testVoltage', |
| | | label: 'è¯éªçµå(kV)', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è¯éªçµå', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'minBendingRadius', |
| | | label: 'æå°å¼¯æ²åå¾(mm)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥æå°å¼¯æ²åå¾', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'weightPerKm', |
| | | label: 'æ¯å
¬ééé(kg/km)', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥æ¯å
¬ééé', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'standardCode', |
| | | label: 'æ§è¡æ å', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ§è¡æ å代å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'itemId', |
| | | label: 'ç©æ', |
| | | component: markRaw(MdItemSelect), |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: 'è¯·éæ©æ¢ä½å¯¼çº¿ç©æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'orderSpec', |
| | | label: 'è®¢è´§è§æ ¼', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥è®¢è´§è§æ ¼', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'conductorType', |
| | | label: '导ä½ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.PRODUCT_CONDUCTOR_TYPE, 'string'), |
| | | placeholder: 'è¯·éæ©å¯¼ä½ç±»å', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<MesPdCtcApi.Ctc>['columns'] { |
| | | return [ |
| | | { field: 'itemName', title: 'ç©æåç§°', minWidth: 140 }, |
| | | { field: 'itemCode', title: 'ç©æç¼ç ', minWidth: 120 }, |
| | | { field: 'orderSpec', title: 'è®¢è´§è§æ ¼', minWidth: 140 }, |
| | | { field: 'transpositionSpec', title: 'æ¢ä½è§æ ¼', minWidth: 120 }, |
| | | { |
| | | field: 'conductorType', |
| | | title: '导ä½ç±»å', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.PRODUCT_CONDUCTOR_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'crossSection', title: 'æªé¢ç§¯(mm²)', width: 110 }, |
| | | { field: 'outerDiameter', title: 'å¤å¾(mm)', width: 100 }, |
| | | { field: 'resistance20c', title: '20âçµé»(Ω/km)', width: 140 }, |
| | | { field: 'wrappingAngle', title: 'ç»å
è§åº¦(°)', width: 110 }, |
| | | { field: 'ratedVoltage', title: 'é¢å®çµå(kV)', width: 110 }, |
| | | { field: 'standardCode', title: 'æ§è¡æ å', minWidth: 140 }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 220, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesPdCtcApi } from '#/api/mes/pd/ctc'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteCtc, |
| | | exportCtc, |
| | | getCtcPage, |
| | | } from '#/api/mes/pd/ctc'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ°å¢ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥ç */ |
| | | function handleDetail(row: MesPdCtcApi.Ctc) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ */ |
| | | function handleEdit(row: MesPdCtcApi.Ctc) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å é¤ */ |
| | | async function handleDelete(row: MesPdCtcApi.Ctc) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.orderSpec ?? '']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteCtc(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.orderSpec ?? ''])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** å¯¼åº */ |
| | | async function handleExport() { |
| | | const data = await exportCtc(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'æ¢ä½å¯¼çº¿ææ¯åæ°.xls', source: data }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getCtcPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesPdCtcApi.Ctc>, |
| | | }); |
| | | </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:pd-ctc:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['mes:pd-ctc:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #orderSpec="{ row }"> |
| | | <Button type="link" @click="handleDetail(row)"> |
| | | {{ row.orderSpec || row.itemName }} |
| | | </Button> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pd-ctc:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pd-ctc:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.orderSpec ?? '']), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:pd-ctc:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { reactive, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { |
| | | Button, |
| | | Descriptions, |
| | | DescriptionsItem, |
| | | InputNumber, |
| | | message, |
| | | Select, |
| | | Tabs, |
| | | } from 'ant-design-vue'; |
| | | |
| | | import { |
| | | calcCtcAngle, |
| | | calcCtcResistance, |
| | | calcCtcReel, |
| | | } from '#/api/mes/pd/ctc'; |
| | | |
| | | const emit = defineEmits<{ |
| | | angle: [value: { wrappingAngle: number }]; |
| | | resistance: [value: { resistance20c: number }]; |
| | | reel: [value: { capacityKm: number }]; |
| | | }>(); |
| | | |
| | | /** ç»å
è§åº¦è®¡ç®è¡¨å */ |
| | | const angleForm = reactive<{ |
| | | conductorThickness?: number; |
| | | conductorWidth?: number; |
| | | cornerRadius?: number; |
| | | insulationThickness?: number; |
| | | tapeWidth?: number; |
| | | overlapWidth?: number; |
| | | }>({}); |
| | | const angleResult = ref<{ |
| | | circumference?: number; |
| | | wrappingAngle?: number; |
| | | wrappingPitch?: number; |
| | | }>(); |
| | | |
| | | /** ç´æµçµé»è®¡ç®è¡¨å */ |
| | | const resistanceForm = reactive<{ |
| | | conductorThickness?: number; |
| | | conductorWidth?: number; |
| | | temperature?: number; |
| | | material?: string; |
| | | }>({ temperature: 75, material: 'copper' }); |
| | | const resistanceResult = ref<{ |
| | | crossSection?: number; |
| | | resistance20cKm?: number; |
| | | resistanceAtTemp?: number; |
| | | resistanceAtTempKm?: number; |
| | | }>(); |
| | | |
| | | /** çå
·å®¹é计ç®è¡¨å */ |
| | | const reelForm = reactive<{ |
| | | reelOuterDiameter?: number; |
| | | reelInnerDiameter?: number; |
| | | reelWidth?: number; |
| | | ctcThickness?: number; |
| | | ctcWidth?: number; |
| | | }>({}); |
| | | const reelResult = ref<{ |
| | | capacityMeter?: number; |
| | | capacityKm?: number; |
| | | }>(); |
| | | |
| | | /** ç»å
è§åº¦è®¡ç® */ |
| | | async function handleCalcAngle() { |
| | | const data = await calcCtcAngle(angleForm); |
| | | angleResult.value = data; |
| | | } |
| | | |
| | | /** ç´æµçµé»è®¡ç® */ |
| | | async function handleCalcResistance() { |
| | | const data = await calcCtcResistance(resistanceForm); |
| | | resistanceResult.value = data; |
| | | } |
| | | |
| | | /** çå
·å®¹éè®¡ç® */ |
| | | async function handleCalcReel() { |
| | | const data = await calcCtcReel(reelForm); |
| | | reelResult.value = data; |
| | | } |
| | | |
| | | /** åå¡«ç»å
è§åº¦ */ |
| | | function handleFillAngle() { |
| | | if (angleResult.value?.wrappingAngle == null) { |
| | | message.warning('请å
æ§è¡ç»å
è§åº¦è®¡ç®'); |
| | | return; |
| | | } |
| | | emit('angle', { wrappingAngle: angleResult.value.wrappingAngle }); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } |
| | | |
| | | /** åå¡«ç´æµçµé» */ |
| | | function handleFillResistance() { |
| | | if (resistanceResult.value?.resistance20cKm == null) { |
| | | message.warning('请å
æ§è¡ç´æµçµé»è®¡ç®'); |
| | | return; |
| | | } |
| | | emit('resistance', { resistance20c: resistanceResult.value.resistance20cKm }); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } |
| | | |
| | | /** åå¡«çå
·å®¹é */ |
| | | function handleFillReel() { |
| | | if (reelResult.value?.capacityKm == null) { |
| | | message.warning('请å
æ§è¡çå
·å®¹é计ç®'); |
| | | return; |
| | | } |
| | | emit('reel', { capacityKm: reelResult.value.capacityKm }); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | angleResult.value = undefined; |
| | | resistanceResult.value = undefined; |
| | | reelResult.value = undefined; |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="æ¢ä½å¯¼çº¿è®¡ç®å·¥å
·" class="w-2/3"> |
| | | <Tabs> |
| | | <Tabs.TabPane key="angle" tab="ç»å
è§åº¦"> |
| | | <div class="grid grid-cols-3 gap-4"> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">导ä½å度(mm)</span> |
| | | <InputNumber |
| | | v-model:value="angleForm.conductorThickness" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 19.08" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">导ä½å®½åº¦(mm)</span> |
| | | <InputNumber |
| | | v-model:value="angleForm.conductorWidth" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 12.6" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">åè§åå¾(mm)</span> |
| | | <InputNumber |
| | | v-model:value="angleForm.cornerRadius" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 1" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">ç»ç¼å度(mm)</span> |
| | | <InputNumber |
| | | v-model:value="angleForm.insulationThickness" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 0.75" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">纸带宽度(mm)</span> |
| | | <InputNumber |
| | | v-model:value="angleForm.tapeWidth" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 22" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">éå 宽度(mm)</span> |
| | | <InputNumber |
| | | v-model:value="angleForm.overlapWidth" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 2" |
| | | /> |
| | | </div> |
| | | </div> |
| | | <div class="mt-4 flex gap-2"> |
| | | <Button type="primary" @click="handleCalcAngle">计ç®</Button> |
| | | <Button @click="handleFillAngle">åå¡«ç»å
è§åº¦</Button> |
| | | </div> |
| | | <Descriptions v-if="angleResult" class="mt-4" :column="3" bordered> |
| | | <DescriptionsItem label="导线å¨é¿(mm)"> |
| | | {{ angleResult.circumference }} |
| | | </DescriptionsItem> |
| | | <DescriptionsItem label="ç»å
è§åº¦(°)"> |
| | | {{ angleResult.wrappingAngle }} |
| | | </DescriptionsItem> |
| | | <DescriptionsItem label="ç»å
èè·(mm)"> |
| | | {{ angleResult.wrappingPitch }} |
| | | </DescriptionsItem> |
| | | </Descriptions> |
| | | </Tabs.TabPane> |
| | | |
| | | <Tabs.TabPane key="resistance" tab="ç´æµçµé»"> |
| | | <div class="grid grid-cols-3 gap-4"> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">导ä½å度(mm)</span> |
| | | <InputNumber |
| | | v-model:value="resistanceForm.conductorThickness" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 1.27" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">导ä½å®½åº¦(mm)</span> |
| | | <InputNumber |
| | | v-model:value="resistanceForm.conductorWidth" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 5.7" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">è®¡ç®æ¸©åº¦(â)</span> |
| | | <InputNumber |
| | | v-model:value="resistanceForm.temperature" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="0" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">å¯¼ä½æè´¨</span> |
| | | <Select |
| | | v-model:value="resistanceForm.material" |
| | | class="!flex-1" |
| | | :options="[ |
| | | { label: 'é', value: 'copper' }, |
| | | { label: 'é', value: 'aluminum' }, |
| | | ]" |
| | | /> |
| | | </div> |
| | | </div> |
| | | <div class="mt-4 flex gap-2"> |
| | | <Button type="primary" @click="handleCalcResistance">计ç®</Button> |
| | | <Button @click="handleFillResistance">åå¡«20âçµé»</Button> |
| | | </div> |
| | | <Descriptions v-if="resistanceResult" class="mt-4" :column="2" bordered> |
| | | <DescriptionsItem label="æªé¢ç§¯(mm²)"> |
| | | {{ resistanceResult.crossSection }} |
| | | </DescriptionsItem> |
| | | <DescriptionsItem label="20âçµé»(Ω/km)"> |
| | | {{ resistanceResult.resistance20cKm }} |
| | | </DescriptionsItem> |
| | | <DescriptionsItem label="è®¡ç®æ¸©åº¦çµé»(Ω/m)"> |
| | | {{ resistanceResult.resistanceAtTemp }} |
| | | </DescriptionsItem> |
| | | <DescriptionsItem label="è®¡ç®æ¸©åº¦çµé»(Ω/km)"> |
| | | {{ resistanceResult.resistanceAtTempKm }} |
| | | </DescriptionsItem> |
| | | </Descriptions> |
| | | </Tabs.TabPane> |
| | | |
| | | <Tabs.TabPane key="reel" tab="çå
·å®¹é"> |
| | | <div class="grid grid-cols-3 gap-4"> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">çå
·å¤å¾ d1(mm)</span> |
| | | <InputNumber |
| | | v-model:value="reelForm.reelOuterDiameter" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="0" |
| | | placeholder="å¦ 1600" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">çè¯å
å¾ d2(mm)</span> |
| | | <InputNumber |
| | | v-model:value="reelForm.reelInnerDiameter" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="0" |
| | | placeholder="å¦ 1100" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">ç宽 l2(mm)</span> |
| | | <InputNumber |
| | | v-model:value="reelForm.reelWidth" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="0" |
| | | placeholder="å¦ 500" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">CTCé«åº¦(mm)</span> |
| | | <InputNumber |
| | | v-model:value="reelForm.ctcThickness" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 19.08" |
| | | /> |
| | | </div> |
| | | <div class="flex items-center gap-2"> |
| | | <span class="w-28 text-right">CTC宽度(mm)</span> |
| | | <InputNumber |
| | | v-model:value="reelForm.ctcWidth" |
| | | class="!flex-1" |
| | | :min="0" |
| | | :precision="2" |
| | | placeholder="å¦ 12.6" |
| | | /> |
| | | </div> |
| | | </div> |
| | | <div class="mt-4 flex gap-2"> |
| | | <Button type="primary" @click="handleCalcReel">计ç®</Button> |
| | | <Button @click="handleFillReel">åå¡«æå¤§å®¹é</Button> |
| | | </div> |
| | | <Descriptions v-if="reelResult" class="mt-4" :column="2" bordered> |
| | | <DescriptionsItem label="æ¯çé¿åº¦(m)"> |
| | | {{ reelResult.capacityMeter }} |
| | | </DescriptionsItem> |
| | | <DescriptionsItem label="æ¯çé¿åº¦(km)"> |
| | | {{ reelResult.capacityKm }} |
| | | </DescriptionsItem> |
| | | </Descriptions> |
| | | </Tabs.TabPane> |
| | | </Tabs> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { MesPdCtcApi } from '#/api/mes/pd/ctc'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createCtc, |
| | | getCtc, |
| | | updateCtc, |
| | | } from '#/api/mes/pd/ctc'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | import CalcModal from './calc-modal.vue'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); // è¡¨åæ¨¡å¼ |
| | | const formData = ref<MesPdCtcApi.Ctc>(); |
| | | const isDetail = computed(() => formType.value === 'detail'); // æ¯å¦æ¥çæ¨¡å¼ |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return 'æ¥çæ¢ä½å¯¼çº¿ææ¯åæ°'; |
| | | } |
| | | return formType.value === 'update' ? 'ä¿®æ¹æ¢ä½å¯¼çº¿ææ¯åæ°' : 'æ°å¢æ¢ä½å¯¼çº¿ææ¯åæ°'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 130, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [CalcModal, calcModalApi] = useVbenModal({ |
| | | connectedComponent: CalcModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** æå¼è®¡ç®å·¥å
· */ |
| | | function handleOpenCalc() { |
| | | calcModalApi.open(); |
| | | } |
| | | |
| | | /** åå¡«ç»å
è§åº¦ */ |
| | | function handleFillAngle(value: { wrappingAngle: number }) { |
| | | formApi.setFieldValue('wrappingAngle', value.wrappingAngle); |
| | | } |
| | | |
| | | /** åå¡«ç´æµçµé» */ |
| | | function handleFillResistance(value: { resistance20c: number }) { |
| | | formApi.setFieldValue('resistance20c', value.resistance20c); |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = (await formApi.getValues()) as MesPdCtcApi.Ctc; |
| | | try { |
| | | await (data.id ? updateCtc(data) : createCtc(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.setState({ schema: useFormSchema() }); |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | modalApi.setState({ showConfirmButton: formType.value !== 'detail' }); |
| | | if (!data?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getCtc(data.id); |
| | | // è®¾ç½®å° values |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-4/5"> |
| | | <Form class="mx-4" /> |
| | | <template #prepend-footer> |
| | | <Button |
| | | v-if="!isDetail" |
| | | type="default" |
| | | @click="handleOpenCalc" |
| | | > |
| | | 计ç®å·¥å
· |
| | | </Button> |
| | | </template> |
| | | <CalcModal |
| | | @angle="handleFillAngle" |
| | | @resistance="handleFillResistance" |
| | | /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | export { default as CalcModal } from './calc-modal.vue'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesPdProductApi } from '#/api/mes/pd/product'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** 审æ¹ç¶æå¸¸é */ |
| | | export const AuditStatus = { |
| | | DRAFT: 0, // æªæäº¤ |
| | | PROCESS: 10, // 审æ¹ä¸ |
| | | APPROVE: 20, // å®¡æ ¸éè¿ |
| | | REJECT: 30, // å®¡æ ¸ä¸éè¿ |
| | | } as const; |
| | | |
| | | /** æ°å¢/ä¿®æ¹äº§åæ¡£æ¡ */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'archiveType', |
| | | label: 'æ¡£æ¡ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_PRODUCT_ARCHIVE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©æ¡£æ¡ç±»å', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¡£æ¡åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¡£æ¡åç§°', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'spec', |
| | | label: 'è§æ ¼åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è§æ ¼åå·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'unit', |
| | | label: 'åä½', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åä½', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'price', |
| | | label: 'åºåä»·æ ¼', |
| | | component: 'InputNumber', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | min: 0, |
| | | precision: 2, |
| | | placeholder: '请è¾å
¥åºåä»·æ ¼', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'å¯ç¨ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_PRODUCT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å¯ç¨ç¶æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'archiveType', |
| | | label: 'æ¡£æ¡ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_PRODUCT_ARCHIVE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©æ¡£æ¡ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: 'æ¡£æ¡åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ¡£æ¡åç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'auditStatus', |
| | | label: '审æ¹ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PD_AUDIT_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©å®¡æ¹ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<MesPdProductApi.Product>['columns'] { |
| | | return [ |
| | | { field: 'code', title: 'æ¡£æ¡ç¼ç ', width: 150 }, |
| | | { field: 'name', title: 'æ¡£æ¡åç§°', minWidth: 160 }, |
| | | { |
| | | field: 'archiveType', |
| | | title: 'æ¡£æ¡ç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PD_PRODUCT_ARCHIVE_TYPE }, |
| | | }, |
| | | }, |
| | | { field: 'spec', title: 'è§æ ¼åå·', width: 140 }, |
| | | { field: 'unit', title: 'åä½', width: 80 }, |
| | | { |
| | | field: 'price', |
| | | title: 'åºåä»·æ ¼', |
| | | width: 100, |
| | | formatter: 'formatNumber', |
| | | }, |
| | | { |
| | | field: 'auditStatus', |
| | | title: '审æ¹ç¶æ', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PD_AUDIT_STATUS }, |
| | | }, |
| | | }, |
| | | { field: 'auditRemark', title: 'å®¡æ¹æè§', width: 150 }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 260, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesPdProductApi } from '#/api/mes/pd/product'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteProduct, |
| | | exportProduct, |
| | | getProductPage, |
| | | submitProduct, |
| | | } from '#/api/mes/pd/product'; |
| | | |
| | | import { AuditStatus, useGridColumns, useGridFormSchema } from './data'; |
| | | import Audit from './modules/audit.vue'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | const [AuditModal, auditModalApi] = useVbenModal({ |
| | | connectedComponent: Audit, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ°å¢ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥ç */ |
| | | function handleDetail(row: MesPdProductApi.Product) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ */ |
| | | function handleEdit(row: MesPdProductApi.Product) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å é¤ */ |
| | | async function handleDelete(row: MesPdProductApi.Product) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.code ?? '']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteProduct(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.code ?? ''])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æäº¤å®¡æ¹ï¼æªæäº¤/驳å â 审æ¹ä¸ï¼ */ |
| | | async function handleSubmit(row: MesPdProductApi.Product) { |
| | | const hideLoading = message.loading({ content: 'æäº¤å®¡æ¹ä¸...', duration: 0 }); |
| | | try { |
| | | await submitProduct(row.id!); |
| | | message.success('æäº¤æå'); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** æå¼å®¡æ¹å¼¹çªï¼éè¿/驳åï¼ */ |
| | | function handleAudit(row: MesPdProductApi.Product, pass: boolean) { |
| | | auditModalApi.setData({ row, pass }).open(); |
| | | } |
| | | |
| | | /** å¯¼åº */ |
| | | async function handleExport() { |
| | | const data = await exportProduct(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'äº§åæ¡£æ¡.xls', source: data }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getProductPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesPdProductApi.Product>, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <AuditModal @success="handleRefresh" /> |
| | | <Grid table-title="äº§åæ¡£æ¡å表"> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('ui.actionTitle.create', ['äº§åæ¡£æ¡']), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['mes:pd-product:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['mes:pd-product:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æäº¤å®¡æ¹', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['mes:pd-product:submit'], |
| | | ifShow: |
| | | row.auditStatus === AuditStatus.DRAFT || |
| | | row.auditStatus === AuditStatus.REJECT, |
| | | popConfirm: { |
| | | title: '确认æäº¤å®¡æ¹ï¼', |
| | | confirm: handleSubmit.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'å®¡æ ¸éè¿', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['mes:pd-product:audit'], |
| | | ifShow: row.auditStatus === AuditStatus.PROCESS, |
| | | onClick: handleAudit.bind(null, row, true), |
| | | }, |
| | | { |
| | | label: 'å®¡æ ¸é©³å', |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['mes:pd-product:audit'], |
| | | ifShow: row.auditStatus === AuditStatus.PROCESS, |
| | | onClick: handleAudit.bind(null, row, false), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pd-product:update'], |
| | | ifShow: |
| | | row.auditStatus === AuditStatus.DRAFT || |
| | | row.auditStatus === AuditStatus.REJECT, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pd-product:delete'], |
| | | ifShow: |
| | | row.auditStatus === AuditStatus.DRAFT || |
| | | row.auditStatus === AuditStatus.REJECT, |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.code ?? '']), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:pd-product:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { MesPdProductApi } from '#/api/mes/pd/product'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { auditProduct } from '#/api/mes/pd/product'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const pass = ref(true); // true=éè¿ / false=驳å |
| | | const rowData = ref<MesPdProductApi.Product>(); |
| | | const getTitle = computed(() => (pass.value ? 'å®¡æ ¸éè¿' : 'å®¡æ ¸é©³å')); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 130, |
| | | }, |
| | | wrapperClass: 'grid-cols-1', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | /** å®¡æ ¸è¡¨ååæ®µ */ |
| | | function useAuditSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'auditRemark', |
| | | label: 'å®¡æ¹æè§', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-1', |
| | | componentProps: { |
| | | rows: 3, |
| | | placeholder: pass.value |
| | | ? '请è¾å
¥å®¡æ¹æè§ï¼å¯éï¼' |
| | | : '请è¾å
¥é©³ååå ï¼å¿
å¡«ï¼', |
| | | }, |
| | | rules: pass.value ? '' : 'required', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const values = await formApi.getValues(); |
| | | try { |
| | | await auditProduct({ |
| | | id: rowData.value!.id!, |
| | | pass: pass.value, |
| | | auditRemark: values.auditRemark, |
| | | }); |
| | | await modalApi.close(); |
| | | emit('success'); |
| | | message.success(pass.value ? '审æ¹éè¿' : '审æ¹é©³å'); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ |
| | | row: MesPdProductApi.Product; |
| | | pass: boolean; |
| | | }>(); |
| | | rowData.value = data.row; |
| | | pass.value = data.pass; |
| | | formApi.setState({ schema: useAuditSchema() }); |
| | | formApi.resetForm(); |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-2/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { MesPdProductApi } from '#/api/mes/pd/product'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createProduct, |
| | | getProduct, |
| | | updateProduct, |
| | | } from '#/api/mes/pd/product'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); // è¡¨åæ¨¡å¼ |
| | | const formData = ref<MesPdProductApi.Product>(); |
| | | const isDetail = computed(() => formType.value === 'detail'); // æ¯å¦æ¥çæ¨¡å¼ |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return 'æ¥çäº§åæ¡£æ¡'; |
| | | } |
| | | return formType.value === 'update' ? 'ä¿®æ¹äº§åæ¡£æ¡' : 'æ°å¢äº§åæ¡£æ¡'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 130, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = (await formApi.getValues()) as MesPdProductApi.Product; |
| | | try { |
| | | await (data.id ? updateProduct(data) : createProduct(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.setState({ schema: useFormSchema() }); |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | modalApi.setState({ showConfirmButton: formType.value !== 'detail' }); |
| | | if (!data?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getProduct(data.id); |
| | | // è®¾ç½®å° values |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-3/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormApi, VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { CrmCustomerApi } from '#/api/crm/customer'; |
| | | import type { MesPdTechConfirmApi } from '#/api/mes/pd/tech-confirm'; |
| | | |
| | | import { markRaw } from 'vue'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import CrmCustomerSelect from '#/components/crm-customer-select.vue'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'update'; |
| | | |
| | | /** æ°å¢/ä¿®æ¹äº§åææ¯ç¡®è®¤å½ */ |
| | | export function useFormSchema( |
| | | formApi?: VbenFormApi, |
| | | ): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'confirmNo', |
| | | label: '确认å½ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç¡®è®¤å½ç¼å·', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'customerId', |
| | | label: '客æ·', |
| | | component: markRaw(CrmCustomerSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | onChange: (item?: CrmCustomerApi.Customer) => { |
| | | formApi?.setFieldValue('customerName', item?.name); |
| | | }, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerName', |
| | | label: '客æ·åç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å®¢æ·åç§°', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'productName', |
| | | label: '产ååç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥äº§ååç§°', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'specification', |
| | | label: 'è§æ ¼åå·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è§æ ¼åå·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'techParams', |
| | | label: 'ææ¯åæ°(JSON)', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ææ¯åæ° JSONï¼å¦ {"thickness":"1.27","width":"5.7"}', |
| | | rows: 4, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'templateType', |
| | | label: '模æ¿ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.PRODUCT_TECH_CONFIRM_TEMPLATE, 'string'), |
| | | placeholder: 'è¯·éæ©æ¨¡æ¿ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.PRODUCT_TECH_CONFIRM_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'drafterName', |
| | | label: 'ç¼å¶äºº', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç¼å¶äººå§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'draftTime', |
| | | label: 'ç¼å¶æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reviewerName', |
| | | label: 'å®¡æ ¸äºº', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å®¡æ ¸äººå§å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reviewTime', |
| | | label: 'å®¡æ ¸æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerConfirmUser', |
| | | label: '客æ·ç¡®è®¤äºº', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å®¢æ·ç¡®è®¤äºº', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'customerConfirmTime', |
| | | label: '客æ·ç¡®è®¤æ¶é´', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | showTime: true, |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-3', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | rows: 2, |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'confirmNo', |
| | | label: '确认å½ç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥ç¡®è®¤å½ç¼å·', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'productName', |
| | | label: '产ååç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | allowClear: true, |
| | | placeholder: '请è¾å
¥äº§ååç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'templateType', |
| | | label: '模æ¿ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.PRODUCT_TECH_CONFIRM_TEMPLATE, 'string'), |
| | | placeholder: 'è¯·éæ©æ¨¡æ¿ç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.PRODUCT_TECH_CONFIRM_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<MesPdTechConfirmApi.TechConfirm>['columns'] { |
| | | return [ |
| | | { field: 'confirmNo', title: '确认å½ç¼å·', minWidth: 160 }, |
| | | { field: 'customerName', title: '客æ·åç§°', minWidth: 180 }, |
| | | { field: 'productName', title: '产ååç§°', minWidth: 140 }, |
| | | { field: 'specification', title: 'è§æ ¼åå·', minWidth: 140 }, |
| | | { |
| | | field: 'templateType', |
| | | title: '模æ¿ç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.PRODUCT_TECH_CONFIRM_TEMPLATE }, |
| | | }, |
| | | }, |
| | | { field: 'drafterName', title: 'ç¼å¶äºº', width: 90 }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.PRODUCT_TECH_CONFIRM_STATUS }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'createTime', |
| | | title: 'å建æ¶é´', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 260, |
| | | fixed: 'right', |
| | | slots: { |
| | | default: 'actions', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesPdTechConfirmApi } from '#/api/mes/pd/tech-confirm'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deleteTechConfirm, |
| | | exportTechConfirm, |
| | | getTechConfirmPage, |
| | | updateTechConfirm, |
| | | } from '#/api/mes/pd/tech-confirm'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | /** å·æ°è¡¨æ ¼ */ |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | /** æ°å¢ */ |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | /** æ¥ç */ |
| | | function handleDetail(row: MesPdTechConfirmApi.TechConfirm) { |
| | | formModalApi.setData({ id: row.id, formType: 'detail' }).open(); |
| | | } |
| | | |
| | | /** ç¼è¾ */ |
| | | function handleEdit(row: MesPdTechConfirmApi.TechConfirm) { |
| | | formModalApi.setData({ id: row.id, formType: 'update' }).open(); |
| | | } |
| | | |
| | | /** å é¤ */ |
| | | async function handleDelete(row: MesPdTechConfirmApi.TechConfirm) { |
| | | const hideLoading = message.loading({ |
| | | content: $t('ui.actionMessage.deleting', [row.confirmNo ?? '']), |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | await deleteTechConfirm(row.id!); |
| | | message.success($t('ui.actionMessage.deleteSuccess', [row.confirmNo ?? ''])); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** ç¶ææµè½¬ */ |
| | | async function handleStatus(row: MesPdTechConfirmApi.TechConfirm, next: number) { |
| | | const hideLoading = message.loading({ content: 'ç¶ææ´æ°ä¸...', duration: 0 }); |
| | | try { |
| | | await updateTechConfirm({ ...row, status: next }); |
| | | message.success($t('ui.actionMessage.operationSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | /** å¯¼åº */ |
| | | async function handleExport() { |
| | | const data = await exportTechConfirm(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'äº§åææ¯ç¡®è®¤å½.xls', source: data }); |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getTechConfirmPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesPdTechConfirmApi.TechConfirm>, |
| | | }); |
| | | </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:pd-tech-confirm:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['mes:pd-tech-confirm:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | <template #confirmNo="{ row }"> |
| | | <Button type="link" @click="handleDetail(row)"> |
| | | {{ row.confirmNo }} |
| | | </Button> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æäº¤å®¡æ ¸', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['mes:pd-tech-confirm:update'], |
| | | ifShow: row.status === 0, |
| | | onClick: handleStatus.bind(null, row, 1), |
| | | }, |
| | | { |
| | | label: 'å®¡æ ¸éè¿', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['mes:pd-tech-confirm:update'], |
| | | ifShow: row.status === 1, |
| | | onClick: handleStatus.bind(null, row, 2), |
| | | }, |
| | | { |
| | | label: '客æ·ç¡®è®¤', |
| | | type: 'link', |
| | | icon: ACTION_ICON.AUDIT, |
| | | auth: ['mes:pd-tech-confirm:update'], |
| | | ifShow: row.status === 2, |
| | | onClick: handleStatus.bind(null, row, 3), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pd-tech-confirm:update'], |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pd-tech-confirm:delete'], |
| | | popConfirm: { |
| | | title: $t('ui.actionMessage.deleteConfirm', [row.confirmNo ?? '']), |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:pd-tech-confirm:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { FormType } from '../data'; |
| | | |
| | | import type { MesPdTechConfirmApi } from '#/api/mes/pd/tech-confirm'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | import { $t } from '@vben/locales'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createTechConfirm, |
| | | getTechConfirm, |
| | | updateTechConfirm, |
| | | } from '#/api/mes/pd/tech-confirm'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formType = ref<FormType>('create'); // è¡¨åæ¨¡å¼ |
| | | const formData = ref<MesPdTechConfirmApi.TechConfirm>(); |
| | | const isDetail = computed(() => formType.value === 'detail'); // æ¯å¦æ¥çæ¨¡å¼ |
| | | const getTitle = computed(() => { |
| | | if (formType.value === 'detail') { |
| | | return 'æ¥çææ¯ç¡®è®¤å½'; |
| | | } |
| | | return formType.value === 'update' ? 'ä¿®æ¹ææ¯ç¡®è®¤å½' : 'æ°å¢ææ¯ç¡®è®¤å½'; |
| | | }); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { |
| | | class: 'w-full', |
| | | }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 130, |
| | | }, |
| | | wrapperClass: 'grid-cols-3', |
| | | layout: 'horizontal', |
| | | schema: [], |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (isDetail.value) { |
| | | await modalApi.close(); |
| | | return; |
| | | } |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | // æäº¤è¡¨å |
| | | const data = (await formApi.getValues()) as MesPdTechConfirmApi.TechConfirm; |
| | | try { |
| | | await (data.id ? updateTechConfirm(data) : createTechConfirm(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.setState({ schema: useFormSchema(formApi) }); |
| | | formApi.setDisabled(formType.value === 'detail'); |
| | | modalApi.setState({ showConfirmButton: formType.value !== 'detail' }); |
| | | if (!data?.id) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | try { |
| | | formData.value = await getTechConfirm(data.id); |
| | | // è®¾ç½®å° values |
| | | await formApi.setValues(formData.value); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-4/5"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 { 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 PLAN_STATUS = { |
| | | DRAFT: 0, |
| | | PUBLISHED: 10, |
| | | }; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType): 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: 'planType', |
| | | label: '颿¡ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_PRO_EMERGENCY_PLAN_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©é¢æ¡ç±»å', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'orgUnit', |
| | | label: '责任åä½', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è´£ä»»åä½/é¨é¨', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'content', |
| | | label: 'å¤ç½®å
容', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥åºæ¥å¤ç½®å
容', |
| | | rows: 4, |
| | | disabled: isReadonly, |
| | | }, |
| | | formItemClass: 'col-span-3', |
| | | }, |
| | | { |
| | | 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: 'planType', |
| | | label: '颿¡ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PRO_EMERGENCY_PLAN_TYPE, 'number'), |
| | | 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: 'planType', |
| | | title: '颿¡ç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PRO_EMERGENCY_PLAN_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'orgUnit', |
| | | 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 { MesProEmergencyPlanApi } from '#/api/mes/pro/emergencyplan'; |
| | | |
| | | 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/emergencyplan'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { PLAN_STATUS, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** MES åºæ¥å¤ç½®é¢æ¡å表 */ |
| | | defineOptions({ name: 'MesProEmergencyPlan' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: MesProEmergencyPlanApi.EmergencyPlan) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleDetail(row: MesProEmergencyPlanApi.EmergencyPlan) { |
| | | 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: MesProEmergencyPlanApi.EmergencyPlan) { |
| | | await publishPlan(row.id!); |
| | | message.success('å·²åå¸'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: MesProEmergencyPlanApi.EmergencyPlan[]; |
| | | }) { |
| | | 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<MesProEmergencyPlanApi.EmergencyPlan>, |
| | | 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-emergency-plan:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pro-emergency-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-emergency-plan:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pro-emergency-plan:update'], |
| | | ifShow: row.status === PLAN_STATUS.DRAFT, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'åå¸', |
| | | type: 'link', |
| | | auth: ['mes:pro-emergency-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-emergency-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 { MesProEmergencyPlanApi } from '#/api/mes/pro/emergencyplan'; |
| | | |
| | | 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/emergencyplan'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesProEmergencyPlanApi.EmergencyPlan>(); |
| | | 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 MesProEmergencyPlanApi.EmergencyPlan; |
| | | 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)); |
| | | 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 { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** 计åç¶æå¸¸é */ |
| | | export const PLAN_STATUS = { |
| | | DRAFT: 0, |
| | | ISSUED: 10, |
| | | FINISHED: 20, |
| | | CANCELLED: 30, |
| | | }; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType): 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: 'planType', |
| | | label: '计åç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_PRO_MAINTENANCE_PLAN_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©è®¡åç±»å', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'station', |
| | | label: 'ç«ç¹', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥ç«ç¹', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'line', |
| | | label: '线路', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥çº¿è·¯', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'planDate', |
| | | label: 'è®¡åæ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'éæ©è®¡åæ¥æ', |
| | | showTime: true, |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'teamId', |
| | | label: 'çç»', |
| | | component: 'Input', |
| | | componentProps: { |
| | | disabled: true, |
| | | placeholder: 'ç±è®¡åæ§è¡åé
', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: [''], |
| | | show: () => false, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: isReadonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'items', |
| | | label: 'å¤ä»¶/ç©èµéæ±', |
| | | component: 'Input', |
| | | formItemClass: 'col-span-3', |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: '计åç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è®¡åç¼ç ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: '计ååç§°', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥è®¡ååç§°', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'planType', |
| | | label: '计åç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PRO_MAINTENANCE_PLAN_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©è®¡åç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'è稿', value: PLAN_STATUS.DRAFT }, |
| | | { label: 'å·²ä¸å', value: PLAN_STATUS.ISSUED }, |
| | | { label: '已宿', value: PLAN_STATUS.FINISHED }, |
| | | { label: '已忶', value: PLAN_STATUS.CANCELLED }, |
| | | ], |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'planDate', |
| | | label: 'è®¡åæ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: getRangePickerDefaultProps(), |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çè¡¨æ ¼å */ |
| | | 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: 'planType', |
| | | title: '计åç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PRO_MAINTENANCE_PLAN_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'station', |
| | | title: 'ç«ç¹', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'line', |
| | | title: '线路', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'teamName', |
| | | title: 'çç»', |
| | | width: 110, |
| | | }, |
| | | { |
| | | field: 'planDate', |
| | | title: 'è®¡åæ¥æ', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'workOrderCode', |
| | | title: 'çæçå·¥å', |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 300, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesProMaintenancePlanApi } from '#/api/mes/pro/maintenance-plan'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { downloadFileFromBlobPart, isEmpty } from '@vben/utils'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { |
| | | deletePlan, |
| | | exportPlan, |
| | | finishPlan, |
| | | generatePurchasePlan, |
| | | generateWorkOrder, |
| | | getPlanPage, |
| | | issuePlan, |
| | | } from '#/api/mes/pro/maintenance-plan'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { PLAN_STATUS, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** MES çµåä½ä¸è®¡åå表 */ |
| | | defineOptions({ name: 'MesProMaintenancePlan' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | async function handleExport() { |
| | | const data = await exportPlan(await gridApi.formApi.getValues()); |
| | | downloadFileFromBlobPart({ fileName: 'çµåä½ä¸è®¡å.xls', source: data }); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: MesProMaintenancePlanApi.MaintenancePlan) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleDetail(row: MesProMaintenancePlanApi.MaintenancePlan) { |
| | | 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 handleIssue(row: MesProMaintenancePlanApi.MaintenancePlan) { |
| | | await issuePlan(row.id!); |
| | | message.success('å·²ä¸å'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | async function handleFinish(row: MesProMaintenancePlanApi.MaintenancePlan) { |
| | | await finishPlan(row.id!); |
| | | message.success('已宿'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | async function handleGeneratePurchasePlan( |
| | | row: MesProMaintenancePlanApi.MaintenancePlan, |
| | | ) { |
| | | const hideLoading = message.loading({ |
| | | content: 'æ£å¨æ ¹æ®å¤ä»¶éæ±çæéè´è®¡å...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const planId = await generatePurchasePlan(row.id!); |
| | | message.success(`å·²çæéè´è®¡åï¼ç¼å· ${planId}ï¼ï¼è¯·å¨éè´ç®¡ç䏿 ¸å¯¹æäº¤å®¡æ¹`); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | async function handleGenerateWorkOrder( |
| | | row: MesProMaintenancePlanApi.MaintenancePlan, |
| | | ) { |
| | | const hideLoading = message.loading({ |
| | | content: 'æ£å¨çæå·¥å...', |
| | | duration: 0, |
| | | }); |
| | | try { |
| | | const workOrderId = await generateWorkOrder(row.id!); |
| | | message.success(`å·²çæå·¥åï¼ç¼å· ${workOrderId}ï¼ï¼å¯å¨ç产订å䏿¥ç`); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: MesProMaintenancePlanApi.MaintenancePlan[]; |
| | | }) { |
| | | 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<MesProMaintenancePlanApi.MaintenancePlan>, |
| | | 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-plan:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: $t('ui.actionTitle.export'), |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['mes:pro-maintenance-plan:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pro-maintenance-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.ISSUED" color="processing">å·²ä¸å</Tag> |
| | | <Tag v-else-if="row.status === PLAN_STATUS.FINISHED" color="success">已宿</Tag> |
| | | <Tag v-else-if="row.status === PLAN_STATUS.CANCELLED" color="warning">已忶</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:pro-maintenance-plan:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:pro-maintenance-plan:update'], |
| | | ifShow: row.status === PLAN_STATUS.DRAFT, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'ä¸å', |
| | | type: 'link', |
| | | auth: ['mes:pro-maintenance-plan:update'], |
| | | ifShow: row.status === PLAN_STATUS.DRAFT, |
| | | popConfirm: { |
| | | title: '确认ä¸å该计åï¼', |
| | | confirm: handleIssue.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: 'çæéè´è®¡å', |
| | | type: 'link', |
| | | auth: ['mes:pro-maintenance-plan:generate-purchase-plan'], |
| | | ifShow: row.status === PLAN_STATUS.ISSUED, |
| | | onClick: handleGeneratePurchasePlan.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'çæå·¥å', |
| | | type: 'link', |
| | | auth: ['mes:pro-maintenance-plan:generate-work-order'], |
| | | ifShow: row.status === PLAN_STATUS.ISSUED && !row.workOrderId, |
| | | onClick: handleGenerateWorkOrder.bind(null, row), |
| | | }, |
| | | { |
| | | label: '宿', |
| | | type: 'link', |
| | | auth: ['mes:pro-maintenance-plan:update'], |
| | | ifShow: row.status === PLAN_STATUS.ISSUED, |
| | | popConfirm: { |
| | | title: 'ç¡®è®¤å®æè¯¥è®¡åï¼', |
| | | confirm: handleFinish.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:pro-maintenance-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 { MesProMaintenancePlanApi } from '#/api/mes/pro/maintenance-plan'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { Button, DatePicker, Input, InputNumber, message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createPlan, |
| | | getPlan, |
| | | updatePlan, |
| | | } from '#/api/mes/pro/maintenance-plan'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesProMaintenancePlanApi.MaintenancePlan>(); |
| | | const formType = ref<FormType>('create'); |
| | | const items = ref<MesProMaintenancePlanApi.PlanItem[]>([]); |
| | | |
| | | 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, |
| | | }); |
| | | |
| | | function handleAddItem() { |
| | | items.value.push({ |
| | | itemId: undefined as unknown as number, |
| | | itemName: '', |
| | | itemCode: '', |
| | | unitMeasureName: '', |
| | | quantity: 1, |
| | | remark: '', |
| | | }); |
| | | } |
| | | |
| | | function handleRemoveItem(index: number) { |
| | | items.value.splice(index, 1); |
| | | } |
| | | |
| | | function handleItemSelect(index: number, item: any) { |
| | | const row = items.value[index]; |
| | | if (!row) { |
| | | return; |
| | | } |
| | | row.itemId = item?.id; |
| | | row.itemName = item?.name; |
| | | row.itemCode = item?.code; |
| | | row.unitMeasureName = item?.unitMeasureName; |
| | | } |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | if (!items.value.length) { |
| | | message.error('请è³å°æ·»å 䏿¡å¤ä»¶/ç©èµéæ±'); |
| | | return; |
| | | } |
| | | if (items.value.some((item) => !item.itemId || !item.quantity)) { |
| | | message.error('请å®åå¤ä»¶éæ±ç产å䏿°é'); |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = |
| | | (await formApi.getValues()) as MesProMaintenancePlanApi.MaintenancePlan; |
| | | data.items = items.value.map((item) => ({ |
| | | itemId: item.itemId, |
| | | quantity: item.quantity, |
| | | remark: item.remark, |
| | | })); |
| | | 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; |
| | | items.value = []; |
| | | 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 getPlan(data.id); |
| | | await formApi.setValues(formData.value); |
| | | items.value = (formData.value.items ?? []).map((item) => ({ ...item })); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal |
| | | :title="getTitle" |
| | | class="w-3/4" |
| | | :show-confirm-button="formType !== 'detail'" |
| | | > |
| | | <Form class="mx-3"> |
| | | <template #items> |
| | | <div class="mt-2"> |
| | | <div v-if="formType !== 'detail'" class="mb-2"> |
| | | <Button type="dashed" @click="handleAddItem">+ æ·»å å¤ä»¶éæ±</Button> |
| | | </div> |
| | | <table class="w-full border-collapse border border-gray-200 text-sm"> |
| | | <thead> |
| | | <tr class="bg-gray-100"> |
| | | <th class="border border-gray-200 px-2 py-1 w-12">åºå·</th> |
| | | <th class="border border-gray-200 px-2 py-1">ç©æ/å¤ä»¶</th> |
| | | <th class="border border-gray-200 px-2 py-1 w-20">åä½</th> |
| | | <th class="border border-gray-200 px-2 py-1 w-32">éæ±æ°é</th> |
| | | <th class="border border-gray-200 px-2 py-1">夿³¨</th> |
| | | <th |
| | | v-if="formType !== 'detail'" |
| | | class="border border-gray-200 px-2 py-1 w-16" |
| | | > |
| | | æä½ |
| | | </th> |
| | | </tr> |
| | | </thead> |
| | | <tbody> |
| | | <tr v-for="(item, index) in items" :key="index"> |
| | | <td class="border border-gray-200 px-2 py-1 text-center"> |
| | | {{ index + 1 }} |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <MdItemSelect |
| | | v-if="formType !== 'detail'" |
| | | :model-value="item.itemId" |
| | | placeholder="è¯·éæ©ç©æ/å¤ä»¶" |
| | | @change="handleItemSelect(index, $event)" |
| | | /> |
| | | <span v-else>{{ item.itemName }}</span> |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1 text-center"> |
| | | {{ item.unitMeasureName || '-' }} |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <InputNumber |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="item.quantity" |
| | | class="!w-full" |
| | | :min="0.01" |
| | | :precision="2" |
| | | /> |
| | | <span v-else>{{ item.quantity }}</span> |
| | | </td> |
| | | <td class="border border-gray-200 px-2 py-1"> |
| | | <Input |
| | | v-if="formType !== 'detail'" |
| | | v-model:value="item.remark" |
| | | placeholder="夿³¨" |
| | | /> |
| | | <span v-else>{{ item.remark || '-' }}</span> |
| | | </td> |
| | | <td |
| | | v-if="formType !== 'detail'" |
| | | class="border border-gray-200 px-2 py-1 text-center" |
| | | > |
| | | <Button type="link" danger @click="handleRemoveItem(index)"> |
| | | å é¤ |
| | | </Button> |
| | | </td> |
| | | </tr> |
| | | <tr v-if="!items.length"> |
| | | <td |
| | | :colspan="formType === 'detail' ? 5 : 6" |
| | | class="border border-gray-200 px-2 py-4 text-center text-gray-400" |
| | | > |
| | | ææ å¤ä»¶éæ±ï¼è¯·æ·»å |
| | | </td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | </div> |
| | | </template> |
| | | </Form> |
| | | </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> |
| | |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | import { MdItemSelect } from '#/views/mes/md/item/components'; |
| | | import { MdWorkstationSelect } from '#/views/mes/md/workstation/components'; |
| | | import { CalTeamSelect } from '#/views/mes/cal/team/components'; |
| | | import { ProProcessSelect } from '#/views/mes/process-design/process/components'; |
| | | import { RouteColorPicker } from '#/views/mes/process-design/route/components'; |
| | | import { ProWorkOrderSelect } from '#/views/mes/pro/workorder/components'; |
| | |
| | | width: 120, |
| | | }, |
| | | { |
| | | field: 'teamName', |
| | | title: 'çç»', |
| | | width: 120, |
| | | }, |
| | | { |
| | | field: 'quantity', |
| | | title: 'æäº§æ°é', |
| | | width: 100, |
| | |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'teamId', |
| | | label: 'çç»', |
| | | component: markRaw(CalTeamSelect), |
| | | componentProps: { |
| | | placeholder: 'è¯·éæ©çç»', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'quantity', |
| | | label: 'æäº§æ°é', |
| | | component: 'InputNumber', |
| | |
| | | import type { MesProWorkOrderApi } from '#/api/mes/pro/workorder'; |
| | | import type { MesProWorkOrderBomApi } from '#/api/mes/pro/workorder/bom'; |
| | | import type { MesProWorkOrderProcessApi } from '#/api/mes/pro/workorder/process'; |
| | | import type { ErpSaleOrderApi } from '#/api/erp/sale/order'; |
| | | |
| | | import { h, markRaw } from 'vue'; |
| | | |
| | |
| | | MdItemSelect, |
| | | } from '#/views/mes/md/item/components'; |
| | | import { MdVendorSelect } from '#/views/mes/md/vendor/components'; |
| | | import { ErpSaleOrderSelect } from '#/views/erp/sale/order/components'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'confirm' | 'create' | 'detail' | 'finish' | 'update'; |
| | |
| | | componentProps: { |
| | | disabled: headerReadonly, |
| | | placeholder: 'è¯·éæ©å®¢æ·', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['orderSourceType'], |
| | | show: (values) => |
| | | values.orderSourceType === MesProWorkOrderSourceTypeEnum.ORDER, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'saleOrderId', |
| | | label: 'è¥é订å', |
| | | component: markRaw(ErpSaleOrderSelect), |
| | | componentProps: { |
| | | disabled: headerReadonly, |
| | | placeholder: 'è¯·éæ©è¥é订åï¼ERPéå®è®¢åï¼', |
| | | // éä¸è¥é订ååèªå¨å¸¦å
¥å®¢æ· |
| | | onChange: async (item?: ErpSaleOrderApi.SaleOrder) => { |
| | | if (!item) { |
| | | return; |
| | | } |
| | | const values = (await formApi?.getValues()) ?? {}; |
| | | if (values.clientId == null && item.customerId) { |
| | | await formApi?.setFieldValue('clientId', item.customerId); |
| | | } |
| | | }, |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['orderSourceType'], |
| | |
| | | width: 120, |
| | | }, |
| | | { |
| | | field: 'saleOrderNo', |
| | | title: 'è¥é订åå·', |
| | | width: 140, |
| | | }, |
| | | { |
| | | field: 'requestDate', |
| | | title: 'éæ±æ¥æ', |
| | | width: 180, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { WorkResultSummary } from '#/api/mes/pro/feedback'; |
| | | |
| | | import { onMounted, reactive, ref } from 'vue'; |
| | | |
| | | import { Page } from '@vben/common-ui'; |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { |
| | | Button, |
| | | Card, |
| | | Col, |
| | | DatePicker, |
| | | Row, |
| | | Select, |
| | | Space, |
| | | Table, |
| | | Tag, |
| | | } from 'ant-design-vue'; |
| | | |
| | | import { getFeedbackSummary } from '#/api/mes/pro/feedback'; |
| | | |
| | | defineOptions({ name: 'MesWorkResultSummary' }); |
| | | |
| | | const loading = ref(false); |
| | | const summary = ref<WorkResultSummary>({}); |
| | | |
| | | const queryForm = reactive<{ |
| | | beginDate?: string; |
| | | endDate?: string; |
| | | workOrderType?: number; |
| | | }>({}); |
| | | |
| | | const workOrderTypeOptions = getDictOptions( |
| | | DICT_TYPE.MES_PRO_WORK_ORDER_TYPE, |
| | | 'number', |
| | | ).map((option) => ({ label: option.label, value: option.value as number })); |
| | | |
| | | async function loadSummary() { |
| | | loading.value = true; |
| | | try { |
| | | summary.value = await getFeedbackSummary({ |
| | | beginDate: queryForm.beginDate, |
| | | endDate: queryForm.endDate, |
| | | workOrderType: queryForm.workOrderType, |
| | | }); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | function handleRangeChange(_: unknown, dateStrings: string[]) { |
| | | queryForm.beginDate = dateStrings[0] || undefined; |
| | | queryForm.endDate = dateStrings[1] || undefined; |
| | | } |
| | | |
| | | function resetQuery() { |
| | | queryForm.beginDate = undefined; |
| | | queryForm.endDate = undefined; |
| | | queryForm.workOrderType = undefined; |
| | | loadSummary(); |
| | | } |
| | | |
| | | onMounted(loadSummary); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page> |
| | | <Card class="mb-4"> |
| | | <Space wrap> |
| | | <DatePicker.RangePicker |
| | | :placeholder="['å¼å§æ¥æ', 'ç»ææ¥æ']" |
| | | value-format="YYYY-MM-DD" |
| | | @change="handleRangeChange" |
| | | /> |
| | | <Select |
| | | v-model:value="queryForm.workOrderType" |
| | | allow-clear |
| | | :options="workOrderTypeOptions" |
| | | placeholder="å·¥åç±»å" |
| | | style="width: 160px" |
| | | /> |
| | | <Button type="primary" :loading="loading" @click="loadSummary"> |
| | | æ¥è¯¢ |
| | | </Button> |
| | | <Button @click="resetQuery">éç½®</Button> |
| | | </Space> |
| | | </Card> |
| | | |
| | | <Row :gutter="16" class="mb-4"> |
| | | <Col :span="8"> |
| | | <Card> |
| | | <div class="text-sm text-gray-500">累计æ¥å·¥æ°é</div> |
| | | <div class="mt-2 text-2xl font-bold"> |
| | | {{ summary.totalFeedbackQuantity ?? 0 }} |
| | | </div> |
| | | </Card> |
| | | </Col> |
| | | <Col :span="8"> |
| | | <Card> |
| | | <div class="text-sm text-gray-500">ç´¯è®¡åæ ¼æ°é</div> |
| | | <div class="mt-2 text-2xl font-bold text-green-600"> |
| | | {{ summary.totalQualifiedQuantity ?? 0 }} |
| | | </div> |
| | | </Card> |
| | | </Col> |
| | | <Col :span="8"> |
| | | <Card> |
| | | <div class="text-sm text-gray-500">累计æ¥å·¥åæ°</div> |
| | | <div class="mt-2 text-2xl font-bold text-blue-600"> |
| | | {{ summary.totalFeedbackCount ?? 0 }} |
| | | </div> |
| | | </Card> |
| | | </Col> |
| | | </Row> |
| | | |
| | | <Card class="mb-4" title="æå·¥åç±»åæ±æ»"> |
| | | <Table |
| | | row-key="workOrderType" |
| | | :data-source="summary.byType ?? []" |
| | | :loading="loading" |
| | | :pagination="false" |
| | | size="middle" |
| | | > |
| | | <Table.Column title="å·¥åç±»å" data-index="workOrderTypeName"> |
| | | <template #default="{ record }"> |
| | | <Tag color="blue">{{ record.workOrderTypeName }}</Tag> |
| | | </template> |
| | | </Table.Column> |
| | | <Table.Column |
| | | title="æ¥å·¥æ°é" |
| | | data-index="feedbackQuantity" |
| | | align="right" |
| | | /> |
| | | <Table.Column |
| | | title="åæ ¼æ°é" |
| | | data-index="qualifiedQuantity" |
| | | align="right" |
| | | /> |
| | | <Table.Column title="æ¥å·¥åæ°" data-index="feedbackCount" align="right" /> |
| | | </Table> |
| | | </Card> |
| | | |
| | | <Card title="ææ¥ææ±æ»"> |
| | | <Table |
| | | row-key="date" |
| | | :data-source="summary.byDate ?? []" |
| | | :loading="loading" |
| | | :pagination="false" |
| | | size="middle" |
| | | > |
| | | <Table.Column title="æ¥æ" data-index="date" /> |
| | | <Table.Column |
| | | title="æ¥å·¥æ°é" |
| | | data-index="feedbackQuantity" |
| | | align="right" |
| | | /> |
| | | <Table.Column |
| | | title="åæ ¼æ°é" |
| | | data-index="qualifiedQuantity" |
| | | align="right" |
| | | /> |
| | | <Table.Column title="æ¥å·¥åæ°" data-index="feedbackCount" align="right" /> |
| | | </Table> |
| | | </Card> |
| | | </Page> |
| | | </template> |
| | |
| | | rules: z.string().min(1, 'å·¥åºåç§°ä¸è½ä¸ºç©º').max(100), |
| | | }, |
| | | { |
| | | fieldName: 'type', |
| | | label: 'çµåå·¥èºåç±»', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_PRO_PROCESS_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©çµåå·¥èºåç±»', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'RadioGroup', |
| | |
| | | }, |
| | | { field: 'name', title: 'å·¥åºåç§°', minWidth: 180 }, |
| | | { |
| | | field: 'type', |
| | | title: 'çµåå·¥èºåç±»', |
| | | width: 120, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_PRO_PROCESS_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | |
| | | import { DICT_TYPE } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { getRangePickerDefaultProps } from '#/utils'; |
| | | |
| | | /** 表åç±»å */ |
| | | export type FormType = 'create' | 'detail' | 'edit'; |
| | | |
| | | /** ç¶æå¸¸é */ |
| | | export const TEST_STATUS = { |
| | | DRAFT: 0, |
| | | SUBMITTED: 10, |
| | | FINISHED: 20, |
| | | }; |
| | | |
| | | /** 表åçé
置项 */ |
| | | export function useFormSchema(formType: FormType): 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: 'testType', |
| | | label: 'æ£æµç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_QC_OUTSOURCE_TEST_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©æ£æµç±»å', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'itemId', |
| | | label: 'æ£æµå¯¹è±¡', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: 'æ£æµå¯¹è±¡ï¼ç©æ/设å¤ï¼', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'testOrg', |
| | | label: 'æ£æµæºæ', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ£æµæºæ', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'testDate', |
| | | label: 'æ£æµæ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | placeholder: 'éæ©æ£æµæ¥æ', |
| | | showTime: true, |
| | | format: 'YYYY-MM-DD HH:mm:ss', |
| | | valueFormat: 'YYYY-MM-DD HH:mm:ss', |
| | | disabled: isReadonly, |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'result', |
| | | label: 'æ£æµç»æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_QC_OUTSOURCE_TEST_RESULT, 'number'), |
| | | placeholder: 'è¯·éæ©æ£æµç»æ', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'reportNo', |
| | | label: 'æ¥åç¼å·', |
| | | component: 'Input', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥æ£æµæ¥åç¼å·', |
| | | disabled: isReadonly, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | componentProps: { |
| | | placeholder: '请è¾å
¥å¤æ³¨', |
| | | autoSize: { minRows: 1, maxRows: 1 }, |
| | | disabled: isReadonly, |
| | | }, |
| | | formItemClass: 'col-span-2', |
| | | }, |
| | | { |
| | | fieldName: 'blobIds', |
| | | label: 'æ£æµæ¥å/èµè´¨æä»¶', |
| | | component: 'FileUpload', |
| | | componentProps: { |
| | | valueKey: 'id', |
| | | maxNumber: 10, |
| | | multiple: true, |
| | | 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: 'testType', |
| | | label: 'æ£æµç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_QC_OUTSOURCE_TEST_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©æ£æµç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'result', |
| | | label: 'æ£æµç»æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_QC_OUTSOURCE_TEST_RESULT, 'number'), |
| | | placeholder: 'è¯·éæ©æ£æµç»æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: [ |
| | | { label: 'è稿', value: TEST_STATUS.DRAFT }, |
| | | { label: 'å·²æäº¤', value: TEST_STATUS.SUBMITTED }, |
| | | { label: '已宿', value: TEST_STATUS.FINISHED }, |
| | | ], |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'testDate', |
| | | label: 'æ£æµæ¥æ', |
| | | component: 'RangePicker', |
| | | componentProps: getRangePickerDefaultProps(), |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çè¡¨æ ¼å */ |
| | | 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: 'testType', |
| | | title: 'æ£æµç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_QC_OUTSOURCE_TEST_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'itemName', |
| | | title: 'æ£æµå¯¹è±¡', |
| | | minWidth: 140, |
| | | }, |
| | | { |
| | | field: 'testOrg', |
| | | title: 'æ£æµæºæ', |
| | | minWidth: 160, |
| | | }, |
| | | { |
| | | field: 'testDate', |
| | | title: 'æ£æµæ¥æ', |
| | | width: 170, |
| | | formatter: 'formatDateTime', |
| | | }, |
| | | { |
| | | field: 'result', |
| | | title: 'æ£æµç»æ', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_QC_OUTSOURCE_TEST_RESULT }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | field: 'action', |
| | | title: 'æä½', |
| | | width: 260, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesQcOutsourceTestApi } from '#/api/mes/qc/outsourcetest'; |
| | | |
| | | 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 { |
| | | deleteTest, |
| | | finishTest, |
| | | getTestPage, |
| | | submitTest, |
| | | } from '#/api/mes/qc/outsourcetest'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { TEST_STATUS, useGridColumns, useGridFormSchema } from './data'; |
| | | import Form from './modules/form.vue'; |
| | | |
| | | /** MES å§å¤æ£æµ/è¯éªåå表 */ |
| | | defineOptions({ name: 'MesQcOutsourceTest' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: Form, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: MesQcOutsourceTestApi.OutsourceTest) { |
| | | formModalApi.setData({ formType: 'edit', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleDetail(row: MesQcOutsourceTestApi.OutsourceTest) { |
| | | 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 deleteTest(ids); |
| | | message.success($t('ui.actionMessage.deleteSuccess')); |
| | | handleRefresh(); |
| | | } finally { |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | async function handleSubmit(row: MesQcOutsourceTestApi.OutsourceTest) { |
| | | await submitTest(row.id!); |
| | | message.success('å·²æäº¤'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | async function handleFinish(row: MesQcOutsourceTestApi.OutsourceTest) { |
| | | await finishTest(row.id!); |
| | | message.success('已宿'); |
| | | handleRefresh(); |
| | | } |
| | | |
| | | const checkedIds = ref<number[]>([]); |
| | | function handleRowCheckboxChange({ |
| | | records, |
| | | }: { |
| | | records: MesQcOutsourceTestApi.OutsourceTest[]; |
| | | }) { |
| | | 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 getTestPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }); |
| | | }, |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesQcOutsourceTestApi.OutsourceTest>, |
| | | 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:qc-outsource-test:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | { |
| | | label: 'æ¹éå é¤', |
| | | type: 'primary', |
| | | danger: true, |
| | | disabled: isEmpty(checkedIds), |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:qc-outsource-test: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 === TEST_STATUS.DRAFT" color="default">è稿</Tag> |
| | | <Tag v-else-if="row.status === TEST_STATUS.SUBMITTED" color="processing">å·²æäº¤</Tag> |
| | | <Tag v-else-if="row.status === TEST_STATUS.FINISHED" color="success">已宿</Tag> |
| | | </template> |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: $t('common.detail'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.VIEW, |
| | | auth: ['mes:qc-outsource-test:query'], |
| | | onClick: handleDetail.bind(null, row), |
| | | }, |
| | | { |
| | | label: $t('common.edit'), |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:qc-outsource-test:update'], |
| | | ifShow: row.status === TEST_STATUS.DRAFT, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'æäº¤', |
| | | type: 'link', |
| | | auth: ['mes:qc-outsource-test:update'], |
| | | ifShow: row.status === TEST_STATUS.DRAFT, |
| | | popConfirm: { |
| | | title: '确认æäº¤è¯¥åæ®ï¼', |
| | | confirm: handleSubmit.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: '宿', |
| | | type: 'link', |
| | | auth: ['mes:qc-outsource-test:update'], |
| | | ifShow: row.status === TEST_STATUS.SUBMITTED, |
| | | popConfirm: { |
| | | title: 'ç¡®è®¤å®æè¯¥åæ®ï¼', |
| | | confirm: handleFinish.bind(null, row), |
| | | }, |
| | | }, |
| | | { |
| | | label: $t('common.delete'), |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:qc-outsource-test:delete'], |
| | | ifShow: row.status === TEST_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 { MesQcOutsourceTestApi } from '#/api/mes/qc/outsourcetest'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { createTest, getTest, updateTest } from '#/api/mes/qc/outsourcetest'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import type { FormType } from '../data'; |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | const formData = ref<MesQcOutsourceTestApi.OutsourceTest>(); |
| | | 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: 140, |
| | | }, |
| | | 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 MesQcOutsourceTestApi.OutsourceTest; |
| | | try { |
| | | await (formType.value === 'create' |
| | | ? createTest(data) |
| | | : updateTest(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 getTest(data.id); |
| | | await formApi.setValues(formData.value); |
| | | if (formData.value?.attachmentList?.length) { |
| | | const blobIds = formData.value.attachmentList.map((item) => ({ |
| | | uid: String(item.id), |
| | | name: item.name || '', |
| | | url: item.url || '', |
| | | status: 'done', |
| | | id: item.id, |
| | | })); |
| | | await formApi.setFieldValue('blobIds', blobIds); |
| | | } |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-2/3" :show-confirm-button="formType !== 'detail'"> |
| | | <Form class="mx-3" /> |
| | | </Modal> |
| | | </template> |
| | |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'issueType', |
| | | label: 'åæ®ç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_WM_PRODUCT_ISSUE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©åæ®ç±»å', |
| | | }, |
| | | dependencies: { |
| | | triggerFields: ['status'], |
| | | show: (values) => values.status === undefined || values.status === 0, |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'requiredTime', |
| | | label: 'éæ±æ¶é´', |
| | | component: 'DatePicker', |
| | |
| | | minWidth: 150, |
| | | }, |
| | | { |
| | | field: 'issueType', |
| | | title: 'åæ®ç±»å', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_WM_PRODUCT_ISSUE_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'workOrderCode', |
| | | title: 'ç产订å', |
| | | minWidth: 140, |
| | |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesWmStockTakingTaskApi } from '#/api/mes/wm/stocktaking/task'; |
| | | |
| | | import { ref } from 'vue'; |
| | | |
| | | import { Page, useVbenModal } from '@vben/common-ui'; |
| | | import { MesWmStockTakingTaskStatusEnum } from '@vben/constants'; |
| | | import { downloadFileFromBlobPart } from '@vben/utils'; |
| | |
| | | exportStockTaking, |
| | | getStockTakingPage, |
| | | } from '#/api/mes/wm/stocktaking/task'; |
| | | import { |
| | | importStockTakingQuantity, |
| | | importStockTakingQuantityTemplate, |
| | | } from '#/api/wls/stocktaking/task/line'; |
| | | import { $t } from '#/locales'; |
| | | |
| | | import { useGridColumns, useGridFormSchema } from './data'; |
| | |
| | | downloadFileFromBlobPart({ fileName: 'çç¹ä»»å¡.xls', source: data }); |
| | | } |
| | | |
| | | /** ä¸è½½çç¹å¯¼å
¥æ¨¡æ¿ */ |
| | | async function handleImportTemplate() { |
| | | const data = await importStockTakingQuantityTemplate(); |
| | | downloadFileFromBlobPart({ fileName: 'çç¹å®çæ°å¯¼å
¥æ¨¡æ¿.xls', source: data }); |
| | | } |
| | | |
| | | const importFileRef = ref<HTMLInputElement>(); |
| | | const importTaskId = ref<number>(); |
| | | |
| | | /** 触å导å
¥æä»¶éæ© */ |
| | | function handleImport(row: MesWmStockTakingTaskApi.StockTakingTask) { |
| | | importTaskId.value = row.id!; |
| | | importFileRef.value?.click(); |
| | | } |
| | | |
| | | /** 导å
¥å®çæ°æä»¶éæ©åä¸ä¼ */ |
| | | async function handleImportFileChange(event: Event) { |
| | | const file = (event.target as HTMLInputElement).files?.[0]; |
| | | if (!file || !importTaskId.value) { |
| | | return; |
| | | } |
| | | const hideLoading = message.loading({ content: 'æ£å¨å¯¼å
¥å®çæ°...', duration: 0 }); |
| | | try { |
| | | const result = await importStockTakingQuantity(importTaskId.value, file); |
| | | message.success(`导å
¥å®æï¼æå ${result.successCount ?? 0} æ¡ï¼å¤±è´¥ ${result.failureCount ?? 0} æ¡`); |
| | | if (result.failureMessages?.length) { |
| | | message.warning(result.failureMessages.slice(0, 5).join('ï¼')); |
| | | } |
| | | handleRefresh(); |
| | | } finally { |
| | | (event.target as HTMLInputElement).value = ''; |
| | | hideLoading(); |
| | | } |
| | | } |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | |
| | | auth: ['mes:wm-stock-taking-task:export'], |
| | | onClick: handleExport, |
| | | }, |
| | | { |
| | | label: '导å
¥æ¨¡æ¿', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.DOWNLOAD, |
| | | auth: ['mes:wm-stock-taking-task:query'], |
| | | onClick: handleImportTemplate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | |
| | | onClick: handleExecute.bind(null, row), |
| | | }, |
| | | { |
| | | label: '导å
¥å®çæ°', |
| | | type: 'link', |
| | | icon: ACTION_ICON.UPLOAD, |
| | | auth: ['mes:wm-stock-taking-task:update'], |
| | | ifShow: row.status === StatusEnum.APPROVING, |
| | | onClick: handleImport.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'åæ¶', |
| | | type: 'link', |
| | | danger: true, |
| | |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | <input |
| | | ref="importFileRef" |
| | | class="hidden" |
| | | type="file" |
| | | accept=".xls,.xlsx" |
| | | @change="handleImportFileChange" |
| | | /> |
| | | </Page> |
| | | </template> |
| | |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'type', |
| | | label: 'åºåºç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_WM_WAREHOUSE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©åºåºç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'area', |
| | | label: 'é¢ç§¯ï¼ã¡ï¼', |
| | | component: 'InputNumber', |
| | |
| | | minWidth: 160, |
| | | }, |
| | | { |
| | | field: 'type', |
| | | title: 'åºåºç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_WM_WAREHOUSE_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'area', |
| | | title: 'é¢ç§¯ï¼ã¡ï¼', |
| | | width: 100, |
| | |
| | | import { h } from 'vue'; |
| | | |
| | | import { DICT_TYPE, MesAutoCodeRuleCode } from '@vben/constants'; |
| | | import { getDictOptions } from '@vben/hooks'; |
| | | |
| | | import { Button } from 'ant-design-vue'; |
| | | |
| | |
| | | placeholder: '请è¾å
¥ä»åºåç§°', |
| | | }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'type', |
| | | label: 'ä»åºç±»å', |
| | | component: 'Select', |
| | | componentProps: { |
| | | options: getDictOptions(DICT_TYPE.MES_WM_WAREHOUSE_TYPE, 'number'), |
| | | placeholder: 'è¯·éæ©ä»åºç±»å', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'chargeUserId', |
| | |
| | | minWidth: 160, |
| | | }, |
| | | { |
| | | field: 'type', |
| | | title: 'ä»åºç±»å', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_WM_WAREHOUSE_TYPE }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'address', |
| | | title: 'ä»åºå°å', |
| | | minWidth: 180, |
| | |
| | | proxy: { |
| | | "/admin-api": { |
| | | changeOrigin: true, |
| | | target: "http://192.168.0.10:48080", |
| | | target: "http://127.0.0.1:48080", |
| | | ws: true, |
| | | }, |
| | | }, |