21 KiB
Raw Permalink Blame History

前端UI参数配置指南

概述

本文档为前端开发人员提供二次回路巡检系统UI参数配置的详细指南包括表单字段、验证规则、数据格式和用户交互设计。


事件驱动配置表单

1. 基本信息字段

interface EventDrivenConfigForm {
  // 基本信息
  configName: string;                    // 配置名称 [必填, 最大100字符]
  inspectionPlanId: string;              // 巡检计划ID [必填]
  inspectionItemId?: string;             // 巡检子项ID [可选]
  
  // 触发配置
  triggerExpression: string;             // 触发表达式 [必填, 最大500字符]
  triggerConditionDescription?: string;  // 触发条件描述 [可选, 最大500字符]
  
  // 时间配置
  mandatoryWaitSeconds: number;          // 强制等待时间 [1-3600秒, 默认30]
  delayTriggerSeconds: number;           // 延时触发时间 [0-3600秒, 默认0]
  timeWindowSeconds: number;             // 时间窗口 [1-3600秒, 默认60]
  
  // 高级选项
  enableTrueToFalseTrigger: boolean;     // 支持true到false触发 [默认false]
  priority: 'Low' | 'Medium' | 'High';   // 优先级 [默认Medium]
  maxRetryCount: number;                 // 最大重试次数 [0-5, 默认1]
  executionTimeoutSeconds: number;       // 执行超时时间 [30-3600秒, 默认300]
  isActive: boolean;                     // 是否启用 [默认true]
}

2. 表单验证规则

const validationRules = {
  configName: [
    { required: true, message: '配置名称不能为空' },
    { max: 100, message: '配置名称长度不能超过100个字符' }
  ],
  
  inspectionPlanId: [
    { required: true, message: '请选择巡检计划' }
  ],
  
  triggerExpression: [
    { required: true, message: '触发表达式不能为空' },
    { max: 500, message: '触发表达式长度不能超过500个字符' },
    { validator: validateExpression, message: '表达式语法错误' }
  ],
  
  mandatoryWaitSeconds: [
    { type: 'number', min: 1, max: 3600, message: '强制等待时间必须在1-3600秒之间' }
  ],
  
  delayTriggerSeconds: [
    { type: 'number', min: 0, max: 3600, message: '延时触发时间必须在0-3600秒之间' }
  ],
  
  timeWindowSeconds: [
    { type: 'number', min: 1, max: 3600, message: '时间窗口必须在1-3600秒之间' }
  ]
};

3. UI组件配置

<template>
  <el-form :model="form" :rules="rules" ref="formRef" label-width="140px">
    <!-- 基本信息 -->
    <el-form-item label="配置名称" prop="configName">
      <el-input 
        v-model="form.configName" 
        placeholder="请输入配置名称"
        maxlength="100"
        show-word-limit
      />
    </el-form-item>
    
    <!-- 触发表达式编辑器 -->
    <el-form-item label="触发表达式" prop="triggerExpression">
      <expression-editor
        v-model="form.triggerExpression"
        :variables="availableVariables"
        @validate="onExpressionValidate"
        placeholder="例如: {16835_0} > 100 && {16836_0} < 200"
      />
      <div class="expression-help">
        <el-button type="text" @click="showExpressionHelp">表达式语法帮助</el-button>
        <el-button type="text" @click="testExpression">测试表达式</el-button>
      </div>
    </el-form-item>
    
    <!-- 时间配置 -->
    <el-row :gutter="20">
      <el-col :span="8">
        <el-form-item label="强制等待时间" prop="mandatoryWaitSeconds">
          <el-input-number
            v-model="form.mandatoryWaitSeconds"
            :min="1"
            :max="3600"
            controls-position="right"
          />
          <span class="unit">秒</span>
        </el-form-item>
      </el-col>
      
      <el-col :span="8">
        <el-form-item label="延时触发时间" prop="delayTriggerSeconds">
          <el-input-number
            v-model="form.delayTriggerSeconds"
            :min="0"
            :max="3600"
            controls-position="right"
          />
          <span class="unit">秒</span>
        </el-form-item>
      </el-col>
      
      <el-col :span="8">
        <el-form-item label="时间窗口" prop="timeWindowSeconds">
          <el-input-number
            v-model="form.timeWindowSeconds"
            :min="1"
            :max="3600"
            controls-position="right"
          />
          <span class="unit"></span>
        </el-form-item>
      </el-col>
    </el-row>
    
    <!-- 高级选项 -->
    <el-collapse>
      <el-collapse-item title="高级选项" name="advanced">
        <el-form-item label="触发方向">
          <el-checkbox v-model="form.enableTrueToFalseTrigger">
            支持从true到false的触发
          </el-checkbox>
          <div class="field-help">
            勾选后表达式结果从true变为false时也会触发巡检
          </div>
        </el-form-item>
        
        <el-form-item label="优先级" prop="priority">
          <el-radio-group v-model="form.priority">
            <el-radio label="Low"></el-radio>
            <el-radio label="Medium"></el-radio>
            <el-radio label="High"></el-radio>
          </el-radio-group>
        </el-form-item>
      </el-collapse-item>
    </el-collapse>
  </el-form>
