feat(tools): 新增台账管理功能模块
- 添加 addLedger API 接口定义和实现 - 创建工程配置表单组件 (EngineeringForm) - 创建设备配置表单组件 (EquipmentForm) - 创建项目和测点表单组件 (ProjectForm, LineForm) - 实现台账树形结构面板 (LedgerTreePanel) - 添加台账数据验证契约检查脚本 - 集成字典选项和动态表单验证功能 - 实现台账节点增删改查完整流程 - 优化 Echarts 图表组件分组渲染性能
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
/* eslint-env node */
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const currentDir = path.dirname(fileURLToPath(import.meta.url))
|
||||
const apiFile = path.join(currentDir, 'index.ts')
|
||||
const interfaceFile = path.join(currentDir, 'interface', 'index.ts')
|
||||
|
||||
const apiSource = fs.readFileSync(apiFile, 'utf8')
|
||||
const interfaceSource = fs.readFileSync(interfaceFile, 'utf8')
|
||||
|
||||
const expectations = [
|
||||
['equipment payload maps devType', /devType:\s*params\.dev_type/],
|
||||
['equipment payload maps devModel', /devModel:\s*params\.dev_model/],
|
||||
['equipment payload maps devAccessMethod', /devAccessMethod:\s*params\.dev_access_method/],
|
||||
['equipment payload maps nodeId', /nodeId:\s*params\.node_id/],
|
||||
['equipment payload maps nodeProcess', /nodeProcess:\s*resolveOptionalNumber\(params\.node_process\)/],
|
||||
['line payload maps lineId', /lineId:\s*resolveOptionalText\(params\.line_id\s*\|\|\s*params\.id\)/],
|
||||
['line payload maps lineNo', /lineNo:\s*params\.line_no/],
|
||||
['line payload maps volGrade', /volGrade:\s*params\.vol_grade/],
|
||||
['line payload maps ctRatio', /ctRatio:\s*params\.ct_ratio/],
|
||||
['line payload maps isGovern', /isGovern:\s*params\.is_govern/],
|
||||
['tree node supports parentId', /parentId\?:\s*string/],
|
||||
['tree node supports parentIds', /parentIds\?:\s*string/],
|
||||
['delete response type is boolean', /requestAddLedger<boolean>\('delete',\s*'\/node'/]
|
||||
]
|
||||
|
||||
const failures = expectations.filter(([, pattern]) => !pattern.test(`${apiSource}\n${interfaceSource}`))
|
||||
|
||||
if (failures.length) {
|
||||
console.error('addLedger API_DEBUG contract check failed:')
|
||||
for (const [name] of failures) {
|
||||
console.error(`- ${name}`)
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log('addLedger API_DEBUG contract check passed')
|
||||
172
frontend/src/api/tools/addLedger/index.ts
Normal file
172
frontend/src/api/tools/addLedger/index.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import http from '@/api'
|
||||
import type { ResultData } from '@/api/interface'
|
||||
import type { AddLedger } from './interface'
|
||||
|
||||
type AddLedgerRequestMethod = 'get' | 'post' | 'delete'
|
||||
|
||||
const ADD_LEDGER_ROUTE_PATHS = ['/addLedger', '/api/addLedger'] as const
|
||||
const ADD_LEDGER_BASE_URL = String(import.meta.env.VITE_API_URL || '').trim()
|
||||
|
||||
const resolveOptionalText = (value: unknown) => {
|
||||
if (value === null || value === undefined) return undefined
|
||||
const text = String(value).trim()
|
||||
return text || undefined
|
||||
}
|
||||
|
||||
const resolveOptionalNumber = (value: unknown) => {
|
||||
if (value === null || value === undefined || value === '') return undefined
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : undefined
|
||||
}
|
||||
|
||||
const toAddLedgerProjectPayload = (params: AddLedger.ProjectForm) => ({
|
||||
id: resolveOptionalText(params.id),
|
||||
engineeringId: resolveOptionalText(params.engineeringId || params.parentId),
|
||||
name: params.name,
|
||||
area: params.area,
|
||||
description: params.description
|
||||
})
|
||||
|
||||
const toAddLedgerEquipmentPayload = (params: AddLedger.EquipmentForm) => ({
|
||||
id: resolveOptionalText(params.id),
|
||||
projectId: resolveOptionalText(params.projectId || params.parentId),
|
||||
name: params.name,
|
||||
ndid: params.ndid,
|
||||
mac: params.mac,
|
||||
devType: params.dev_type,
|
||||
devModel: params.dev_model,
|
||||
devAccessMethod: params.dev_access_method,
|
||||
nodeId: params.node_id,
|
||||
nodeProcess: resolveOptionalNumber(params.node_process),
|
||||
upgrade: params.upgrade
|
||||
})
|
||||
|
||||
const toAddLedgerLinePayload = (params: AddLedger.LineForm) => ({
|
||||
lineId: resolveOptionalText(params.line_id || params.id),
|
||||
deviceId: resolveOptionalText(params.deviceId || params.parentId),
|
||||
name: params.name,
|
||||
lineNo: params.line_no,
|
||||
conType: params.conType,
|
||||
volGrade: params.vol_grade,
|
||||
position: params.position,
|
||||
ctRatio: params.ct_ratio,
|
||||
ct2Ratio: params.ct2_ratio,
|
||||
ptRatio: params.pt_ratio,
|
||||
pt2Ratio: params.pt2_ratio,
|
||||
shortCircuitCapacity: params.short_circuit_capacity,
|
||||
basicCapacity: params.basic_capacity,
|
||||
protocolCapacity: params.protocol_capacity,
|
||||
devCapacity: params.dev_capacity,
|
||||
monitorObj: params.monitor_obj,
|
||||
isGovern: params.is_govern,
|
||||
monitorUser: params.monitor_user,
|
||||
isImportant: params.is_important
|
||||
})
|
||||
|
||||
const resolveDevProxyTarget = () => {
|
||||
const proxyConfig = import.meta.env.VITE_PROXY
|
||||
if (!Array.isArray(proxyConfig)) return ''
|
||||
|
||||
const matchedProxy = proxyConfig.find(item => Array.isArray(item) && item[0] === '/api')
|
||||
if (!matchedProxy?.[1]) return ''
|
||||
|
||||
return String(matchedProxy[1]).replace(/\/+$/, '')
|
||||
}
|
||||
|
||||
const buildAddLedgerRequestPaths = (path: string) => {
|
||||
const requestPaths = new Set<string>()
|
||||
const devProxyTarget = resolveDevProxyTarget()
|
||||
|
||||
for (const routePath of ADD_LEDGER_ROUTE_PATHS) {
|
||||
if (ADD_LEDGER_BASE_URL === '/api' && routePath.startsWith('/api/')) {
|
||||
if (devProxyTarget) {
|
||||
requestPaths.add(`${devProxyTarget}${routePath}${path}`)
|
||||
}
|
||||
|
||||
requestPaths.add(`${window.location.origin}${routePath}${path}`)
|
||||
continue
|
||||
}
|
||||
|
||||
requestPaths.add(`${routePath}${path}`)
|
||||
}
|
||||
|
||||
return Array.from(requestPaths)
|
||||
}
|
||||
|
||||
const isFallbackableAddLedgerError = (error: unknown) => {
|
||||
const responseCode = typeof error === 'object' && error !== null && 'code' in error ? String(error.code) : ''
|
||||
const responseMessage = typeof error === 'object' && error !== null && 'message' in error ? String(error.message) : ''
|
||||
const normalizedMessage = responseMessage.toLowerCase()
|
||||
|
||||
return (
|
||||
responseCode === '404' ||
|
||||
normalizedMessage.includes('unknown operate') ||
|
||||
normalizedMessage.includes('not found') ||
|
||||
normalizedMessage.includes('no handler found')
|
||||
)
|
||||
}
|
||||
|
||||
const requestAddLedger = async <T>(
|
||||
method: AddLedgerRequestMethod,
|
||||
path: string,
|
||||
params?: object
|
||||
): Promise<ResultData<T>> => {
|
||||
let lastError: unknown
|
||||
const requestPaths = buildAddLedgerRequestPaths(path)
|
||||
|
||||
for (let index = 0; index < requestPaths.length; index += 1) {
|
||||
const requestPath = requestPaths[index]
|
||||
|
||||
try {
|
||||
if (method === 'get') {
|
||||
return await http.get<T>(requestPath, params)
|
||||
}
|
||||
|
||||
if (method === 'delete') {
|
||||
return await http.delete<T>(requestPath, params)
|
||||
}
|
||||
|
||||
return await http.post<T>(requestPath, params)
|
||||
} catch (error) {
|
||||
lastError = error
|
||||
|
||||
if (index === requestPaths.length - 1 || !isFallbackableAddLedgerError(error)) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError
|
||||
}
|
||||
|
||||
export const getAddLedgerTree = () => {
|
||||
return requestAddLedger<AddLedger.LedgerTreeNode[]>('get', '/tree')
|
||||
}
|
||||
|
||||
export const getAddLedgerDetail = (params: AddLedger.DetailParams) => {
|
||||
return requestAddLedger<AddLedger.NodeDetail>('get', '/detail', params)
|
||||
}
|
||||
|
||||
export const saveAddLedgerEngineering = (params: AddLedger.EngineeringForm) => {
|
||||
return requestAddLedger<AddLedger.EngineeringForm>('post', '/engineering/save', params)
|
||||
}
|
||||
|
||||
export const saveAddLedgerProject = (params: AddLedger.ProjectForm) => {
|
||||
return requestAddLedger<AddLedger.ProjectForm>('post', '/project/save', toAddLedgerProjectPayload(params))
|
||||
}
|
||||
|
||||
export const saveAddLedgerEquipment = (params: AddLedger.EquipmentForm) => {
|
||||
return requestAddLedger<AddLedger.EquipmentForm>('post', '/equipment/save', toAddLedgerEquipmentPayload(params))
|
||||
}
|
||||
|
||||
export const saveAddLedgerLine = (params: AddLedger.LineForm) => {
|
||||
return requestAddLedger<AddLedger.LineForm>('post', '/line/save', toAddLedgerLinePayload(params))
|
||||
}
|
||||
|
||||
export const getAvailableLineNos = (params: AddLedger.AvailableLineNoParams) => {
|
||||
return requestAddLedger<number[]>('get', '/line/availableLineNos', params)
|
||||
}
|
||||
|
||||
export const deleteAddLedgerNode = (params: AddLedger.DeleteNodeParams) => {
|
||||
return requestAddLedger<boolean>('delete', '/node', params)
|
||||
}
|
||||
110
frontend/src/api/tools/addLedger/interface/index.ts
Normal file
110
frontend/src/api/tools/addLedger/interface/index.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
export namespace AddLedger {
|
||||
export type NodeLevel = 0 | 1 | 2 | 3
|
||||
|
||||
export interface LedgerTreeNode {
|
||||
id?: string
|
||||
Id?: string
|
||||
pid?: string
|
||||
Pid?: string
|
||||
pids?: string
|
||||
Pids?: string
|
||||
parentId?: string
|
||||
parentIds?: string
|
||||
name?: string
|
||||
Name?: string
|
||||
level?: NodeLevel
|
||||
Level?: NodeLevel
|
||||
state?: number
|
||||
State?: number
|
||||
children?: LedgerTreeNode[]
|
||||
}
|
||||
|
||||
export interface NormalizedTreeNode {
|
||||
id: string
|
||||
pid: string
|
||||
pids: string
|
||||
name: string
|
||||
level: NodeLevel
|
||||
children: NormalizedTreeNode[]
|
||||
raw: LedgerTreeNode
|
||||
}
|
||||
|
||||
export interface DetailParams {
|
||||
id: string
|
||||
level: NodeLevel
|
||||
}
|
||||
|
||||
export interface EngineeringForm {
|
||||
id?: string
|
||||
name: string
|
||||
province?: string
|
||||
city?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface ProjectForm {
|
||||
id?: string
|
||||
engineeringId?: string
|
||||
parentId?: string
|
||||
name: string
|
||||
area?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface EquipmentForm {
|
||||
id?: string
|
||||
engineeringId?: string
|
||||
projectId?: string
|
||||
parentId?: string
|
||||
name: string
|
||||
ndid: string
|
||||
mac: string
|
||||
dev_type?: string
|
||||
dev_model: string
|
||||
dev_access_method?: string
|
||||
node_id?: string
|
||||
node_process?: string
|
||||
upgrade?: number
|
||||
}
|
||||
|
||||
export interface LineForm {
|
||||
id?: string
|
||||
line_id?: string
|
||||
deviceId?: string
|
||||
parentId?: string
|
||||
name: string
|
||||
line_no?: number
|
||||
conType?: number
|
||||
vol_grade?: number
|
||||
position?: string
|
||||
ct_ratio?: number
|
||||
ct2_ratio?: number
|
||||
pt_ratio?: number
|
||||
pt2_ratio?: number
|
||||
short_circuit_capacity?: number
|
||||
basic_capacity?: number
|
||||
protocol_capacity?: number
|
||||
dev_capacity?: number
|
||||
monitor_obj?: string
|
||||
is_govern?: number
|
||||
monitor_user?: string
|
||||
is_important?: number
|
||||
}
|
||||
|
||||
export type NodeDetail = EngineeringForm | ProjectForm | EquipmentForm | LineForm
|
||||
|
||||
export interface AvailableLineNoParams {
|
||||
deviceId: string
|
||||
lineId?: string
|
||||
}
|
||||
|
||||
export interface DeleteNodeParams {
|
||||
id: string
|
||||
level: NodeLevel
|
||||
}
|
||||
|
||||
export interface SelectOption<T = string | number> {
|
||||
label: string
|
||||
value: T
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user