feat(auth): 统一数据库运维菜单路由并添加装置单位及监测点限值配置功能

- 统一数据库监控菜单路径到 /system-ops/dbms 入口
- 添加 isDbmsMenu 函数处理多种数据库菜单路径匹配
- 在动态路由中增加多个数据库监控路径的重定向规则
- 添加设备单位配置功能包括新增 EquipmentUnitForm 接口定义
- 添加监测点限值配置功能包括新增 OverlimitDetail 接口定义
- 在装置表单中添加单位配置按钮并集成单位调试功能
- 在监测点表单中添加限值配置按钮并集成限值调试功能
- 添加电压等级变更时的默认容量和变比同步逻辑
- 配置监测点表单中的线路类型选择选项
- 添加装置表单中比率输入组的高度紧凑样式
- 新增数据库运维静态路由配置和别名支持
This commit is contained in:
2026-05-29 15:10:14 +08:00
parent 055e69fff7
commit d055a8e1a0
33 changed files with 3790 additions and 32 deletions

View File

@@ -0,0 +1,47 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const currentDir = path.dirname(fileURLToPath(import.meta.url))
const pageDir = path.join(currentDir, '..')
const dialogSource = fs.readFileSync(path.join(pageDir, 'components/DbmsConnectionDialog.vue'), 'utf8')
const payloadSource = fs.readFileSync(path.join(pageDir, 'utils/taskPayload.ts'), 'utf8')
const checks = [
['dialog uses compact Navicat-like width', /width="630px"/.test(dialogSource)],
['dialog uses shared runtime size class', /class="dbms-connection-size-dialog"/.test(dialogSource)],
['dialog renders connection flow header', /class="connection-flow"/.test(dialogSource)],
['dialog keeps Basic connection type visible', /model-value="Basic"/.test(dialogSource)],
[
'dialog exposes service name and SID radio choices',
/el-radio[\s\S]*SERVICE_NAME[\s\S]*el-radio[\s\S]*SID/.test(dialogSource)
],
[
'dialog keeps only common connection fields in the visible form',
!/label="Schema"|label="Directory"|label="目录路径"|label="扩展配置"|label="备注"/.test(dialogSource)
],
[
'dialog keeps selected database type for payload',
/buildConnectionPayload\(form,\s*selectedDbType\.value\)/.test(dialogSource)
],
[
'dialog uses fixed viewport-relative height',
/:global\(\.dbms-connection-size-dialog\.el-dialog\)[\s\S]*height:\s*calc\(100vh - 170px\)/.test(dialogSource)
],
['dialog constrains height to viewport', /max-height:\s*calc\(100vh - 170px\)/.test(dialogSource)],
['dialog avoids large fixed bottom whitespace', !/margin:\s*0 auto 178px/.test(dialogSource)],
[
'new Oracle connection defaults service name to ORCL',
/serviceName:\s*resolveText\(record\?\.serviceName\)\s*\|\|\s*'ORCL'/.test(payloadSource)
]
]
const failures = checks.filter(([, passed]) => !passed)
if (failures.length) {
console.error('dbms connection dialog layout contract failed:')
failures.forEach(([message]) => console.error(`- ${message}`))
process.exit(1)
}
console.log('dbms connection dialog layout contract passed')

View File