</template>

巡检项配置表单

1. 基本字段配置

interface InspectionItemForm {
  // 基本信息
  itemName: string;                      // 项目名称 [必填, 最大200字符]
  itemDescription?: string;              // 项目描述 [可选, 最大1000字符]
  inspectionType: InspectionType;        // 巡检类型 [必填]
  
  // 计算配置
  calculationExpression?: string;        // 自定义计算表达式 [可选, 最大1000字符]
  expressionDurationRequirementSeconds: number; // 表达式持续时间要求 [0-3600秒, 默认0]
  calculationMethod: CalculationMethod;  // 计算方式 [必填]
  timeWindowSeconds: number;             // 时间窗口 [1-3600秒, 默认10]
  
  // 阈值配置
  voltageDeviationThreshold: number;     // 电压偏差阈值 [0.1-100kV, 默认1.0]
  currentDeviationThresholdPercent: number; // 电流偏差阈值百分比 [0.1-100%, 默认5.0]
  phaseAngleDifferenceThreshold: number; // 相角差阈值 [0-180度, 默认10.0]
  
  // 状态配置
  isActive: boolean;                     // 是否启用 [默认true]
  sortOrder: number;                     // 排序序号 [默认0]
}

2. 巡检类型选项

const inspectionTypeOptions = [
  {
    value: 'VoltageCollectionConsistency',
    label: '电压采集一致性判断',
    description: '检查多个电压采集点的数值一致性',
    icon: 'el-icon-lightning',
    color: '#409EFF'
  },
  {
    value: 'CurrentCollectionConsistency', 
    label: '电流采集一致性判断',
    description: '检查电流采集的一致性和合理性',
    icon: 'el-icon-connection',
    color: '#67C23A'
  },
  {
    value: 'PositionSignalVerification',
    label: '位置信号采集校验',
    description: '校验开关位置信号的正确性',
    icon: 'el-icon-switch-button',
    color: '#E6A23C'
  },
  {
    value: 'ACCircuitPolarityVerification',
    label: '交流回路极性校验',
    description: '验证交流回路的极性正确性',
    icon: 'el-icon-sort',
    color: '#F56C6C'
  },
  {
    value: 'CommunicationQualityAnalysis',
    label: '通信质量分析',
    description: '分析通信链路的质量和可靠性',
    icon: 'el-icon-connection',
    color: '#909399'
  }
];

3. 表达式编辑器组件

