diff --git a/src/utils/useMessage.ts b/src/utils/useMessage.ts new file mode 100644 index 0000000..ef0d147 --- /dev/null +++ b/src/utils/useMessage.ts @@ -0,0 +1,127 @@ +import { ElMessage, ElMessageBox, ElNotification } from 'element-plus' +export const useMessage = () => { + return { + // 消息提示 + info(content: string) { + ElMessage.info(content) + }, + // 错误消息 + error(content: string) { + ElMessage.error(content) + }, + // 成功消息 + success(content: string) { + ElMessage.success(content) + }, + // 警告消息 + warning(content: string) { + ElMessage.warning(content) + }, + // 弹出提示 + alert(content: string) { + ElMessageBox.alert(content, '系统提示') + }, + // 错误提示 + alertError(content: string) { + ElMessageBox.alert(content, '系统提示', { type: 'error' }) + }, + // 成功提示 + alertSuccess(content: string) { + ElMessageBox.alert(content, '系统提示', { type: 'success' }) + }, + // 警告提示 + alertWarning(content: string) { + ElMessageBox.alert(content, '系统提示', { type: 'warning' }) + }, + // 通知提示 + notify(content: string) { + ElNotification.info(content) + }, + // 错误通知 + notifyError(content: string, tip?: string) { + ElNotification.error({ + title: tip ? tip : '系统提示', + message: content, + }) + }, + // 成功通知 + notifySuccess(content: string) { + ElNotification.success(content) + }, + // 警告通知 + notifyWarning(content: string) { + ElNotification.warning(content) + }, + // 确认窗体 + confirm(content: string, tip?: string) { + return ElMessageBox.confirm(content, tip ? tip : '系统提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }) + }, + forceConfirm(content: string, tip?: string, buttonText?: string) { + return ElMessageBox.confirm(content, tip ? tip : '系统提示', { + confirmButtonText: buttonText ?? '确定', + showCancelButton: false, + closeOnClickModal: false, + closeOnPressEscape: false, + showClose: false, + type: 'warning', + }) + }, + // 删除窗体 + delConfirm(content?: string, tip?: string) { + return new Promise((resolve, reject) => { + ElMessageBox.confirm( + content ? content : '是否确认删除数据项?', + tip ? tip : '系统提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + } + ) + .then(() => { + resolve('') + }) + .catch(() => { + reject('') + }) + }) + }, + // 导出窗体 + exportConfirm(content?: string, tip?: string) { + return ElMessageBox.confirm( + content ? content : '是否确认导出数据项?', + tip ? tip : '系统提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + } + ) + }, + // 提交内容 + prompt(content: string, tip: string) { + return ElMessageBox.prompt(content, tip, { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }) + }, + + promptVerify(content: string, tip: string, pattern: string, inputErrorMessage = '') { + const PatternRegExp = new RegExp( + `^${pattern.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')}$` + ) + return ElMessageBox.prompt(content, tip, { + confirmButtonText: '确定', + cancelButtonText: '取消', + inputPattern: PatternRegExp, + inputErrorMessage: inputErrorMessage, + type: 'warning', + }) + }, + } +} diff --git a/src/utils/zmq.ts b/src/utils/zmq.ts index c97b1b3..ee809b8 100644 --- a/src/utils/zmq.ts +++ b/src/utils/zmq.ts @@ -1,4 +1,5 @@ import { v4 as uuidv4 } from 'uuid'; + export type ManualAction = 'init' | 'release' | 'write' | 'report' | 'lock' | 'unlock' | 'export' | 'cancel' | 'import' | 'upgrade' @@ -32,14 +33,15 @@ export interface TimeoutMsg { timeoutId: string timeoutTopic: string } + export interface ZmqMessage { cmd: ZmqCMD msg: string community: boolean topic: string } -type TopicType = 'event' | 'status' +type TopicType = 'event' | 'status' // web端发布消息类型 @@ -49,6 +51,7 @@ export interface PublishMsg { reply: 'yes' | 'no' // 是否需要回复 params: any // 消息的参数 } + // web端发布消息服务端返回的数据类型 export interface PubMsgData { code: number @@ -66,7 +69,6 @@ export interface SubMsgData { } - /* * @Description: 获取发布的主题 * type: 主题类型 @@ -130,6 +132,7 @@ export function getPubInitData(action: T, params: any, r params }) } + export function isHexadecimal(text: string) { const hexRegex = /^[0-9A-Fa-f]+$/ return hexRegex.test(text) diff --git a/src/views/stationData/component/newDataChart.vue b/src/views/stationData/component/newDataChart.vue index 0d31d3a..c447bab 100644 --- a/src/views/stationData/component/newDataChart.vue +++ b/src/views/stationData/component/newDataChart.vue @@ -8,6 +8,7 @@ :loading-options="loadingOpt" :loading="loading" @click="onChartClick" + @legendselectchanged="changeLegend" /> @@ -41,6 +42,42 @@ const ZOOM_HEIGHT = 30 const ZOOM_BOTTOM = 10 const emits = defineEmits(['on-change-coefficient']) +const EXTENDED_PALETTE = [ + '#5470c6', + '#91cc75', + '#fac858', + '#ee6666', + '#73c0de', + '#3ba272', + '#fc8452', + '#9a60b4', + '#ea7ccc', + '#2f4554', + '#61a0a8', + '#d48265', + '#91c7ae', + '#749f83', + '#ca8622', + '#bda29a', + '#6e7074', + '#546570', + '#c4ccd3', + '#f05b72', + '#ef5b9c', + '#f47920', + '#905a3d', + '#fab27b', + '#2a5caa', + '#444693', + '#726930', + '#b2d235', + '#6d8346', + '#ac6767', + '#1d953f', + '#6950a1', + '#918597', +] + type Legend = { addr: string label: string @@ -109,8 +146,12 @@ const chartOption = computed(() => { } loading.value = false const tmpSeries: SeriesOption[] = [] - for (const legend of props.legends.filter(item => item.addr !== 'ts')) { + const legends = props.legends.filter(item => item.addr !== 'ts') + for (let i = 0; i < legends.length; i++) { + const legend = legends[i] const entry = props.chartDatas.get(legend.addr) ?? [] + const color = EXTENDED_PALETTE[i % EXTENDED_PALETTE.length] + const lineData: SeriesOption = { name: `${legend.label}|${legend.addr}`, type: 'line', @@ -118,7 +159,11 @@ const chartOption = computed(() => { // symbol: "none", connectNulls: true, triggerLineEvent: true, + itemStyle: { + color: color, + }, data: entry.map(([ts, val]) => { + console.log(ts) const coef = legend?.coefficient ? new Big(legend.coefficient) : new Big(1) let valBig = new Big(val ?? 0) valBig = valBig.div(coef) @@ -155,22 +200,71 @@ const chartOption = computed(() => { appendToBody: true, formatter: function (params: any) { // 时间格式化 - const date = new Date(params[0].value[0]) - const timeStr = dayjs(params[0].value[0]).format('YYYY-MM-DD HH:mm:ss.SSS') + const targetTime = params[0].value[0] + const timeStr = dayjs(targetTime).format('YYYY-MM-DD HH:mm:ss.SSS') let relVal = timeStr + const findNearest = (data: any[], target: number) => { + let left = 0 + let right = data.length - 1 + let nearest = data[0] + let minDiff = Math.abs(data[0][0] - target) - for (let i = 0; i < params.length; i++) { - const data = props.legends.find(item => item.addr === params[i].seriesId) - const coefficient = data?.coefficient ? Number(data.coefficient) : undefined - const unit = data?.unit || '' - relVal += `
-
${params[i].marker}${params[i].seriesName.split('|')[0]}:
-
${params[i].value[1]}${ - coefficient ? `【系数为${coefficient}】` : '' - }${unit}
-
` + while (left <= right) { + const mid = Math.floor((left + right) / 2) + const current = data[mid] + const diff = Math.abs(current[0] - target) + + if (diff < minDiff) { + minDiff = diff + nearest = current + } + + if (current[0] < target) { + left = mid + 1 + } else if (current[0] > target) { + right = mid - 1 + } else { + return current + } + } + return nearest } + + chartOption.value.series + ?.filter((series: any) => !unCheckArr.value.includes(series.name)) + .forEach((series: any, index: number) => { + // 尝试从 params 中找到当前系列的数据(如果是 hover 点) + const paramItem = params.find((p: any) => p.seriesId === series.id) + + let value + let marker + + if (paramItem) { + value = paramItem.value[1] + marker = paramItem.marker + } else { + // 如果不在 params 中,则在原始数据中查找最近的点 + const nearestPoint = findNearest(series.data, targetTime) + value = nearestPoint ? nearestPoint[1] : '--' + // 手动生成 marker + const color = series.itemStyle?.color || '#000' + marker = `*` + } + + const legendData = props.legends.find(item => item.addr === series.id) + const coefficient = legendData?.coefficient + ? Number(legendData.coefficient) + : undefined + const unit = legendData?.unit || '' + const seriesName = series.name.split('|')[0] + + relVal += `
+
${marker}${seriesName}:
+
${value}${coefficient ? `【系数为${coefficient}】` : ''}${unit}
+
` + }) + return relVal }, }, diff --git a/src/views/stationData/index.vue b/src/views/stationData/index.vue index 5498eb7..44d1d85 100644 --- a/src/views/stationData/index.vue +++ b/src/views/stationData/index.vue @@ -106,6 +106,9 @@ import { type SiteInfo, useTransferDataStore } from '@/stores/transferData' import { storeToRefs } from 'pinia' import { getSiteList, type ISite } from '@/api/module/transfer' import EdfsWrap from "@/components/Edfs-wrap.vue"; +import { useMessage } from '@/utils/useMessage' + +const message = useMessage() const env = import.meta.env @@ -141,6 +144,8 @@ async function loadSiteList() { const res = await getSiteList() if (res.code === 200 || res.code === 0) { siteList.value = res.data + } else { + message.error(res?.msg || '获取站点列表失败') } } diff --git a/src/views/stationData/topology/components/detailDrawer.vue b/src/views/stationData/topology/components/detailDrawer.vue index b2849c2..7ec4582 100644 --- a/src/views/stationData/topology/components/detailDrawer.vue +++ b/src/views/stationData/topology/components/detailDrawer.vue @@ -147,7 +147,7 @@ const handleInput = debounce(async (val: number) => { const isShowDrawer = defineModel() const title = computed(() => curDevice.value?.isonLine - ? '数据详情' + ? `${curDevice.value?.sn ?? ''}数据详情` : env.VITE_APP_ENV === 'local' ? `已导出数据详情` : '已导入数据详情' @@ -391,7 +391,7 @@ function setChartData(pointData: any[]) { pointData.forEach((data: any[]) => { const [ts, val, addr] = data if (checkPointList.value.some(i => i.addr === addr) && !!val) { - const time = dayjs(Number(ts)).format('YYYY-MM-DD HH:mm:ss.SSS') + const time = Number(ts) if (addr && val) { const colData = chartData.get(addr) const value = Number(Number(val).toFixed(5)) diff --git a/src/views/stationData/transfer/components/deviceDrawer.vue b/src/views/stationData/transfer/components/deviceDrawer.vue index c4a09af..e0b4808 100644 --- a/src/views/stationData/transfer/components/deviceDrawer.vue +++ b/src/views/stationData/transfer/components/deviceDrawer.vue @@ -2,45 +2,45 @@
- +
@@ -64,7 +64,7 @@ >/{{ deviceInfo.total }},剩余{{ + >,剩余{{ deviceInfo.total - deviceInfo.fetchLimit }}
@@ -78,13 +78,13 @@
查询数据 @@ -92,18 +92,18 @@ 小时
@@ -112,7 +112,7 @@
- -