全新的连接配置页面采用现代化设计,使用分组表单卡片 + 右侧摘要 + 底部粘性操作栏的布局,提供直观的配置管理体验。
src/
├── api/mock/
│ └── connection.js # Mock API 接口
├── views/config/connectconf/
│ └── index.vue # 连接配置主页面
└── components/ # 通用组件
├── XtGroupedFormCard/ # 分组表单卡片
├── XtRightSummaryPanel/ # 右侧摘要面板
├── XtStickyActionBar/ # 底部粘性操作栏
├── XtTestConnectionDrawer/ # 测试连接抽屉
└── XtDiffPublishDialog/ # 差异发布对话框
graph TD
A[连接配置页面] --> B[分组表单卡片]
A --> C[右侧摘要面板]
A --> D[底部操作栏]
A --> E[测试连接抽屉]
A --> F[发布确认对话框]
B --> B1[基础网络]
B --> B2[MQTT配置]
B --> B3[HTTP/回调]
B --> B4[代理与安全]
C --> C1[统计信息]
C --> C2[配置项列表]
C --> C3[快速操作]
D --> D1[保存草稿]
D --> D2[测试全部]
D --> D3[发布变更]
D --> D4[回滚配置]
// 大屏双列布局
@media (min-width: 1440px) {
.form-groups {
grid-template-columns: 1fr 1fr;
}
}
// 中屏隐藏右侧面板
@media (max-width: 1279px) {
.desktop-summary {
display: none;
}
.mobile-only {
display: block;
}
}
// 移动端优化
@media (max-width: 768px) {
.page-header {
padding: var(--spacing-4);
}
.form-groups {
gap: var(--spacing-4);
}
}
| 字段名 | 类型 | 必填 | 校验规则 | 说明 |
|---|---|---|---|---|
| toolIp | String | ✅ | IP格式 | 工具机IP地址 |
| serverIp | String | ✅ | IP格式 | 服务器IP地址 |
| enableProxy | Boolean | - | - | 是否启用代理 |
| netProxy | String | - | URL格式 | 网络代理地址 |
| 字段名 | 类型 | 必填 | 校验规则 | 说明 |
|---|---|---|---|---|
| mqttBroker | String | ✅ | MQTT URL格式 | MQTT代理地址 |
| mqttProductId | String | ✅ | 3-50字符 | 产品ID |
| mqttDeviceId | String | ✅ | 3-50字符 | 设备ID |
| mqttSecret | String | ✅ | 最少8位 | 设备密钥(敏感字段) |
| 字段名 | 类型 | 必填 | 校验规则 | 说明 |
|---|---|---|---|---|
| httpBase | String | ✅ | HTTP/HTTPS URL | HTTP基础地址 |
| httpTimeout | Number | ✅ | 1000-300000 | 超时时间(毫秒) |
| 字段名 | 类型 | 必填 | 校验规则 | 说明 |
|---|---|---|---|---|
| packetFilter | Boolean | - | - | 数据包过滤开关 |
| certFile | File | - | 证书格式,<2MB | 证书文件 |
const ipPattern = /^(25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/
const urlPattern = /^https?:\/\/.+/
const mqttPattern = /^mqtt:\/\/.+:\d+$/
const portRange = { min: 1, max: 65535 }
sequenceDiagram
participant U as 用户
participant P as 页面
participant A as Mock API
U->>P: 访问连接配置页面
P->>A: 调用 getConfig()
A-->>P: 返回当前配置和草稿
P->>P: 初始化表单数据
P->>P: 计算变更状态
P-->>U: 显示配置界面
sequenceDiagram
participant U as 用户
participant P as 页面
participant S as 摘要面板
U->>P: 修改配置字段
P->>P: 触发字段验证
P->>P: 计算变更差异
P->>S: 更新摘要统计
S-->>U: 实时显示变更状态
sequenceDiagram
participant U as 用户
participant P as 页面
participant D as 测试抽屉
participant A as Mock API
U->>P: 点击"测试该组"
P->>D: 打开测试抽屉
P->>A: 调用 testConnection()
A-->>P: 返回测试日志
P->>D: 显示时间轴日志
D-->>U: 展示测试结果
sequenceDiagram
participant U as 用户
participant P as 页面
participant Diff as 差异对话框
participant A as Mock API
U->>P: 点击"发布"
P->>P: 生成配置差异
P->>Diff: 显示差异对话框
U->>Diff: 选择要发布的字段
Diff->>A: 调用 publishConfig()
A-->>Diff: 返回发布结果
Diff->>P: 更新原始数据
P-->>U: 显示发布成功
// 密钥字段默认掩码显示
showSecrets: {
mqttSecret: false // 默认隐藏
}
// 点击眼睛图标切换显示
toggleSecret(field) {
this.showSecrets[field] = !this.showSecrets[field]
}
// 实时计算变更数量
dirtyCount() {
let count = 0
Object.keys(this.formData).forEach(key => {
if (JSON.stringify(this.formData[key]) !== JSON.stringify(this.originalData[key])) {
count++
}
})
return count
}
// 统计表单验证错误
errorCount() {
let count = 0
const forms = ['networkForm', 'mqttForm', 'httpForm', 'securityForm']
forms.forEach(formName => {
if (this.$refs[formName]) {
this.$refs[formName].fields.forEach(field => {
if (field.validateState === 'error') {
count++
}
})
}
})
return count
}
| 接口名称 | 功能 | 延迟 | 失败率 |
|---|---|---|---|
getConfig() |
获取配置 | 300ms | 0% |
saveDraft() |
保存草稿 | 500ms | 10% |
publishConfig() |
发布配置 | 1000ms | 5% |
rollbackConfig() |
回滚配置 | 800ms | 3% |
testConnection() |
测试连接 | 800-1500ms | 动态 |
{
ts: 1640995200000, // 时间戳
step: '连接数据库', // 步骤名称
status: 'ok', // 状态: 'ok'|'warn'|'fail'
cost: 120, // 耗时(ms)
tip: '连接成功', // 提示信息
details: '详细日志信息...' // 详细信息(可选)
}
{
key: 'database.host', // 配置键
name: '数据库主机', // 显示名称
path: 'config.database', // 配置路径
oldValue: 'localhost', // 原值
newValue: '192.168.1.100', // 新值
isChanged: true, // 是否变更
isNew: false, // 是否新增
isDeleted: false, // 是否删除
description: '主机地址变更' // 变更说明
}
<template>
<!-- 页面会自动加载配置数据 -->
<div>
<!-- 修改配置字段会自动触发变更检测 -->
<!-- 右侧摘要面板会实时更新统计信息 -->
<!-- 底部操作栏会显示变更和错误数量 -->
</div>
</template>
// 测试单个分组
await this.testGroup('基础网络')
// 测试所有分组
await this.handleTestAll()
// 获取配置差异
const diffs = getConfigDiffs(this.originalData, this.formData)
// 发布选中的配置
await publishConfig(['toolIp', 'serverIp'])
所有组件都自动支持明暗主题切换:
// 切换主题
window.toggleTheme()
// 设置特定主题
window.setTheme('dark') // 或 'light', 'auto'
<XtGroupedFormCard
title="新分组"
desc="新分组的描述"
@test="() => testGroup('新分组')"
@reset="() => resetGroup('newGroup')"
>
<!-- 表单内容 -->
</XtGroupedFormCard>
formRules: {
newField: [
{ required: true, message: '请输入新字段', trigger: 'blur' },
{ validator: customValidator, trigger: 'blur' }
]
}
// 在 mock/connection.js 中添加
const testSteps = {
'新分组': [
{
name: '检查新配置',
getTip: (status, config) => {
return status === 'ok' ? '配置正确' : '配置错误'
}
}
]
}
A: 确保在 handleFieldChange 方法中正确映射了表单引用:
const formMap = {
newField: 'newForm' // 添加新字段映射
}
A: 在 showSecrets 对象中添加字段,并在模板中使用 show-password 属性:
showSecrets: {
newSecret: false
}
A: 检查响应式断点设置,确保 isMobile 计算属性正确:
isMobile() {
return this.windowWidth < 1280
}
如有任何问题或建议,请联系前端开发团队。
Happy Coding! 🎉