<template>
  <div class="expression-editor">
    <!-- Monaco编辑器 -->
    <div ref="editorContainer" class="editor-container"></div>
    
    <!-- 工具栏 -->
    <div class="editor-toolbar">
      <el-button-group>
        <el-button size="mini" @click="formatExpression">格式化</el-button>
        <el-button size="mini" @click="validateExpression">验证</el-button>
        <el-button size="mini" @click="testExpression">测试</el-button>
      </el-button-group>
      
      <el-dropdown @command="insertVariable">
        <el-button size="mini">
          插入变量<i class="el-icon-arrow-down el-icon--right"></i>
        </el-button>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item 
            v-for="variable in variables" 
            :key="variable.code"
            :command="variable.code"
          >
            {{ variable.code }} - {{ variable.description }}
          </el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
    
    <!-- 验证结果 -->
    <div v-if="validationResult" class="validation-result">
      <el-alert
        :type="validationResult.isValid ? 'success' : 'error'"
        :title="validationResult.isValid ? '表达式语法正确' : '表达式语法错误'"
        :description="validationResult.errors.join('; ')"
        show-icon
        :closable="false"
      />
    </div>
    
    <!-- 测试结果 -->
    <div v-if="testResult" class="test-result">
      <el-card>
        <div slot="header">
          <span>测试结果</span>
          <el-tag :type="testResult.flag ? 'success' : 'danger'" style="float: right;">
            {{ testResult.flag ? '正常' : '异常' }}
          </el-tag>
        </div>
        <div>
          <p><strong>计算结果:</strong> {{ testResult.result }}</p>
          <p><strong>执行时间:</strong> {{ testResult.executionTimeMs }}ms</p>
          <p><strong>变量值:</strong></p>
          <ul>
            <li v-for="(value, key) in testResult.variableValues" :key="key">
              {{ key }}: {{ value }}
            </li>
          </ul>
        </div>
      </el-card>
    </div>
  </div>
</template>

<script>
import * as monaco from 'monaco-editor';

export default {
  name: 'ExpressionEditor',
  props: {
    value: String,
    variables: Array,
    placeholder: String
  },
  data() {
    return {
      editor: null,
      validationResult: null,
      testResult: null
    };
  },
  mounted() {
    this.initEditor();
  },
  methods: {
    initEditor() {
      this.editor = monaco.editor.create(this.$refs.editorContainer, {
        value: this.value || '',
        language: 'javascript',
        theme: 'vs',
        minimap: { enabled: false },
        scrollBeyondLastLine: false,
        wordWrap: 'on',
        automaticLayout: true
      });
      
      this.editor.onDidChangeModelContent(() => {
        this.$emit('input', this.editor.getValue());
      });
    },
    
    async validateExpression() {
      const expression = this.editor.getValue();
      try {
        const response = await this.$http.post('/api/secondary-circuit/validate-expression', {
          expression
        });
        this.validationResult = response.data.resultData;
        this.$emit('validate', this.validationResult);
      } catch (error) {
        this.validationResult = {
          isValid: false,
          errors: [error.message]
        };
      }
    },
    
    async testExpression() {
      const expression = this.editor.getValue();
      try {
        const response = await this.$http.post('/api/secondary-circuit/test-expression', {
          expression,
          timeWindowSeconds: 60
        });
        this.testResult = response.data.resultData;
      } catch (error) {
        this.$message.error('测试表达式失败: ' + error.message);
      }
    },
    
    insertVariable(variableCode) {
      const position = this.editor.getPosition();
      this.editor.executeEdits('', [{
        range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
        text: `{${variableCode}}`
      }]);
      this.editor.focus();
    }
  }
};
</script>

JavaScript代码编辑器

1. 代码编辑器配置

