766 lines
21 KiB
Markdown
766 lines
21 KiB
Markdown
|
|
# 前端UI参数配置指南
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
本文档为前端开发人员提供二次回路巡检系统UI参数配置的详细指南,包括表单字段、验证规则、数据格式和用户交互设计。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 事件驱动配置表单
|
|||
|
|
|
|||
|
|
### 1. 基本信息字段
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
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. 表单验证规则
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
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组件配置
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<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. 基本字段配置
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
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. 巡检类型选项
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
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. 表达式编辑器组件
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<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. 代码编辑器配置
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<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. 代码模板
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
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. 时间窗口数据表格
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<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. 实时数据图表
|
|||
|
|
|
|||
|
|
```vue
|
|||
|
|
<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变量定义
|
|||
|
|
|
|||
|
|
```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. 组件样式
|
|||
|
|
|
|||
|
|
```css
|
|||
|
|
.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年*
|