Browse Source

feat: 一些调整

main
betaqi 1 week ago
parent
commit
aba07c78e9
  1. 127
      src/utils/useMessage.ts
  2. 7
      src/utils/zmq.ts
  3. 120
      src/views/stationData/component/newDataChart.vue
  4. 5
      src/views/stationData/index.vue
  5. 4
      src/views/stationData/topology/components/detailDrawer.vue
  6. 69
      src/views/stationData/transfer/components/deviceDrawer.vue
  7. 2
      src/views/stationData/transfer/index.vue

127
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',
})
},
}
}

7
src/utils/zmq.ts

@ -1,4 +1,5 @@
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
export type ManualAction = export type ManualAction =
'init' | 'release' | 'write' | 'report' | 'lock' | 'unlock' | 'init' | 'release' | 'write' | 'report' | 'lock' | 'unlock' |
'export' | 'cancel' | 'import' | 'upgrade' 'export' | 'cancel' | 'import' | 'upgrade'
@ -32,14 +33,15 @@ export interface TimeoutMsg {
timeoutId: string timeoutId: string
timeoutTopic: string timeoutTopic: string
} }
export interface ZmqMessage { export interface ZmqMessage {
cmd: ZmqCMD cmd: ZmqCMD
msg: string msg: string
community: boolean community: boolean
topic: string topic: string
} }
type TopicType = 'event' | 'status'
type TopicType = 'event' | 'status'
// web端发布消息类型 // web端发布消息类型
@ -49,6 +51,7 @@ export interface PublishMsg<A> {
reply: 'yes' | 'no' // 是否需要回复 reply: 'yes' | 'no' // 是否需要回复
params: any // 消息的参数 params: any // 消息的参数
} }
// web端发布消息服务端返回的数据类型 // web端发布消息服务端返回的数据类型
export interface PubMsgData { export interface PubMsgData {
code: number code: number
@ -66,7 +69,6 @@ export interface SubMsgData {
} }
/* /*
* @Description: * @Description:
* type: * type:
@ -130,6 +132,7 @@ export function getPubInitData<T extends ManualAction>(action: T, params: any, r
params params
}) })
} }
export function isHexadecimal(text: string) { export function isHexadecimal(text: string) {
const hexRegex = /^[0-9A-Fa-f]+$/ const hexRegex = /^[0-9A-Fa-f]+$/
return hexRegex.test(text) return hexRegex.test(text)

120
src/views/stationData/component/newDataChart.vue

@ -8,6 +8,7 @@
:loading-options="loadingOpt" :loading-options="loadingOpt"
:loading="loading" :loading="loading"
@click="onChartClick" @click="onChartClick"
@legendselectchanged="changeLegend"
/> />
</div> </div>
<LineChartDlg ref="LineChartDlgRef" @on-save="onChangeCoefficient" /> <LineChartDlg ref="LineChartDlgRef" @on-save="onChangeCoefficient" />
@ -41,6 +42,42 @@ const ZOOM_HEIGHT = 30
const ZOOM_BOTTOM = 10 const ZOOM_BOTTOM = 10
const emits = defineEmits(['on-change-coefficient']) 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 = { type Legend = {
addr: string addr: string
label: string label: string
@ -109,8 +146,12 @@ const chartOption = computed<EChartsOption>(() => {
} }
loading.value = false loading.value = false
const tmpSeries: SeriesOption[] = [] 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 entry = props.chartDatas.get(legend.addr) ?? []
const color = EXTENDED_PALETTE[i % EXTENDED_PALETTE.length]
const lineData: SeriesOption = { const lineData: SeriesOption = {
name: `${legend.label}|${legend.addr}`, name: `${legend.label}|${legend.addr}`,
type: 'line', type: 'line',
@ -118,7 +159,11 @@ const chartOption = computed<EChartsOption>(() => {
// symbol: "none", // symbol: "none",
connectNulls: true, connectNulls: true,
triggerLineEvent: true, triggerLineEvent: true,
itemStyle: {
color: color,
},
data: entry.map(([ts, val]) => { data: entry.map(([ts, val]) => {
console.log(ts)
const coef = legend?.coefficient ? new Big(legend.coefficient) : new Big(1) const coef = legend?.coefficient ? new Big(legend.coefficient) : new Big(1)
let valBig = new Big(val ?? 0) let valBig = new Big(val ?? 0)
valBig = valBig.div(coef) valBig = valBig.div(coef)
@ -155,22 +200,71 @@ const chartOption = computed<EChartsOption>(() => {
appendToBody: true, appendToBody: true,
formatter: function (params: any) { formatter: function (params: any) {
// //
const date = new Date(params[0].value[0]) const targetTime = params[0].value[0]
const timeStr = dayjs(params[0].value[0]).format('YYYY-MM-DD HH:mm:ss.SSS') const timeStr = dayjs(targetTime).format('YYYY-MM-DD HH:mm:ss.SSS')
let relVal = timeStr 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++) { while (left <= right) {
const data = props.legends.find(item => item.addr === params[i].seriesId) const mid = Math.floor((left + right) / 2)
const coefficient = data?.coefficient ? Number(data.coefficient) : undefined const current = data[mid]
const unit = data?.unit || '' const diff = Math.abs(current[0] - target)
relVal += `<div style="display: flex; justify-content: space-between; gap: 30px;">
<div>${params[i].marker}${params[i].seriesName.split('|')[0]}:</div> if (diff < minDiff) {
<div>${params[i].value[1]}${ minDiff = diff
coefficient ? `【系数为${coefficient}` : '' nearest = current
}${unit}</div> }
</div>`
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 = `<span style="display:inline-block;margin-right:4px;color:${color};font-weight:bold;font-size: 20px;text-align:center;vertical-align:middle;line-height:10px;">*</span>`
}
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 += `<div style="display: flex; justify-content: space-between; gap: 30px;">
<div style="display:flex;align-items:center;">${marker}${seriesName}:</div>
<div>${value}${coefficient ? `【系数为${coefficient}` : ''}${unit}</div>
</div>`
})
return relVal return relVal
}, },
}, },

5
src/views/stationData/index.vue

@ -106,6 +106,9 @@ import { type SiteInfo, useTransferDataStore } from '@/stores/transferData'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { getSiteList, type ISite } from '@/api/module/transfer' import { getSiteList, type ISite } from '@/api/module/transfer'
import EdfsWrap from "@/components/Edfs-wrap.vue"; import EdfsWrap from "@/components/Edfs-wrap.vue";
import { useMessage } from '@/utils/useMessage'
const message = useMessage()
const env = import.meta.env const env = import.meta.env
@ -141,6 +144,8 @@ async function loadSiteList() {
const res = await getSiteList() const res = await getSiteList()
if (res.code === 200 || res.code === 0) { if (res.code === 200 || res.code === 0) {
siteList.value = res.data siteList.value = res.data
} else {
message.error(res?.msg || '获取站点列表失败')
} }
} }

4
src/views/stationData/topology/components/detailDrawer.vue

@ -147,7 +147,7 @@ const handleInput = debounce(async (val: number) => {
const isShowDrawer = defineModel<boolean>() const isShowDrawer = defineModel<boolean>()
const title = computed(() => const title = computed(() =>
curDevice.value?.isonLine curDevice.value?.isonLine
? '数据详情' ? `${curDevice.value?.sn ?? ''}数据详情`
: env.VITE_APP_ENV === 'local' : env.VITE_APP_ENV === 'local'
? `已导出数据详情` ? `已导出数据详情`
: '已导入数据详情' : '已导入数据详情'
@ -391,7 +391,7 @@ function setChartData(pointData: any[]) {
pointData.forEach((data: any[]) => { pointData.forEach((data: any[]) => {
const [ts, val, addr] = data const [ts, val, addr] = data
if (checkPointList.value.some(i => i.addr === addr) && !!val) { 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) { if (addr && val) {
const colData = chartData.get(addr) const colData = chartData.get(addr)
const value = Number(Number(val).toFixed(5)) const value = Number(Number(val).toFixed(5))

69
src/views/stationData/transfer/components/deviceDrawer.vue

@ -2,45 +2,45 @@
<div class="fault-rule-drawer"> <div class="fault-rule-drawer">
<el-drawer <el-drawer
v-model="isShowDrawer" v-model="isShowDrawer"
:before-close="handleBeforeClose"
:title="title" :title="title"
direction="rtl" direction="rtl"
size="100%"
modal-class="model-dev-opn" modal-class="model-dev-opn"
:before-close="handleBeforeClose" size="100%"
@opened="onDrawerOpened" @opened="onDrawerOpened"
> >
<main class="wh-full flex"> <main class="wh-full flex">
<EdfsWrap <EdfsWrap
title="点位组"
isCollapse
:collapsed="collapsed" :collapsed="collapsed"
@collapse="onCollapse"
:style="{ width: `${collapsed ? 0 : 600}px` }" :style="{ width: `${collapsed ? 0 : 600}px` }"
isCollapse
title="点位组"
@collapse="onCollapse"
> >
<PointGroupTree <PointGroupTree
v-if="isShowDrawer" v-if="isShowDrawer"
ref="pointGroupTreeRef"
:data="pointGroup" :data="pointGroup"
@device-select="onGroupChange"
:groupChangeLoading="groupChangeLoading" :groupChangeLoading="groupChangeLoading"
@onchangePoints="onchangePoints"
:pointList="pointList"
:loadingPoints="loadingPoints"
:isTransfer="props.isTransfer" :isTransfer="props.isTransfer"
:loadingPoints="loadingPoints"
:pointList="pointList"
:siteInfo="props.siteInfo" :siteInfo="props.siteInfo"
ref="pointGroupTreeRef" @onchangePoints="onchangePoints"
@device-select="onGroupChange"
/> />
</EdfsWrap> </EdfsWrap>
<div <div
class="flex-1 p-4 h-full overflow-hidden relative"
v-loading="loading" v-loading="loading"
class="flex-1 p-4 h-full overflow-hidden relative"
element-loading-text="数据加载中请耐心等待..." element-loading-text="数据加载中请耐心等待..."
> >
<div <div
class="absolute inset-0 z-[99] flex items-center justify-center bg-white/80 backdrop-blur-sm transition-opacity duration-300"
v-if="isFetching" v-if="isFetching"
class="absolute inset-0 z-[99] flex items-center justify-center bg-white/80 backdrop-blur-sm transition-opacity duration-300"
> >
<div class="w-full max-w-[700px] mx-auto px-4 sm:px-6 lg:px-8"> <div class="w-full max-w-[700px] mx-auto px-4 sm:px-6 lg:px-8">
<el-scrollbar height="70vh" max-height="600px" class="pr-2"> <el-scrollbar class="pr-2" height="70vh" max-height="600px">
<div class="space-y-4 md:space-y-6"> <div class="space-y-4 md:space-y-6">
<div <div
v-for="deviceInfo in pointInfoData" v-for="deviceInfo in pointInfoData"
@ -52,9 +52,9 @@
</div> </div>
<el-progress <el-progress
:text-inside="true"
:stroke-width="20"
:percentage="deviceInfo.progress" :percentage="deviceInfo.progress"
:stroke-width="20"
:text-inside="true"
/> />
<div class="text-gray-600 text-base"> <div class="text-gray-600 text-base">
@ -64,7 +64,7 @@
>/<span class="text-blue-600 font-semibold">{{ >/<span class="text-blue-600 font-semibold">{{
deviceInfo.total deviceInfo.total
}}</span }}</span
>剩余<span class="text-red-500 font-semibold">{{ >剩余<span class="text-red-500 font-semibold">{{
deviceInfo.total - deviceInfo.fetchLimit deviceInfo.total - deviceInfo.fetchLimit
}}</span> }}</span>
</div> </div>
@ -78,13 +78,13 @@
<div class="w-400px"> <div class="w-400px">
<el-date-picker <el-date-picker
v-model="time" v-model="time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
range-separator="到"
:default-value="getBeforeMonth" :default-value="getBeforeMonth"
start-placeholder="开始时间"
end-placeholder="结束时间"
:disabled-date="disabledDate" :disabled-date="disabledDate"
end-placeholder="结束时间"
range-separator="到"
start-placeholder="开始时间"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
/> />
</div> </div>
<el-button type="primary" @click="loadChardData2">查询数据</el-button> <el-button type="primary" @click="loadChardData2">查询数据</el-button>
@ -92,18 +92,18 @@
<el-input-number <el-input-number
:key="refreshKey" :key="refreshKey"
v-model="num" v-model="num"
:min="1"
:max="diffHours" :max="diffHours"
:min="1"
@input="handleInput" @input="handleInput"
/> />
小时 小时
</div> </div>
<NewDataChart <NewDataChart
v-if="isShowChart" v-if="isShowChart"
ref="chartRef"
:axis-data="Array.from(axisData)"
:chart-datas="chartData" :chart-datas="chartData"
:legends="legends" :legends="legends"
:axis-data="Array.from(axisData)"
ref="chartRef"
@onChangeCoefficient="onChangeCoefficient" @onChangeCoefficient="onChangeCoefficient"
/> />
</div> </div>
@ -112,7 +112,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import NewDataChart from '../../component/newDataChart.vue' import NewDataChart from '../../component/newDataChart.vue'
import PointGroupTree from './PointGroupTree.vue' import PointGroupTree from './PointGroupTree.vue'
import type { IMyPoint, IOfflineDevice, IOnlineDevice } from '../../type' import type { IMyPoint, IOfflineDevice, IOnlineDevice } from '../../type'
@ -174,11 +174,11 @@ const handleInput = debounce(async (val: number) => {
const isShowDrawer = defineModel<boolean>() const isShowDrawer = defineModel<boolean>()
const title = computed(() => const title = computed(() =>
props.isTransfer +props.isTransfer
? '数据详情' ? `${curDevice.value?.sn ?? ''}数据详情`
: env.VITE_APP_ENV === 'local' : env.VITE_APP_ENV === 'local'
? `已导出数据详情` ? `${curDevice.value?.sn ?? ''}已导出数据详情`
: '已导入数据详情' : `${curDevice.value?.sn ?? ''}已导入数据详情`,
) )
const message = useMessage() const message = useMessage()
@ -316,7 +316,7 @@ watch(
pointInfoData.value = newValue pointInfoData.value = newValue
loading.value = false loading.value = false
}, },
{ deep: true } { deep: true },
) )
async function loadChardData2() { async function loadChardData2() {
@ -356,7 +356,7 @@ function setChartData2(groupName: string, data: any[]) {
data.forEach((data: any[]) => { data.forEach((data: any[]) => {
const [ts, val, addr] = data const [ts, val, addr] = data
const time = dayjs(Number(ts)).format('YYYY-MM-DD HH:mm:ss.SSS') const time = Number(ts)
if (addr && val) { if (addr && val) {
const colData = chartData.get(`${groupName}-${addr}`) const colData = chartData.get(`${groupName}-${addr}`)
const value = Number(Number(val).toFixed(5)) const value = Number(Number(val).toFixed(5))
@ -390,7 +390,8 @@ function handleBeforeClose(done: () => void) {
chartGroupMap.clear() chartGroupMap.clear()
done() done()
}) })
.catch(() => {}) .catch(() => {
})
} }
function clearData() { function clearData() {
@ -472,7 +473,7 @@ function onchangePoints(checkPoints: IMyPoint[]) {
if (!curGroup?.value || !curGroupName?.value) return message.error('请选择设备') if (!curGroup?.value || !curGroupName?.value) return message.error('请选择设备')
const currentCheckPoints = checkPoints.filter( const currentCheckPoints = checkPoints.filter(
(r: any) => r.parentName === curGroupName.value (r: any) => r.parentName === curGroupName.value,
) )
chartParamsMap.set(curGroupName.value, { chartParamsMap.set(curGroupName.value, {
name: curGroupName.value, name: curGroupName.value,
@ -517,7 +518,7 @@ defineExpose({
}) })
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.fault-rule-drawer { .fault-rule-drawer {
font-size: 16px; font-size: 16px;

2
src/views/stationData/transfer/index.vue

@ -182,7 +182,7 @@ const isonLineTransfer = computed(() => type.value === 'export')
const message = useMessage() const message = useMessage()
const localTransferBtnText = computed(()=> isonLineTransfer.value ? '数据导出' : '数据导入') const localTransferBtnText = computed(() => isonLineTransfer.value ? '数据导出' : '数据导入')
const transferDataStore = useTransferDataStore() const transferDataStore = useTransferDataStore()
const { devicesMap } = storeToRefs(transferDataStore) const { devicesMap } = storeToRefs(transferDataStore)

Loading…
Cancel
Save