<template>
  <div class="javascript-editor">
    <div class="editor-header">
      <h4>JavaScript代码编辑器</h4>
      <div class="editor-actions">
        <el-button size="mini" @click="loadTemplate">加载模板</el-button>
        <el-button size="mini" @click="runCode">运行代码</el-button>
        <el-button size="mini" type="primary" @click="saveCode">保存</el-button>
      </div>
    </div>
    
    <div ref="codeEditor" class="code-editor"></div>
    
    <!-- 数据预览 -->
    <el-collapse v-model="activeCollapse">
      <el-collapse-item title="数据预览" name="dataPreview">
        <pre>{{ JSON.stringify(sampleData, null, 2) }}</pre>
      </el-collapse-item>
      
      <el-collapse-item title="执行结果" name="result" v-if="executionResult">
        <div class="execution-result">
          <el-tag :type="executionResult.flag ? 'success' : 'danger'">
            {{ executionResult.flag ? '正常' : '异常' }}
          </el-tag>
          <p><strong>结果描述:</strong> {{ executionResult.resultDescription }}</p>
          <p><strong>执行时间:</strong> {{ executionResult.executionTimeMs }}ms</p>
          <div v-if="executionResult.details && executionResult.details.length">
            <p><strong>执行详情:</strong></p>
            <ul>
              <li v-for="detail in executionResult.details" :key="detail">{{ detail }}</li>
            </ul>
          </div>
        </div>
      </el-collapse-item>
    </el-collapse>
  </div>
</template>

2. 代码模板

const codeTemplates = {
  basic: `// 基本阈值检查模板
function checkThreshold() {
    let abnormalCount = 0;
    
    for (let item of data) {
        for (let key in item) {
            if (item[key].value > 100) {
                abnormalCount++;
            }
        }
    }
    
    return {
        Flag: abnormalCount === 0,
        ResultDescription: abnormalCount === 0 ? 
            "所有数据点正常" : 
            \`发现\${abnormalCount}个异常数据点\`
    };
}

checkThreshold();`,

  voltageConsistency: `// 电压一致性检查模板
function checkVoltageConsistency() {
    let voltageValues = [];
    
    // 收集所有电压值
    for (let item of data) {
        for (let key in item) {
            if (key.endsWith('_0')) { // 遥测数据
                voltageValues.push(item[key].value);
            }
        }
    }
    
    if (voltageValues.length < 2) {
        return {
            Flag: false,
            ResultDescription: "电压数据点不足,无法进行一致性检查"
        };
    }
    
    // 计算电压偏差
    let maxVoltage = Math.max(...voltageValues);
    let minVoltage = Math.min(...voltageValues);
    let deviation = maxVoltage - minVoltage;
    
    return {
        Flag: deviation <= 5.0,
        ResultDescription: \`电压偏差: \${deviation.toFixed(2)}V, \${deviation <= 5.0 ? '正常' : '异常'}\`
    };
}

checkVoltageConsistency();`,

  timeSeries: `// 时间序列分析模板
function analyzeTimeSeries() {
    let timeSeriesData = {};
    
    // 按变量代码分组数据
    for (let item of data) {
        for (let key in item) {
            if (!timeSeriesData[key]) {
                timeSeriesData[key] = [];
            }
            timeSeriesData[key].push({
                value: item[key].value,
                time: item[key].time
            });
        }
    }
    
    let results = [];
    
    // 分析每个变量的趋势
    for (let variable in timeSeriesData) {
        let values = timeSeriesData[variable];
        if (values.length >= 2) {
            let trend = values[values.length - 1].value - values[0].value;
            results.push({
                variable: variable,
                trend: trend,
                isStable: Math.abs(trend) < 1.0
            });
        }
    }
    
    let unstableCount = results.filter(r => !r.isStable).length;
    
    return {
        Flag: unstableCount === 0,
        ResultDescription: unstableCount === 0 ? 
            "所有变量趋势稳定" : 
            \`\${unstableCount}个变量趋势不稳定\`
    };
}

analyzeTimeSeries();`
};

数据展示组件

1. 时间窗口数据表格