@@ -0,0 +1,87 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const currentDir = path.dirname(fileURLToPath(import.meta.url))
const pageDir = path.join(currentDir, '..')
const missingFiles = []
const read = file => {
const filePath = path.join(pageDir, file)
if (!fs.existsSync(filePath)) {
missingFiles.push(file)
return ''
}
return fs.readFileSync(filePath, 'utf8')
}
const files = {
page: 'index.vue',
selector: 'components/DbmsConnectionTypeDialog.vue',
connectionDialog: 'components/DbmsConnectionDialog.vue',
taskPayload: 'utils/taskPayload.ts',
apiTypes: '../../../api/system/dbms/interface/index.ts'
}
const checks = [
[
'page renders connection type dialog',
/<DbmsConnectionTypeDialog[\s\S]*@next="handleSelectConnectionType"/.test(read(files.page))
],
[
'connect command opens type selector',
/\bconnect:\s*\(\)\s*=>\s*openConnectionTypeDialog\(\)/.test(read(files.page))
],
[
'new connection command opens type selector',
/\bnewConnection:\s*\(\)\s*=>\s*openConnectionTypeDialog\(\)/.test(read(files.page))
],
['selector defaults to Oracle', /selectedType\s*=\s*ref<Dbms\.DbType>\('ORACLE'\)/.test(read(files.selector))],
['selector width matches connection form dialog', /width="630px"/.test(read(files.selector))],
['selector uses shared runtime size class', /dbms-connection-size-dialog/.test(read(files.selector))],
['selector uses own stretch class', /dbms-connection-type-size-dialog/.test(read(files.selector))],
['selector height matches connection form dialog', /height:\s*calc\(100vh - 170px\)/.test(read(files.selector))],
[
'selector height constraint matches connection form dialog',
/max-height:\s*calc\(100vh - 170px\)/.test(read(files.selector))
],
[
'selector body stretches content area',
/dbms-connection-type-size-dialog \.el-dialog__body\)[\s\S]*display:\s*flex/.test(read(files.selector))
],
['selector content fills dialog body', /connection-type-dialog[\s\S]*flex:\s*1/.test(read(files.selector))],
['selector only exposes Oracle and MySQL', /type:\s*'ORACLE'[\s\S]*type:\s*'MYSQL'/.test(read(files.selector))],
['selector keeps next action explicit', /emit\('next',\s*selectedType\.value\)/.test(read(files.selector))],
[
'page blocks MySQL until backend is available',
/if\s*\(dbType\s*===\s*'MYSQL'\)[\s\S]*MySQL 连接配置暂未接入/.test(read(files.page))
],
['connection form displays selected database type', /selectedDbType/.test(read(files.connectionDialog))],
[
'connection form open accepts selected database type',
/open = \(nextMode: 'add' \| 'edit', record\?: Dbms\.ConnectionRecord, dbType: Dbms\.DbType = 'ORACLE'\)/.test(
read(files.connectionDialog)
)
],
[
'connection form payload uses selected database type',
/buildConnectionPayload\(form,\s*selectedDbType\.value\)/.test(read(files.connectionDialog))
],
[
'payload builder accepts database type',
/buildConnectionPayload[\s\S]*dbType: Dbms\.DbType = 'ORACLE'/.test(read(files.taskPayload))
],
['api type allows MySQL selection', /export type DbType = 'ORACLE' \| 'MYSQL'/.test(read(files.apiTypes))]
]
const failures = [
...missingFiles.map(file => [`required file exists: ${file}`, false]),
...checks.filter(([, passed]) => !passed)
]
if (failures.length) {
console.error('dbms connection type dialog contract failed:')
failures.forEach(([message]) => console.error(`- ${message}`))
process.exit(1)
}
console.log('dbms connection type dialog contract passed')

View File

@@ -0,0 +1,29 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const currentDir = path.dirname(fileURLToPath(import.meta.url))
const pageDir = path.join(currentDir, '..')
const read = file => fs.readFileSync(path.join(pageDir, file), 'utf8')
const pageSource = read('index.vue')
const onMountedBlock = pageSource.match(/onMounted\(\(\)\s*=>\s*\{[\s\S]*?\n\}\)/)?.[0] ?? ''
const checks = [
['page should not auto load dbms overview on menu open', !onMountedBlock.includes('loadOverview()')],
['page should not auto load dbms connections on menu open', !onMountedBlock.includes('loadConnections()')],
['page should not auto load dbms tasks on menu open', !onMountedBlock.includes('loadTasks()')],
['page should not auto load dbms files on menu open', !onMountedBlock.includes('loadFiles()')],
['connection tree keeps manual refresh entry', /<DbmsConnectionTree[\s\S]*@refresh="loadConnections"/.test(pageSource)]
]
const failures = checks.filter(([, passed]) => !passed)
if (failures.length) {
console.error('dbms initial api contract failed:')
failures.forEach(([message]) => console.error(`- ${message}`))
process.exit(1)
}
console.log('dbms initial api contract passed')

View File

@@ -0,0 +1,38 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const currentDir = path.dirname(fileURLToPath(import.meta.url))
const pageDir = path.join(currentDir, '..')
const read = file => fs.readFileSync(path.join(pageDir, file), 'utf8')
const files = {
page: 'index.vue',
toolbar: 'components/DbmsToolbar.vue',
tree: 'components/DbmsConnectionTree.vue',
workspace: 'components/DbmsWorkspace.vue'
}
const checks = [
['page renders dbms workbench shell', /class="dbms-workbench"/, read(files.page)],
['page uses top toolbar', /<DbmsToolbar[\s\S]*@command="handleToolbarCommand"/, read(files.page)],
['page uses collapsible connection tree', /<DbmsConnectionTree[\s\S]*:collapsed="treeCollapsed"/, read(files.page)],
['page uses right workspace', /<DbmsWorkspace[\s\S]*:active-section="activeSection"/, read(files.page)],
['toolbar exposes Navicat-like commands', /command:\s*'connect'[\s\S]*command:\s*'backup'[\s\S]*command:\s*'bi'/, read(files.toolbar)],
['connection tree supports collapse toggle', /emit\('toggle'\)/, read(files.tree)],
['connection tree groups tables and views', /type:\s*'tableGroup'[\s\S]*type:\s*'viewGroup'/, read(files.tree)],
['workspace shows table grid', /<el-table[\s\S]*:data="tables"/, read(files.workspace)],
['workspace keeps view empty state explicit', /暂无视图接口/, read(files.workspace)],
['workspace can open task panel', /<DbmsTaskPanel/, read(files.workspace)],
['workspace can open task status card', /<DbmsTaskStatusCard/, read(files.workspace)]
]
const failures = checks.filter(([, passed]) => !passed)
if (failures.length) {
console.error('dbms workbench layout contract failed:')
failures.forEach(([message]) => console.error(`- ${message}`))
process.exit(1)
}
console.log('dbms workbench layout contract passed')