Browse Source

feat: 迁移数据展示修改为折线图

main
betaqi 4 months ago
parent
commit
e04557a310
  1. 1
      global.types/auto-imports.d.ts
  2. 2
      package.json
  3. 3893
      pnpm-lock.yaml
  4. 5
      src/api/module/transfer/index.ts
  5. 115
      src/views/stationData/components/deviceDrawer.vue
  6. 234
      src/views/stationData/components/newDataChart.vue
  7. 10
      src/views/stationData/components/transferDlg.vue
  8. 19
      src/views/stationData/transferData.vue

1
global.types/auto-imports.d.ts vendored

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const ElLoading: typeof import('element-plus/es')['ElLoading']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']

2
package.json

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
"axios": "^1.8.4",
"dayjs": "^1.11.13",
"dexie": "^4.0.11",
"echarts": "^5.6.0",
"element-plus": "^2.9.5",
"jszmq": "^0.1.2",
"lodash-es": "^4.17.21",
@ -23,6 +24,7 @@ @@ -23,6 +24,7 @@
"qs": "^6.14.0",
"uuid": "^11.1.0",
"vue": "^3.5.13",
"vue-echarts": "^7.0.3",
"vue-router": "^4.5.0"
},
"devDependencies": {

3893
pnpm-lock.yaml

File diff suppressed because it is too large Load Diff

5
src/api/module/transfer/index.ts

@ -3,8 +3,9 @@ import { globalServer } from '../index' @@ -3,8 +3,9 @@ import { globalServer } from '../index'
interface IGetDeviceDataParams {
columns: string[]
isLocal: boolean
limit: number
offset: number
limit?: number
offset?: number
host?: string
}

115
src/views/stationData/components/deviceDrawer.vue

@ -9,36 +9,12 @@ @@ -9,36 +9,12 @@
:before-close="handleBeforeClose"
>
<main class="drawer-box">
<EdfsTable
class="table"
v-loading="loading"
:data="tableData"
ref="tableRef"
:highlight-current-row="true"
:page-total="total"
:current-page="queryParams.pageNo"
:page-size="queryParams.pageSize"
row-class-name="row"
@pageCurrentChange="handleJump"
>
<template v-for="(col, idx) in tableCol" :key="idx">
<el-table-column
v-if="col.prop.endsWith('Time')"
:label="col.label"
:min-width="col.minWidth"
>
<template #default="scope">
{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column
v-else
:prop="col.prop"
:label="col.label"
:min-width="col.minWidth"
/>
</template>
</EdfsTable>
<NewDataChart
:chart-datas="chartDatas"
:legends="tableCol"
:axis-data="Array.from(axisData)"
v-if="isShowDrawer"
/>
</main>
</el-drawer>
</div>
@ -46,6 +22,7 @@ @@ -46,6 +22,7 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import NewDataChart from './newDataChart.vue'
import {
getPubInitData,
type ManualAction,
@ -85,19 +62,11 @@ const curDevice = ref<IOfflineDevice | IOnlineDevice>() @@ -85,19 +62,11 @@ const curDevice = ref<IOfflineDevice | IOnlineDevice>()
function open(device: IOfflineDevice | IOnlineDevice) {
curDevice.value = device
props.isTransfer ? loadDeviceDetails() : zmqImport(device as IOfflineDevice)
isShowDrawer.value = true
}
const tableData = ref<any[]>([])
const loading = ref(true)
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: undefined,
})
const tableRef = ref()
const pointsData = ref<{ addr: string; label: string }[]>([])
const columsParams = computed(() => ['ts', ...tableCol.value.map(i => i.addr)])
const pointsData = ref<any[]>([])
async function loadPoints() {
const params: IPointsParams = {}
if (props.isTransfer) {
@ -117,46 +86,57 @@ async function loadPoints() { @@ -117,46 +86,57 @@ async function loadPoints() {
pointsData.value = res.data
tableCol.value = res.data.map((i: any) => ({
label: i.point_name,
prop: i.point_id,
minWidth: '10%',
addr: i.point_id,
}))
tableCol.value.push({
label: '时间',
prop: 'ts',
minWidth: '10%',
addr: 'ts',
})
}
return res
}
const chartDatas = new Map<string, any[]>()
const axisData = new Set<string>()
async function loadDeviceDetails() {
if (!queryParams.pageSize) {
queryParams.pageSize = tableRef.value?.getSize() ?? 100
if (!fullscreenLoading.value) {
openFullScreen()
}
loading.value = true
const poinsRes = await loadPoints()
if (!poinsRes || poinsRes.code !== 0) {
message.error('获取点位数据失败')
loading.value = false
fullscreenLoading.value?.close()
return
}
const res = await getDeviceDetails({
columns: ['ts', ...pointsData.value.map(i => i.point_id)],
columns: columsParams.value,
isLocal: props.isTransfer ? false : true,
limit: queryParams?.pageSize ?? 100,
offset: queryParams.pageNo,
host: props.isTransfer ? (curDevice.value as IOnlineDevice).clientIp : '',
})
if (res.code === 0) {
tableData.value = res.data.results
total.value = res.data.total
res.data.results.forEach((data: any[]) => {
columsParams.value.forEach((col, idx) => {
if (idx === 0) {
axisData.add(data[idx])
}
const ts = data[0]
if (col !== 'ts') {
const colData = chartDatas.get(col)
if (colData) {
colData.push([ts, data[idx]])
} else {
chartDatas.set(col, [[ts, data[idx]]])
}
}
})
})
console.log(chartDatas)
console.log(axisData)
console.log(tableCol.value)
isShowDrawer.value = true
}
loading.value = false
}
function handleJump(page: number) {
queryParams.pageNo = page
loadDeviceDetails()
fullscreenLoading.value?.close()
}
function zmqImport(device: IOfflineDevice) {
@ -178,7 +158,6 @@ function zmqImportCb(msg: PubMsgData) { @@ -178,7 +158,6 @@ function zmqImportCb(msg: PubMsgData) {
const { id, result } = msg
if (result !== 'progress') {
if (result === 'success') {
loading.value = true
loadDeviceDetails()
} else {
message.error(`设备数据获取失败`)
@ -197,10 +176,24 @@ function zmqTimeoutCb(msg: TimeoutMsg) { @@ -197,10 +176,24 @@ function zmqTimeoutCb(msg: TimeoutMsg) {
function handleBeforeClose(done: () => void) {
isShowDrawer.value = false
tableCol.value = []
chartDatas.clear()
axisData.clear()
fullscreenLoading.value = null
done()
}
const fullscreenLoading = ref<any>(null)
const openFullScreen = () => {
fullscreenLoading.value = ElLoading.service({
lock: true,
text: '数据加载中,请稍后...',
background: 'rgba(255, 255, 255, 0.8)',
})
}
defineExpose({
open,
openFullScreen,
})
</script>

234
src/views/stationData/components/newDataChart.vue

@ -0,0 +1,234 @@ @@ -0,0 +1,234 @@
<template>
<div class="device-data-chart">
<v-chart
class="chart"
:option="chartOption"
:autoresize="autoresize"
:loading-options="loadingOpt"
:loading="loading"
ref="chartRef"
@legendselectchanged="changeLegend"
/>
</div>
</template>
<script setup lang="ts">
import VChart from 'vue-echarts'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import type { EChartsOption, SeriesOption } from 'echarts/types/dist/shared.js'
import { LineChart } from 'echarts/charts'
import {
TooltipComponent,
LegendComponent,
DataZoomComponent,
GridComponent,
} from 'echarts/components'
use([
CanvasRenderer,
TooltipComponent,
LineChart,
LegendComponent,
GridComponent,
DataZoomComponent,
])
const ZOOM_HEIGHT = 30
const ZOOM_BOTTOM = 10
type Legend = {
addr: string
label: string
}
const autoresize = {
throttle: 0,
}
const props = defineProps({
title: String,
smooth: Boolean, //线
legends: {
type: Array as PropType<Legend[]>,
default: () => [],
},
axisData: {
type: Array as PropType<string[]>,
default: () => [],
},
chartDatas: {
type: Object as PropType<Map<string, any[]>>,
default: () => new Map(),
},
})
const loading = ref(true)
const loadingOpt = {
type: 'default',
text: '暂无数据',
color: '#c23531',
textColor: '#666',
maskColor: 'rgba(0, 0, 0, 0)',
showSpinner: false,
}
const chartRef = ref()
const chartOption = computed<EChartsOption>(() => {
if (props.chartDatas.size === 0) {
return {}
}
loading.value = false
const tmpSeries: SeriesOption[] = []
for (const legend of props.legends.filter(item => item.addr !== 'ts')) {
const entry = props.chartDatas.get(legend.addr)
const lineData: SeriesOption = {
name: legend.label,
type: 'line',
// symbol: "none",
connectNulls: true,
data: entry,
}
if (props.smooth) {
lineData.smooth = true
} else {
lineData.step = 'middle'
}
tmpSeries.push(lineData)
}
const option: EChartsOption = {
grid: {
left: 60,
right: 198,
top: '5%',
bottom: `25%`,
},
tooltip: {
trigger: 'axis',
confine: true,
appendToBody: true,
},
legend: {
type: 'scroll',
orient: 'vertical',
formatter: name => {
return name.length > 15 ? name.substring(0, 15) + '...' : name
},
tooltip: {
show: true,
},
right: 0,
top: 20,
itemWidth: 10,
itemHeight: 10,
pageIconSize: 12,
data: props.legends.map(item => item.label),
selected: unCheckArr.value.reduce((acc, cur) => {
acc[cur] = false
return acc
}, {} as Record<string, boolean>),
},
xAxis: {
type: 'category',
axisLine: {
//y线
show: true,
lineStyle: {
width: 1,
type: 'solid',
},
},
axisLabel: {
show: false,
},
data: props.axisData,
},
yAxis: {
type: 'value',
splitNumber: 6,
axisLine: {
//y线
show: true,
lineStyle: {
width: 1,
type: 'solid',
},
},
axisLabel: {
margin: 12,
fontSize: 12,
},
splitLine: {
show: true,
lineStyle: {},
},
},
dataZoom: [
{
type: 'inside',
xAxisIndex: 0,
filterMode: 'filter',
start: 90,
end: 100,
},
{
type: 'slider',
xAxisIndex: 0,
filterMode: 'filter',
start: 90,
end: 100,
height: ZOOM_HEIGHT,
bottom: `20%`,
borderColor: '#88abf5',
dataBackground: {
lineStyle: {
color: '#d2dbee',
},
},
selectedDataBackground: {
lineStyle: {
color: '#2c6cf7',
},
},
moveHandleStyle: {
color: '#2c6cf7',
},
},
],
series: tmpSeries,
}
return option
})
const unCheckArr = ref<string[]>([])
function changeLegend(data: { name: string; selected: Record<string, boolean> }) {
const { name, selected } = data
if (!selected[name]) {
unCheckArr.value.push(name)
} else {
unCheckArr.value = unCheckArr.value.filter(item => item !== name)
}
console.log(unCheckArr.value)
}
// useWindowResize(handleResize)
</script>
<style scoped lang="scss">
.device-data-chart {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.chart {
width: 100%;
height: 90%;
min-height: 100px;
}
}
</style>

10
src/views/stationData/components/transferDlg.vue

@ -18,6 +18,8 @@ @@ -18,6 +18,8 @@
class="flex-1"
start-placeholder="开始时间"
end-placeholder="结束时间"
:default-value="defaultDateRange"
:disabled-date="disabledAfterToday"
/></el-row>
</div>
</EdfsDialog>
@ -30,7 +32,13 @@ import { cloneDeep } from 'lodash-es' @@ -30,7 +32,13 @@ import { cloneDeep } from 'lodash-es'
import type { IOnlineDevice } from '../type'
import { useMessage } from '@/composables/useMessage'
const message = useMessage()
const disabledAfterToday = (time: { getTime: () => number }) => {
return time.getTime() > Date.now() //
}
const defaultDateRange = [
dayjs().subtract(1, 'month').startOf('month'),
dayjs().endOf('month'),
] //
const emit = defineEmits<{
'on-save': [PublishMsg<'export'>, IOnlineDevice]
}>()

19
src/views/stationData/transferData.vue

@ -114,6 +114,7 @@ @@ -114,6 +114,7 @@
<div
v-for="i in curTransferLog"
:class="i.status === 'failed' ? 'text-red-500' : ''"
class="text-gray-600"
>
{{ i.msg }}
</div>
@ -275,7 +276,6 @@ function clearTransferData() { @@ -275,7 +276,6 @@ function clearTransferData() {
function zmqTimeoutCb(msg: TimeoutMsg) {
const { device, action } = pubIdWithDevice.get(msg.timeoutId)!
debugger
if (device && action === 'export') {
message.error(`迁移超时,请重新稍后尝试`)
pubIdWithDevice.delete(msg.timeoutId)
@ -376,26 +376,15 @@ onMounted(() => { @@ -376,26 +376,15 @@ onMounted(() => {
const isShowDetails = ref(false)
const deviceDrawerRef = ref<typeof DeviceDrawer>()
function onDeviceDetails(item: IOfflineDevice) {
isShowDetails.value = true
deviceDrawerRef.value?.openFullScreen()
deviceDrawerRef.value?.open(item)
}
</script>
<style scoped lang="scss">
// .transfer-info-wrap {
// @apply flex-col gap-col-10 m-t-10 h-80px;
// .info-item {
// @apply h-32px flex justify-between items-center;
// .info-item-label {
// @apply color-[#6C727F] text-14px font-500;
// }
// .info-item-value {
// @apply color-black text-14px font-500;
// }
// }
// }
.transfer-log-wrap {
height: calc(100% - 20px);
margin-top: 10px;
height: calc(100% - 30px);
@apply border-radius-8px bg-[#F9FAFB] p-10;
:deep(.el-scrollbar) {
height: calc(100% - 20px);

Loading…
Cancel
Save