<template>
  <div class="time-window-data">
    <div class="data-header">
      <h4>时间窗口数据</h4>
      <el-button @click="refreshData" :loading="loading">刷新数据</el-button>
    </div>
    
    <el-table :data="tableData" border>
      <el-table-column prop="variableCode" label="变量代码" width="120"/>
      <el-table-column prop="variableName" label="变量名称" width="200"/>
      <el-table-column prop="value" label="当前值" width="100">
        <template slot-scope="scope">
          <span :class="getValueClass(scope.row)">{{ scope.row.value }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="unit" label="单位" width="80"/>
      <el-table-column prop="updateTime" label="更新时间" width="160"/>
      <el-table-column prop="status" label="状态" width="100">
        <template slot-scope="scope">
          <el-tag :type="scope.row.status === 'normal' ? 'success' : 'danger'">
            {{ scope.row.status === 'normal' ? '正常' : '异常' }}
          </el-tag>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

2. 实时数据图表

<template>
  <div class="realtime-chart">
    <div ref="chart" class="chart-container"></div>
  </div>
</template>

<script>
import * as echarts from 'echarts';

export default {
  name: 'RealtimeChart',
  props: {
    data: Array,
    title: String
  },
  mounted() {
    this.initChart();
  },
  methods: {
    initChart() {
      this.chart = echarts.init(this.$refs.chart);
      
      const option = {
        title: {
          text: this.title || '实时数据'
        },
        tooltip: {
          trigger: 'axis',
          formatter: function (params) {
            let result = params[0].name + '<br/>';
            params.forEach(param => {
              result += param.marker + param.seriesName + ': ' + param.value + '<br/>';
            });
            return result;
          }
        },
        legend: {
          data: []
        },
        xAxis: {
          type: 'time',
          splitLine: {
            show: false
          }
        },
        yAxis: {
          type: 'value',
          splitLine: {
            show: true
          }
        },
        series: []
      };
      
      this.chart.setOption(option);
      this.updateChart();
    },
    
    updateChart() {
      if (!this.data || !this.chart) return;
      
      const series = this.processData(this.data);
      this.chart.setOption({
        legend: {
          data: series.map(s => s.name)
        },
        series: series
      });
    },
    
    processData(data) {
      const seriesMap = {};
      
      data.forEach(item => {
        Object.keys(item).forEach(key => {
          if (!seriesMap[key]) {
            seriesMap[key] = {
              name: key,
              type: 'line',
              data: []
            };
          }
          
          seriesMap[key].data.push([
            item[key].time,
            item[key].value
          ]);
        });
      });
      
      return Object.values(seriesMap);
    }
  },
  watch: {
    data: {
      handler() {
        this.updateChart();
      },
      deep: true
    }
  }
};
</script>

样式指南

1. CSS变量定义

:root {
  --primary-color: #409EFF;
  --success-color: #67C23A;
  --warning-color: #E6A23C;
  --danger-color: #F56C6C;
  --info-color: #909399;
  
  --border-radius: 4px;
  --box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  --transition: all 0.3s ease;
}

2. 组件样式

.expression-editor {
  border: 1px solid #DCDFE6;
  border-radius: var(--border-radius);
  overflow: hidden;
}

.editor-container {
  height: 200px;
  border-bottom: 1px solid #EBEEF5;
}

.editor-toolbar {
  padding: 8px 12px;
  background-color: #F5F7FA;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.validation-result,
.test-result {
  margin-top: 12px;
}

.field-help {
  font-size: 12px;
  color: var(--info-color);
  margin-top: 4px;
}

.unit {
  margin-left: 8px;
  color: var(--info-color);
  font-size: 12px;
}

最佳实践

1. 用户体验优化

  • 实时验证: 在用户输入时实时验证表达式语法
  • 智能提示: 提供变量代码和函数的智能提示
  • 错误提示: 清晰的错误信息和修复建议
  • 数据预览: 实时预览计算结果和数据变化

2. 性能优化

  • 防抖处理: 对频繁的验证和测试操作进行防抖
  • 懒加载: 大量数据的懒加载和虚拟滚动
  • 缓存机制: 缓存验证结果和测试数据
  • 异步处理: 长时间操作的异步处理和进度提示

3. 可访问性

  • 键盘导航: 支持完整的键盘操作
  • 屏幕阅读器: 提供适当的ARIA标签
  • 颜色对比: 确保足够的颜色对比度
  • 焦点管理: 合理的焦点顺序和视觉反馈

本文档版本: 1.0
最后更新: 2024年