| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 }, |
| | | }); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 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; |
| | | } |
| | | } |
| | | |
| | | /** æåæ°éæ°æ®å¹¶å
¥åºï¼è¿ 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', |
| | | ); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 { 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); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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 } from '@vben/common-ui'; |
| | | |
| | | import { DatePicker, Select, Spin } from 'ant-design-vue'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | import { 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' }, |
| | | ]; |
| | | |
| | | async function loadData() { |
| | | loading.value = true; |
| | | try { |
| | | forecastData.value = await getForecastList(forecastCode.value); |
| | | } catch { |
| | | forecastData.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="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> |
| | | </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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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, |
| | | 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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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, ref } 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 [LatestGrid, latestGridApi] = useVbenVxeGrid({ |
| | | gridOptions: { |
| | | columns: useLatestColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | 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: 'auto', |
| | | 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) { |
| | | if (checked) { |
| | | refreshTimer = setInterval(() => { |
| | | if (activeTab.value === 'latest') { |
| | | latestGridApi.query(); |
| | | } |
| | | }, 30_000); |
| | | } else if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | refreshTimer = undefined; |
| | | } |
| | | } |
| | | |
| | | onBeforeUnmount(() => { |
| | | if (refreshTimer) { |
| | | clearInterval(refreshTimer); |
| | | } |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <div class="p-4"> |
| | | <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"> |
| | | <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> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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 { 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> |