|
@@ -47,6 +47,7 @@ const periodDetail = ref<PeriodDetail | null>(null)
|
|
|
const periodVisible = ref(false)
|
|
const periodVisible = ref(false)
|
|
|
const periodLoading = ref(false)
|
|
const periodLoading = ref(false)
|
|
|
const labels = ref<WaveLabel[]>([])
|
|
const labels = ref<WaveLabel[]>([])
|
|
|
|
|
+const refreshChartAfterPoints = ref(false)
|
|
|
const labelDialogVisible = ref(false)
|
|
const labelDialogVisible = ref(false)
|
|
|
const labelSaving = ref(false)
|
|
const labelSaving = ref(false)
|
|
|
const LABEL_SEVERITY: Record<string, number> = { 轻微: 1, 异常: 2, 严重异常: 3 }
|
|
const LABEL_SEVERITY: Record<string, number> = { 轻微: 1, 异常: 2, 严重异常: 3 }
|
|
@@ -192,8 +193,12 @@ const pointModel = computed({
|
|
|
set: (value: string[]) => {
|
|
set: (value: string[]) => {
|
|
|
const wave = value.filter((item) => (DEVICE_POINTS as readonly string[]).includes(item)) as DevicePoint[]
|
|
const wave = value.filter((item) => (DEVICE_POINTS as readonly string[]).includes(item)) as DevicePoint[]
|
|
|
const site = value.filter((item) => !(DEVICE_POINTS as readonly string[]).includes(item))
|
|
const site = value.filter((item) => !(DEVICE_POINTS as readonly string[]).includes(item))
|
|
|
- selectedDevicePoints.value = wave
|
|
|
|
|
- selectedSitePoints.value = site
|
|
|
|
|
|
|
+ const sameWave = wave.length === selectedDevicePoints.value.length
|
|
|
|
|
+ && wave.every((point, index) => point === selectedDevicePoints.value[index])
|
|
|
|
|
+ const sameSite = site.length === selectedSitePoints.value.length
|
|
|
|
|
+ && site.every((point, index) => point === selectedSitePoints.value[index])
|
|
|
|
|
+ if (!sameWave) selectedDevicePoints.value = wave
|
|
|
|
|
+ if (!sameSite) selectedSitePoints.value = site
|
|
|
},
|
|
},
|
|
|
})
|
|
})
|
|
|
const sitePointLabel = (point: SitePoint) => `${point.itemName} ${point.itemDescription}`.trim()
|
|
const sitePointLabel = (point: SitePoint) => `${point.itemName} ${point.itemDescription}`.trim()
|
|
@@ -214,13 +219,20 @@ const fileTabs = computed(() => {
|
|
|
const isSiteTab = computed(() => fileListPoint.value === SITE_TAB_LABEL)
|
|
const isSiteTab = computed(() => fileListPoint.value === SITE_TAB_LABEL)
|
|
|
const windowSiteRows = computed(() => {
|
|
const windowSiteRows = computed(() => {
|
|
|
if (!isSiteTab.value) return []
|
|
if (!isSiteTab.value) return []
|
|
|
- return (waveData.value?.points ?? []).map((point, index) => ({
|
|
|
|
|
- index,
|
|
|
|
|
- sampleTime: point.sampleTime,
|
|
|
|
|
- values: Object.fromEntries(
|
|
|
|
|
- selectedSitePoints.value.map((itemName) => [itemName, point.siteValues?.[itemName] ?? null]),
|
|
|
|
|
- ),
|
|
|
|
|
- }))
|
|
|
|
|
|
|
+ const latestByTime = new Map(timePoints.value.map((point) => [point.sampleTime, point]))
|
|
|
|
|
+ return (waveData.value?.points ?? []).map((point, index) => {
|
|
|
|
|
+ const latest = latestByTime.get(point.sampleTime) ?? point
|
|
|
|
|
+ return {
|
|
|
|
|
+ index,
|
|
|
|
|
+ sampleTime: point.sampleTime,
|
|
|
|
|
+ values: Object.fromEntries(
|
|
|
|
|
+ selectedSitePoints.value.map((itemName) => [
|
|
|
|
|
+ itemName,
|
|
|
|
|
+ point.siteValues?.[itemName] ?? latest.siteValues?.[itemName] ?? null,
|
|
|
|
|
+ ]),
|
|
|
|
|
+ ),
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
const devicePointCounts = computed<Record<string, { ab: number | null }>>(() => {
|
|
const devicePointCounts = computed<Record<string, { ab: number | null }>>(() => {
|
|
@@ -449,6 +461,7 @@ function firstRunningIndex(): number {
|
|
|
async function loadTimePoints() {
|
|
async function loadTimePoints() {
|
|
|
if (!selectedDevicePart.value) return
|
|
if (!selectedDevicePart.value) return
|
|
|
if (!selectedDevicePoints.value.length && !isPksOnly.value) return
|
|
if (!selectedDevicePoints.value.length && !isPksOnly.value) return
|
|
|
|
|
+ const hadWaveData = Boolean(waveData.value)
|
|
|
timePointsLoading.value = true
|
|
timePointsLoading.value = true
|
|
|
waveData.value = null
|
|
waveData.value = null
|
|
|
errorMessage.value = ''
|
|
errorMessage.value = ''
|
|
@@ -473,7 +486,12 @@ async function loadTimePoints() {
|
|
|
chartDirty.value = true
|
|
chartDirty.value = true
|
|
|
void loadLabels()
|
|
void loadLabels()
|
|
|
void loadFaults()
|
|
void loadFaults()
|
|
|
|
|
+ if (refreshChartAfterPoints.value) {
|
|
|
|
|
+ refreshChartAfterPoints.value = false
|
|
|
|
|
+ if (hadWaveData) void loadWaveWindowNow()
|
|
|
|
|
+ }
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
|
|
+ refreshChartAfterPoints.value = false
|
|
|
timePoints.value = []
|
|
timePoints.value = []
|
|
|
referencePoints.value = []
|
|
referencePoints.value = []
|
|
|
errorMessage.value = error instanceof Error ? error.message : '时间点读取失败'
|
|
errorMessage.value = error instanceof Error ? error.message : '时间点读取失败'
|
|
@@ -777,11 +795,20 @@ function labelTargetFor(rowKey: string) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const labelLookup = computed(() => {
|
|
|
|
|
+ const map = new Map<string, WaveLabel[]>()
|
|
|
|
|
+ for (const label of labels.value) {
|
|
|
|
|
+ const list = map.get(label.rowKey)
|
|
|
|
|
+ if (list) list.push(label)
|
|
|
|
|
+ else map.set(label.rowKey, [label])
|
|
|
|
|
+ }
|
|
|
|
|
+ return map
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
function labelVisibleAt(rowKey: string, at: string): WaveLabel | undefined {
|
|
function labelVisibleAt(rowKey: string, at: string): WaveLabel | undefined {
|
|
|
const target = new Date(at.replace(' ', 'T')).getTime()
|
|
const target = new Date(at.replace(' ', 'T')).getTime()
|
|
|
- const candidates = labels.value.filter((label) => (
|
|
|
|
|
- label.rowKey === rowKey
|
|
|
|
|
- && new Date(label.startTime.replace(' ', 'T')).getTime() <= target
|
|
|
|
|
|
|
+ const candidates = (labelLookup.value.get(rowKey) ?? []).filter((label) => (
|
|
|
|
|
+ new Date(label.startTime.replace(' ', 'T')).getTime() <= target
|
|
|
&& target < new Date(label.endTime.replace(' ', 'T')).getTime()
|
|
&& target < new Date(label.endTime.replace(' ', 'T')).getTime()
|
|
|
))
|
|
))
|
|
|
candidates.sort((a, b) => {
|
|
candidates.sort((a, b) => {
|
|
@@ -792,6 +819,26 @@ function labelVisibleAt(rowKey: string, at: string): WaveLabel | undefined {
|
|
|
return candidates[0]
|
|
return candidates[0]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const LABEL_CELL_TINTS: Record<string, string> = {
|
|
|
|
|
+ 轻微: '#fff7e0',
|
|
|
|
|
+ 异常: '#ffedd9',
|
|
|
|
|
+ 严重异常: '#ffe3e3',
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function labelCellStyle(rowKey: string, at: string) {
|
|
|
|
|
+ const label = labelVisibleAt(rowKey, at)
|
|
|
|
|
+ const tint = label ? LABEL_CELL_TINTS[label.labelType] : undefined
|
|
|
|
|
+ return tint ? { backgroundColor: tint } : undefined
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function siteCellStyle(itemName: string, sampleTime: string) {
|
|
|
|
|
+ return labelCellStyle(itemName, sampleTime)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function waveRowStyle(file: WaveWindowFile) {
|
|
|
|
|
+ return labelCellStyle(file.devicePoint, file.sampleTime)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function labelInputFromForm() {
|
|
function labelInputFromForm() {
|
|
|
return {
|
|
return {
|
|
|
target_type: labelForm.targetType,
|
|
target_type: labelForm.targetType,
|
|
@@ -1004,7 +1051,10 @@ watch(unitNumber, async () => {
|
|
|
watch(selectedSitePoints, (points) => {
|
|
watch(selectedSitePoints, (points) => {
|
|
|
if (!initialized.value) return
|
|
if (!initialized.value) return
|
|
|
if (selectedDevicePart.value) rememberPart(selectedDevicePart.value)
|
|
if (selectedDevicePart.value) rememberPart(selectedDevicePart.value)
|
|
|
- if (isPksMode.value) scheduleTimePoints()
|
|
|
|
|
|
|
+ if (isPksMode.value) {
|
|
|
|
|
+ if (waveData.value) refreshChartAfterPoints.value = true
|
|
|
|
|
+ scheduleTimePoints()
|
|
|
|
|
+ }
|
|
|
if (points.length) fileListPoint.value = SITE_TAB_LABEL
|
|
if (points.length) fileListPoint.value = SITE_TAB_LABEL
|
|
|
})
|
|
})
|
|
|
function locateFile(file: WaveWindowFile) {
|
|
function locateFile(file: WaveWindowFile) {
|
|
@@ -1347,7 +1397,7 @@ onBeforeUnmount(() => {
|
|
|
<tr v-for="row in windowSiteRows" :key="row.index">
|
|
<tr v-for="row in windowSiteRows" :key="row.index">
|
|
|
<td class="mono">{{ row.index + 1 }}</td>
|
|
<td class="mono">{{ row.index + 1 }}</td>
|
|
|
<td class="mono">{{ row.sampleTime }}</td>
|
|
<td class="mono">{{ row.sampleTime }}</td>
|
|
|
- <td v-for="itemName in selectedSitePoints" :key="itemName" class="mono">{{ row.values[itemName] ?? '—' }}</td>
|
|
|
|
|
|
|
+ <td v-for="itemName in selectedSitePoints" :key="itemName" class="mono" :style="siteCellStyle(itemName, row.sampleTime)">{{ row.values[itemName] ?? '—' }}</td>
|
|
|
</tr>
|
|
</tr>
|
|
|
</tbody>
|
|
</tbody>
|
|
|
</table>
|
|
</table>
|
|
@@ -1366,7 +1416,7 @@ onBeforeUnmount(() => {
|
|
|
</tr>
|
|
</tr>
|
|
|
</thead>
|
|
</thead>
|
|
|
<tbody>
|
|
<tbody>
|
|
|
- <tr v-for="file in windowFiles" :key="file.id">
|
|
|
|
|
|
|
+ <tr v-for="file in windowFiles" :key="file.id" :style="waveRowStyle(file)">
|
|
|
<td class="mono">{{ file.id }}</td>
|
|
<td class="mono">{{ file.id }}</td>
|
|
|
<td>{{ file.pointName }}</td>
|
|
<td>{{ file.pointName }}</td>
|
|
|
<td>{{ file.devicePoint }}</td>
|
|
<td>{{ file.devicePoint }}</td>
|