修改项目树问题 绘制稳态治理分析页面

This commit is contained in:
guanj
2026-06-04 09:08:37 +08:00
parent c2805d7e9e
commit 4f32f84132
42 changed files with 3368 additions and 2287 deletions

View File

@@ -6,31 +6,15 @@
<TableHeader datePicker showExport>
<template v-slot:select>
<el-form-item label="事件类型">
<el-select
v-model.trim="tableStore.table.params.eventType"
clearable
placeholder="请选择事件类型"
>
<el-option
v-for="item in eventList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model.trim="tableStore.table.params.eventType" clearable placeholder="请选择事件类型">
<el-option v-for="item in eventList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="位置">
<el-select
v-model.trim="tableStore.table.params.location"
clearable
placeholder="请选择位置"
>
<el-option
v-for="item in locationList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model.trim="tableStore.table.params.location" clearable placeholder="请选择位置">
<el-option v-for="item in locationList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</template>
@@ -39,14 +23,8 @@
</div>
<el-empty v-else description="请选择设备" class="analyze-dvr-right" />
</div>
<waveFormAnalysis
v-loading="loading"
v-if="isWaveCharts"
ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false"
:wp="wp"
style="padding: 10px"
/>
<waveFormAnalysis v-loading="loading" v-if="isWaveCharts" ref="waveFormAnalysisRef"
@handleHideCharts="isWaveCharts = false" :wp="wp" style="padding: 10px" />
<!-- <div :style="{ height: pageHeight.height }" style="padding: 10px; overflow: hidden" v-if="!view">
<el-row>
<el-col :span="12">
@@ -145,14 +123,26 @@ const tableStore = new TableStore({
}
},
{ title: '事件描述', field: 'showName', minWidth: 150 },
{ title: '发生位置', field: 'evtParamPosition', minWidth: 150 },
{
title: '发生位置', field: 'evtParamPosition', minWidth: 150,
formatter: (row: any) => {
const val = row.cellValue
if (val === null || val === undefined || val === '' || val === '-') return '/'
return val
}
},
{
title: '持续时间(s)',
field: 'evtParamTm',
sortable: true,
minWidth: 110,
formatter: (row: any) => {
return Math.floor(row.cellValue * 10000) / 100
const val = row.cellValue
if (val === null || val === undefined || val === '' || val === '-') return '/'
const num = Number(val)
if (Number.isNaN(num)) return '/'
return Math.floor(num * 10000) / 100
}
},
{
@@ -179,16 +169,19 @@ const tableStore = new TableStore({
type: 'primary',
icon: 'el-icon-DataLine',
render: 'basicButton',
loading: 'loading1',
disabled: row => {
return !row.wavePath && row.evtParamTm < 20
},
click: async row => {
row.loading1 = true
loading.value = true
isWaveCharts.value = true
await analyseWave(row.id)
.then(res => {
isWaveCharts.value = true
loading.value = true
row.loading1 = false
if (res != undefined) {
boxoList.value = row

View File

@@ -0,0 +1,392 @@
<template>
<div class="analyze-steadyState" v-loading="loading">
<analyzeTree ref="treeRef" :showCheckbox="true" :default-checked-keys="checkedKeyIds" @check="onCheck"
:height="40" :engineering="true" />
<div class="analyze-steadyState-right">
<div ref="headerRef">
<TableHeader :showSearch="false" ref="tableHeaderRef" @selectChange="selectChange">
<template v-slot:select>
<el-form-item label="时间:">
<DatePicker ref="datePickerRef"></DatePicker>
</el-form-item>
<el-form-item label="统计指标:">
<el-select style="width: 200px" v-model.trim="formInline.statisticalId" filterable
@change="frequencyFlag" placeholder="请选择">
<el-option v-for="item in zblist" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="谐波次数:" v-show="frequencyShow">
<el-select v-model.trim="formInline.frequency" filterable placeholder="请选择"
style="width: 100px">
<el-option v-for="item in 49" :key="item + 1" :label="item + 1"
:value="item + 1"></el-option>
</el-select>
</el-form-item>
<el-form-item label="值类型:">
<el-select v-model.trim="formInline.valueType" filterable placeholder="请选择">
<el-option v-for="item in typelist" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
</template>
<template v-slot:operation>
<el-button type="primary" @click="search" icon="el-icon-Search">查询</el-button>
<el-button :type="timeControl ? 'primary' : ''" icon="el-icon-Sort" @click="setTimeControl">
缺失数据
</el-button>
</template>
</TableHeader>
</div>
<el-empty description="暂无数据" v-if="!echartsData" style="flex: 1"></el-empty>
<template v-else>
<div :style="echartHeight">
<MyEchart :options="echartsData" />
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import { mainHeight } from '@/utils/layout'
import analyzeTree from '@/components/tree/govern/analyzeTree.vue'
import { queryByCode, queryCsDictTree } from '@/api/system-boot/dictTree'
import { queryCommonStatisticalByTime } from '@/api/cs-harmonic-boot/stable'
import DatePicker from '@/components/form/datePicker/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { yMethod, completeTimeSeries } from '@/utils/echartMethod'
import TableHeader from '@/components/table/header/index.vue'
const MAX_CHECK = 2
const defaultCheckedKeys = ref<any[]>([])
const checkedKeyIds = computed(() => defaultCheckedKeys.value.map(item => item.id))
const treeRef = ref()
const isSyncing = ref(false)
const tableHeaderRef = ref()
const headerRef = ref()
const echartHeight = ref(mainHeight(80))
const loading = ref(false)
const echartsData = ref<any>(null)
const datePickerRef = ref()
const timeControl = ref(false)
const timeFlag = ref(true)
const frequencyShow = ref(false)
const dataLists = ref<any[]>([])
const zblist = ref<any[]>([])
const formInline = reactive({
statisticalId: '',
valueType: '',
startTime: '',
endTime: '',
frequency: '' as string | number
})
const typelist = [
{ label: '平均值', value: 'avg' },
{ label: '最大值', value: 'max' },
{ label: '最小值', value: 'min' },
{ label: 'CP95值', value: 'cp95' }
]
const lineStyle = [
{ type: 'solid', width: 3 },
{ type: 'dotted', width: 3 },
{ type: 'dashed', width: 3 }
]
const getEngineeringTree = () => treeRef.value?.treRef?.treeRef4
const init = () => {
return queryByCode('portable-harmonic-jx').then(res => {
return queryCsDictTree(res.data?.id).then(res => {
zblist.value = res.data.map((item: any) => ({
value: item.id,
label: item.name,
...item
}))
formInline.statisticalId = zblist.value[0]?.value
formInline.valueType = typelist[0].value
})
})
}
onMounted(() => {
init()
})
const onCheck = (_data: any, { checkedNodes }: { checkedNodes: any[] }) => {
if (isSyncing.value) return
const tree = getEngineeringTree()
if (!tree) return
if (checkedNodes.length <= MAX_CHECK) {
defaultCheckedKeys.value = checkedNodes.map(node => ({ ...node }))
if (checkedNodes.length === 0) {
echartsData.value = null
dataLists.value = []
}
return
}
const allowedIds = new Set(defaultCheckedKeys.value.map(item => item.id))
const extras = checkedNodes.filter(node => !allowedIds.has(node.id))
if (defaultCheckedKeys.value.length >= MAX_CHECK) {
isSyncing.value = true
extras.forEach(node => tree.setChecked(node.id, false, false))
nextTick(() => {
isSyncing.value = false
})
ElMessage.warning(`最多只能选择${MAX_CHECK}`)
return
}
const room = MAX_CHECK - defaultCheckedKeys.value.length
extras.slice(0, room).forEach(node => {
if (!defaultCheckedKeys.value.some(item => item.id === node.id)) {
defaultCheckedKeys.value.push({ ...node })
}
})
const rejected = extras.slice(room)
if (rejected.length) {
isSyncing.value = true
rejected.forEach(node => tree.setChecked(node.id, false, false))
nextTick(() => {
isSyncing.value = false
})
ElMessage.warning(`最多只能选择${MAX_CHECK}`)
}
}
const search = async () => {
if (defaultCheckedKeys.value.length === 0) {
ElMessage.warning('请选择设备')
return
}
if (zblist.value.length === 0) {
await init()
}
if (timeFlag.value) {
datePickerRef.value?.setInterval(5)
timeFlag.value = false
}
loading.value = true
formInline.startTime = datePickerRef.value.timeValue[0]
formInline.endTime = datePickerRef.value.timeValue[1]
if (!frequencyShow.value) {
formInline.frequency = ''
}
try {
const results = await Promise.all(
defaultCheckedKeys.value.map(device =>
queryCommonStatisticalByTime({
...formInline,
devId: device.id
}).then(({ data }: { data: any[] }) => ({
deviceName: device.name,
data
}))
)
)
dataLists.value = results.flatMap(({ deviceName, data }) =>
data.map((item: any) => ({
...item,
anotherName:
defaultCheckedKeys.value.length > 1 ? `${deviceName}_${item.anotherName}` : item.anotherName
}))
)
setEchart()
} catch {
loading.value = false
}
}
const setEchart = () => {
loading.value = true
const data = JSON.parse(JSON.stringify(dataLists.value))
if (data.length) {
const list = processingOfData(data, 'unit')
echartsData.value = {}
const legend: any[] = []
const xAxis: any[] = []
const yAxis: any[] = []
const series: any[] = []
const color: any[] = []
data.forEach((item: any) => {
if (!xAxis.includes(item.time)) {
xAxis.push(item.time)
}
})
const units = Object.keys(list)
for (const unit in list) {
const [min, max] = yMethod(list[unit].map((item: any) => item.statisticalData))
yAxis.push({
name: unit == 'null' ? '' : unit,
type: 'value',
min,
max,
axisLine: {
show: true,
lineStyle: { color: '#333' }
}
})
const anotherList = processingOfData(list[unit], 'anotherName')
for (const k in anotherList) {
const lineName = lineStyle[Object.keys(anotherList).indexOf(k)]
const phaseList = processingOfData(anotherList[k], 'phase')
for (const j in phaseList) {
color.push(j == 'A' ? '#DAA520' : j == 'B' ? '#2E8B57' : j == 'C' ? '#A52a2a' : '#0000CC')
legend.push(
j == 'T' ? k : j == 'A' ? `A相_${k}` : j == 'B' ? `B相_${k}` : j == 'C' ? `C相_${k}` : j
)
series.push({
name: j == 'T' ? k : j == 'A' ? `A相_${k}` : j == 'B' ? `B相_${k}` : j == 'C' ? `C相_${k}` : j,
symbol: 'none',
smooth: true,
type: 'line',
data: timeControl.value
? completeTimeSeries(
phaseList[j].map((item: any) => [
item.time,
Math.floor(item.statisticalData * 100) / 100,
unit,
lineName.type
])
)
: phaseList[j].map((item: any) => [
item.time,
Math.floor(item.statisticalData * 100) / 100,
unit,
lineName.type
]),
lineStyle: lineName,
yAxisIndex: units.indexOf(unit)
})
}
}
}
echartsData.value = {
title: {
text: zblist.value.filter(item => item.id == formInline.statisticalId)[0]?.name
},
tooltip: {
axisPointer: {
type: 'cross',
label: { color: '#fff', fontSize: 16 }
},
textStyle: { color: '#fff', fontStyle: 'normal', opacity: 0.35, fontSize: 14 },
backgroundColor: 'rgba(0,0,0,0.55)',
borderWidth: 0,
formatter(params: any) {
const xname = params[0].value[0]
let str = `${xname}<br>`
params.forEach((el: any) => {
let marker = ''
if (el.value[3] == 'dashed') {
for (let i = 0; i < 3; i++) {
marker += `<span style="display:inline-block;border: 2px ${el.color} solid;margin-right:5px;width:10px;height:0px;background-color:#ffffff00;"></span>`
}
} else {
marker = `<span style="display:inline-block;border: 2px ${el.color} ${el.value[3]};margin-right:5px;width:40px;height:0px;background-color:#ffffff00;"></span>`
}
str += `${marker}${el.seriesName.split('(')[0]}${el.value[1] != null ? el.value[1] + ' ' + (el.value[2] == 'null' ? '' : el.value[2]) : '-'
}<br>`
})
return str
}
},
legend: {
itemWidth: 20,
itemHeight: 20,
itemStyle: { opacity: 0 },
type: 'scroll',
top: 25
},
grid: {
left: '20px',
right: '40px',
bottom: '50px',
top: '80px',
containLabel: true
},
toolbox: { feature: { saveAsImage: {} } },
color,
xAxis: {
name: '',
type: 'time',
axisLabel: {
formatter: { day: '{MM}-{dd}', month: '{MM}', year: '{yyyy}' }
}
},
yAxis,
options: { series }
}
} else {
echartsData.value = null
}
loading.value = false
}
const setTimeControl = () => {
timeControl.value = !timeControl.value
setEchart()
}
const processingOfData = (data: any, type: string) => {
const groupedData: any = {}
data.forEach((item: any) => {
if (!groupedData[item[type]]) {
groupedData[item[type]] = []
}
groupedData[item[type]].push(item)
})
return groupedData
}
const frequencyFlag = () => {
const name = zblist.value.filter(item => item.id == formInline.statisticalId)[0]?.name
if (name?.includes('含有率') || name?.includes('幅值')) {
frequencyShow.value = true
formInline.frequency = 2
} else {
frequencyShow.value = false
}
tableHeaderRef.value?.computedSearchRow()
}
const selectChange = () => {
setTimeout(() => {
echartHeight.value = mainHeight(23 + headerRef.value.offsetHeight)
}, 100)
}
</script>
<style lang="scss">
.analyze-steadyState {
display: flex;
height: 100%;
min-height: 0;
&-right {
height: 100%;
overflow: hidden;
flex: 1;
padding: 10px 10px 10px 0;
display: flex;
flex-direction: column;
}
}
</style>
<style lang="scss" scoped>
.el-select {
min-width: 100px;
}
</style>

View File

@@ -0,0 +1,204 @@
<template>
<div class="default-main report-zl-page1" :style="height">
<div class="report-zl-sidebar">
<!-- <pointTreeWx :default-expand-all="false" template @node-click="handleNodeClick" @init="handleNodeClick"
@Policy="stencil">
</pointTreeWx> -->
<CloudDeviceEntryTree ref="TerminalRef" :height="37" template @Policy="stencil" @node-click="handleNodeClick"
@init="handleNodeClick"></CloudDeviceEntryTree>
</div>
<div class="report-zl-main">
<TableHeader datePicker ref="TableHeaderRef" :showReset="false">
<template v-slot:select>
<!-- <el-form-item label="时间:">
<DatePicker ref="datePickerRef"></DatePicker>
</el-form-item> -->
<el-form-item label="模板策略">
<el-select v-model.trim="Template" @change="changetype" placeholder="请选择模版" value-key="id">
<el-option v-for="item in templatePolicy" :key="item.id" :label="item.excelName"
:value="item"></el-option>
</el-select>
</el-form-item>
<!-- <el-form-item label="监测对象">
<el-select
filterable
v-model="tableStore.table.params.sensitiveUserId"
placeholder="请选择监测对象"
clearable
>
<el-option v-for="item in idList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item> -->
</template>
<template #operation>
<el-button icon="el-icon-Download" type="primary" @click="exportEvent">导出</el-button>
</template>
</TableHeader>
<div class="box" v-loading="tableStore.table.loading">
<div id="luckysheet" class="report-zl-sheet" v-if="tableStore.table.data.length > 0"></div>
<el-empty class="report-zl-sheet" v-else description="暂无数据" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue'
import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { exportExcel } from '@/views/system/reportForms/export.js'
import DatePicker from '@/components/form/datePicker/time.vue'
import CloudDeviceEntryTree from '@/components/tree/govern/cloudDeviceEntryTreeZL.vue'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
import { ElMessage } from 'element-plus'
// import data from './123.json'
const height = mainHeight(70)
const dictData = useDictData()
const TableHeaderRef = ref()
const dotList: any = ref({})
const Template: any = ref({})
const reportForm: any = ref('')
const datePickerRef = ref()
const templatePolicy: any = ref([])
const name = ref('')
const tableStore = new TableStore({
url: '/cs-harmonic-boot/customReport/getSensitiveUserReport',
method: 'POST',
column: [],
showPage: false,
beforeSearchFun: () => {
tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id
tableStore.table.params.sensitiveUserId = dotList.value.id
// ;(tableStore.table.params.startTime = datePickerRef.value.timeValue[0]),
// (tableStore.table.params.endTime = datePickerRef.value.timeValue[1]),
if (!tableStore.table.params.tempId) {
return ElMessage.warning('请选择模板')
}
delete tableStore.table.params.searchBeginTime
delete tableStore.table.params.searchEndTime
delete tableStore.table.params.timeFlag
},
loadCallback: () => {
console.log('🚀 ~ tableStore.table:', tableStore.table.data)
name.value = dotList.value.name
// tableStore.table.data.forEach((item: any) => {
// item.data1 ? (item.data = JSON.parse(item.data1)) : ''
// item.celldata.forEach((k: any) => {
// item.data[k.r][k.c].v ? (item.data[k.r][k.c] = k.v) : ''
// })
// })
setTimeout(() => {
luckysheet.create({
container: 'luckysheet',
title: '', // 表 头名
lang: 'zh', // 中文
showtoolbar: false, // 是否显示工具栏
showinfobar: false, // 是否显示顶部信息栏
showsheetbar: true, // 是否显示底部sheet按钮
allowEdit: false, // 禁止所有编辑操作(必填)
data: tableStore.table.data
// tableStore.table.data
})
}, 10)
}
})
provide('tableStore', tableStore)
tableStore.table.params.resourceType = 1
tableStore.table.params.customType = 1
const flag = ref(true)
onMounted(() => {
initListByIds()
})
const idList = ref([])
// 监测对象
const initListByIds = () => {
getListByIds({}).then((res: any) => {
if (res.data.length > 0) {
idList.value = res.data
if (!tableStore.table.params.sensitiveUserId && idList.value?.length > 0) {
tableStore.table.params.sensitiveUserId = idList.value[0].id
}
// templateListData()
}
})
}
const stencil = (val: any) => {
console.log('🚀 ~ stencil ~ val:', val)
templatePolicy.value = val.filter((item: any) => item.excelType == '4')
Template.value = templatePolicy.value[0]
reportForm.value = templatePolicy.value[0]?.excelType
}
const changetype = (val: any) => {
reportForm.value = val.excelType
}
const handleNodeClick = (data: any, node: any) => {
if (data?.level == 3) {
dotList.value = data
setTimeout(() => {
tableStore.index()
}, 500)
} else {
tableStore.table.loading = false
}
}
const exportEvent = () => {
const now = new Date()
const year = now.getFullYear() // 4位年份
const month = now.getMonth() + 1 // 月份0-11需+1
const day = now.getDate() // 日期1-31
// 格式化YYYY - MM - DD补零
const formattedDate = `${year}${String(month).padStart(2, '0')}${String(day).padStart(2, '0')}`
exportExcel(luckysheet.getAllSheets(), name.value + formattedDate)
}
</script>
<style lang="scss">
.report-zl-page1 {
display: flex;
overflow: hidden;
.cn-tree {
padding: 0 10px 0 0 !important;
}
}
.report-zl-sidebar {
width: 280px;
flex-shrink: 0;
min-height: 0;
overflow: hidden;
}
.report-zl-main {
flex: 1;
min-width: 0;
min-height: 0;
background: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
}
.box {
flex: 1;
min-height: 0;
padding: 10px 0;
overflow: hidden;
}
.report-zl-sheet {
height: 100%;
}
</style>
<style></style>

View File

@@ -0,0 +1,37 @@
<template>
<div class="default-main" :style="{ height: pageHeight.height }">
<el-tabs v-model="activeTab" type="border-card">
<el-tab-pane label="图表" name="chart">
<chart v-if="activeTab === 'chart'"/>
</el-tab-pane>
<el-tab-pane label="报表" name="report">
<report v-if="activeTab === 'report'"/>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { mainHeight } from '@/utils/layout'
import chart from './components/chart.vue'
import report from './components/report.vue'
defineOptions({
name: 'govern/analyze/steadyState'
})
const pageHeight = mainHeight(20)
const activeTab = ref('chart')
</script>
<style scoped lang="scss">
:deep(.el-tabs__content) {
padding: 0;
}
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div class="default-main">
<h1>暂态治理分析</h1>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: 'govern/analyze/transient'
})
</script>
<style scoped lang="scss"></style>

View File

@@ -16,12 +16,17 @@
</el-select>
</el-form-item>
<el-form-item>
<el-radio-group v-model="searchForm.dataLevel" :disabled="props?.TrendList?.lineType != 1"
@change="init()">
<el-radio-group v-model="searchForm.dataLevel" @change="init()">
<el-radio-button label="一次值" value="Primary" />
<el-radio-button label="二次值" value="Secondary" />
</el-radio-group>
</el-form-item>
<el-form-item>
<el-radio-group v-model="searchForm.dataModel" @change="init()">
<el-radio-button label="全量" value="0" />
<el-radio-button label="增量" value="1" />
</el-radio-group>
</el-form-item>
<!-- <el-form-item label="统计类型">
<el-select style="min-width: 120px !important" placeholder="请选择" v-model="searchForm.valueType">
<el-option value="max" label="最大值"></el-option>
@@ -102,6 +107,7 @@ searchForm.value = {
searchBeginTime: '',
searchEndTime: '',
dataLevel: 'Primary',
dataModel: '0',
valueType: 'avg'
}
//统计指标
@@ -157,6 +163,7 @@ const init = async () => {
list: lists,
// valueType: searchForm.value.type,
dataLevel: searchForm.value.dataLevel,
dataModel: searchForm.value.dataModel,
// valueType: searchForm.value.valueType,
startTime: datePickerRef.value && datePickerRef.value.timeValue[0],
endTime: datePickerRef.value && datePickerRef.value.timeValue[1]

View File

@@ -111,10 +111,11 @@ const tableStore: any = new TableStore({
},
click: async row => {
row.loading1 = true
isWaveCharts.value = true
loading.value = true
await analyseWave(row.id)
.then(res => {
isWaveCharts.value = true
loading.value = true
row.loading1 = false
if (res != undefined) {
boxoList.value = row

View File

@@ -21,9 +21,9 @@
</el-tooltip>
<Icon @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
<!-- <Icon @click="onMenuCollapse" :name="menuCollapse ? 'el-icon-Expand' : 'el-icon-Fold'"
:class="menuCollapse ? 'unfold' : ''" size="18" class="fold ml10 menu-collapse"
style="cursor: pointer" />
style="cursor: pointer" /> -->
</div>
<el-tree style="flex: 1; overflow: auto" :props="defaultProps" highlight-current
:filter-node-method="filterNode" node-key="id" v-bind="$attrs" default-expand-all :data="tree"

View File

@@ -95,10 +95,11 @@ const tableStore = new TableStore({
},
click: async row => {
row.loading1 = true
loading.value = true
isWaveCharts.value = true
await analyseWave(row.id)
.then(res => {
loading.value = true
isWaveCharts.value = true
row.loading1 = false
if (res != undefined) {
boxoList.value = row

View File

@@ -0,0 +1,569 @@
<template>
<div class="default-main manage-process" :style="{ height: pageHeight.height }">
<DeviceTree @node-click="nodeClick" @init="nodeClick" @deviceTypeChange=""></DeviceTree>
<div class="manage-process-right">
<div class="process-flow-section">
<el-descriptions title="数据链路">
</el-descriptions>
<div class="process-flow-wrap">
<div class="process-flow">
<template v-for="(node, index) in flowNodes" :key="node.label">
<div class="process-step">
<div class="process-node" :class="`process-node--${getNodeState(index)}`">
<div class="process-node__icon" :style="{ color: getNodeColor(index) }">
<el-icon v-if="node.isEl"><component :is="node.icon" /></el-icon>
<component v-else :is="node.icon" :color="getNodeColor(index)" />
</div>
<div class="process-node__name">{{ node.label }}</div>
</div>
</div>
<div
v-if="index < flowNodes.length - 1"
class="process-bridge"
:class="`process-bridge--${getLineState(index)}`"
>
<svg class="process-bridge__svg" viewBox="0 0 120 16" preserveAspectRatio="none">
<line class="process-bridge__bg" x1="0" y1="8" x2="104" y2="8" />
<line class="process-bridge__active" x1="0" y1="8" x2="104" y2="8" />
<line
v-if="getLineState(index) === 'flowing'"
class="process-bridge__flow"
x1="0"
y1="8"
x2="104"
y2="8"
/>
<polygon class="process-bridge__head" points="106,4 116,8 106,12" />
</svg>
</div>
</template>
</div>
</div>
</div>
<div class="process-list-section">
<el-descriptions title="数据列表" />
<div class="process-list-body">
<el-table :data="processTableData" border stripe height="100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="name" label="节点" min-width="100" align="center" />
<el-table-column prop="link" label="上游链路" min-width="140" align="center" />
<el-table-column prop="status" label="连接状态" min-width="100" align="center">
<template #default="{ row }">
<span :class="`process-table-status process-table-status--${row.state}`">
{{ row.status }}
</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { h, watch, onUnmounted, ref, computed, defineComponent } from 'vue'
import { Monitor, Coin } from '@element-plus/icons-vue'
import { mainHeight } from '@/utils/layout'
import DeviceTree from '@/components/tree/govern/deviceTree.vue'
defineOptions({
name: 'process'
})
const pageHeight = mainHeight(20)
/** 连接状态null-初始0-终端绿/终端↔前置1-前置绿/前置↔MQTT2-MQTT绿/MQTT↔数据库3-数据库绿/数据库↔Web4-全部完成 */
const linkStatus = ref<number | null>(2)
const COLOR = {
pending: '#94a3b8',
success: '#009f05',
error: '#e05252'
}
const createProcessIcon = (render: (color: string) => ReturnType<typeof h>[]) =>
defineComponent({
props: {
color: { type: String, default: COLOR.pending }
},
setup(props) {
return () =>
h(
'svg',
{
viewBox: '0 0 48 48',
fill: 'none',
xmlns: 'http://www.w3.org/2000/svg',
class: 'process-node__svg'
},
render(props.color)
)
}
})
/** 终端:采集设备 */
const IconTerminal = createProcessIcon(c => [
h('rect', { x: 8, y: 10, width: 32, height: 22, rx: 3, stroke: c, 'stroke-width': 2.2 }),
h('rect', { x: 14, y: 15, width: 20, height: 12, rx: 1.5, stroke: c, 'stroke-width': 1.5, opacity: 0.45 }),
h('path', { d: 'M4 36h40', stroke: c, 'stroke-width': 2.2, 'stroke-linecap': 'round' }),
h('path', { d: 'M22 32v4', stroke: c, 'stroke-width': 2.2, 'stroke-linecap': 'round' }),
h('circle', { cx: 38, cy: 8, r: 3, fill: c, opacity: 0.85 })
])
/** 前置:双机服务器 */
const IconFrontEnd = createProcessIcon(c => [
h('rect', { x: 10, y: 8, width: 28, height: 13, rx: 2.5, stroke: c, 'stroke-width': 2.2, fill: 'rgba(148,163,184,0.08)' }),
h('circle', { cx: 16, cy: 14.5, r: 2, fill: c }),
h('line', { x1: 22, y1: 14.5, x2: 34, y2: 14.5, stroke: c, 'stroke-width': 1.5, opacity: 0.5 }),
h('rect', { x: 10, y: 27, width: 28, height: 13, rx: 2.5, stroke: c, 'stroke-width': 2.2, fill: 'rgba(148,163,184,0.08)' }),
h('circle', { cx: 16, cy: 33.5, r: 2, fill: c }),
h('line', { x1: 22, y1: 33.5, x2: 34, y2: 33.5, stroke: c, 'stroke-width': 1.5, opacity: 0.5 })
])
/** MQTT云 + 节点 */
const IconMqtt = createProcessIcon(c => [
h('circle', { cx: 24, cy: 26, r: 2.5, fill: c }),
h('circle', { cx: 12, cy: 16, r: 2, fill: c, opacity: 0.75 }),
h('circle', { cx: 36, cy: 16, r: 2, fill: c, opacity: 0.75 }),
h('circle', { cx: 12, cy: 36, r: 2, fill: c, opacity: 0.75 }),
h('circle', { cx: 36, cy: 36, r: 2, fill: c, opacity: 0.75 }),
h('line', { x1: 24, y1: 26, x2: 12, y2: 16, stroke: c, 'stroke-width': 1.4, opacity: 0.55 }),
h('line', { x1: 24, y1: 26, x2: 36, y2: 16, stroke: c, 'stroke-width': 1.4, opacity: 0.55 }),
h('line', { x1: 24, y1: 26, x2: 12, y2: 36, stroke: c, 'stroke-width': 1.4, opacity: 0.55 }),
h('line', { x1: 24, y1: 26, x2: 36, y2: 36, stroke: c, 'stroke-width': 1.4, opacity: 0.55 }),
h('path', {
d: 'M15 24c0-4.5 3.5-7.5 9-7.5s9 3 9 7.5c3.5 0 6 2.5 6 6s-2.5 6-6 6H15c-3.5 0-6-2.5-6-6s2.5-6 6-6z',
stroke: c,
'stroke-width': 2,
fill: '#fff'
}),
h('text', { x: 24, y: 27.5, 'text-anchor': 'middle', fill: c, 'font-size': 7.5, 'font-weight': 'bold' }, 'MQTT')
])
const flowNodes = [
{ label: '终端', icon: IconTerminal },
{ label: '前置', icon: IconFrontEnd },
{ label: 'MQTT', icon: IconMqtt },
{ label: '数据库', icon: Coin, isEl: true },
{ label: 'Web', icon: Monitor, isEl: true }
]
/** 全部完成状态值 */
const COMPLETE_STATUS = 4
/** 连线数量 */
const LINE_COUNT = flowNodes.length - 1
const createErrorArray = <T,>(length: number, value: T) => Array.from({ length }, () => value)
type NodeState = 'pending' | 'success' | 'error'
type LineState = 'idle' | 'flowing' | 'success' | 'error'
const lineErrors = ref(createErrorArray(LINE_COUNT, false))
const nodeErrors = ref(createErrorArray(flowNodes.length, false))
let timeoutTimer: ReturnType<typeof setTimeout> | null = null
const clearTimeoutTimer = () => {
if (timeoutTimer) {
clearTimeout(timeoutTimer)
timeoutTimer = null
}
}
const resetErrorsFrom = (fromIndex: number) => {
for (let i = fromIndex; i < lineErrors.value.length; i++) {
lineErrors.value[i] = false
}
for (let i = fromIndex + 1; i < nodeErrors.value.length; i++) {
nodeErrors.value[i] = false
}
}
const resetAllErrors = () => {
lineErrors.value = createErrorArray(LINE_COUNT, false)
nodeErrors.value = createErrorArray(flowNodes.length, false)
}
const startTimeout = (status: number | null) => {
clearTimeoutTimer()
if (status === null || status >= COMPLETE_STATUS) return
const capturedStatus = status
timeoutTimer = setTimeout(() => {
if (linkStatus.value !== capturedStatus) return
lineErrors.value[capturedStatus] = true
nodeErrors.value[capturedStatus + 1] = true
}, 5000)
}
watch(
linkStatus,
(val, oldVal) => {
const prev = oldVal ?? null
if (val !== null && (prev === null || val > prev)) {
resetErrorsFrom(val)
} else if (val !== null && prev !== null && val < prev) {
resetAllErrors()
} else if (val === null) {
resetAllErrors()
}
startTimeout(val)
},
{ immediate: true }
)
onUnmounted(clearTimeoutTimer)
const getNodeState = (index: number): NodeState => {
if (nodeErrors.value[index]) return 'error'
if (linkStatus.value === null) return 'pending'
if (linkStatus.value >= COMPLETE_STATUS) return 'success'
if (index <= linkStatus.value) return 'success'
return 'pending'
}
const getLineState = (index: number): LineState => {
if (lineErrors.value[index]) return 'error'
if (linkStatus.value === null) return 'idle'
if (linkStatus.value >= COMPLETE_STATUS) return 'success'
if (index < linkStatus.value) return 'success'
if (index === linkStatus.value) return 'flowing'
return 'idle'
}
const getNodeColor = (index: number) => COLOR[getNodeState(index)]
const getNodeStatusText = (index: number) => {
const state = getNodeState(index)
if (state === 'error') return '连接失败'
if (state === 'success') return '连接成功'
if (linkStatus.value !== null && linkStatus.value < COMPLETE_STATUS && index === linkStatus.value + 1) {
return '连接中'
}
return '待连接'
}
const processTableData = computed(() =>
flowNodes.map((node, index) => ({
name: node.label,
link: index === 0 ? '-' : `${flowNodes[index - 1].label}${node.label}`,
status: getNodeStatusText(index),
state: getNodeState(index)
}))
)
const nodeClick = async (e: anyObj) => {
console.log('点击设备树节点')
}
</script>
<style scoped lang="scss">
$green: #009f05;
$green-light: #e8f7e9;
$red: #e05252;
$red-light: #fdeeee;
$gray: #cbd5e1;
$gray-text: #94a3b8;
$node-min: 110px;
$node-max: 120px;
$bridge-min: 60px;
.manage-process {
display: flex;
height: 100%;
&-right {
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
min-height: 0;
overflow: hidden;
padding: 10px 10px 10px 0;
:deep(.el-descriptions__header) {
height: 36px;
margin-bottom: 7px;
display: flex;
align-items: center;
flex-shrink: 0;
}
}
}
.process-flow-section {
flex-shrink: 0;
}
.process-list-section {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
margin-top: 8px;
}
.process-list-body {
flex: 1;
min-height: 0;
}
.process-table-status {
&--success {
color: $green;
}
&--error {
color: $red;
}
&--pending {
color: $gray-text;
}
}
.process-flow-wrap {
width: 100%;
display: flex;
align-items: flex-start;
padding-bottom: 8px;
padding: 0 10%;
overflow-x: auto;
}
.process-flow {
display: flex;
align-items: center;
width: 100%;
min-width: min(100%, calc(#{$node-min} * 5 + #{$bridge-min} * 4));
min-height: $node-min;
box-sizing: border-box;
}
.process-step {
flex: 1 1 $node-min;
min-width: $node-min;
max-width: $node-max;
display: flex;
justify-content: center;
}
.process-node {
position: relative;
width: 100%;
min-width: $node-min;
min-height: $node-min;
max-width: $node-max;
aspect-ratio: 1;
height: auto;
padding: clamp(8px, 8%, 12px);
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
border-radius: 16px;
background: #f8fafc;
border: 1px solid #e2e8f0;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
&__index {
position: absolute;
top: 8px;
left: 10px;
font-size: 11px;
font-weight: 700;
color: #cbd5e1;
font-variant-numeric: tabular-nums;
transition: color 0.3s;
}
&__icon {
display: flex;
align-items: center;
justify-content: center;
width: clamp(52px, 48%, 60px);
height: clamp(52px, 48%, 60px);
border-radius: 12px;
background: #fff;
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
.el-icon {
font-size: clamp(34px, 32%, 40px);
}
.process-node__svg {
width: clamp(34px, 32%, 40px);
height: clamp(34px, 32%, 40px);
display: block;
}
}
&__name {
margin-top: 8px;
font-size: clamp(13px, 12%, 15px);
font-weight: 600;
color: #334155;
letter-spacing: 1px;
line-height: 1.2;
}
&__status {
margin-top: 4px;
font-size: clamp(12px, 11%, 13px);
color: $gray-text;
transition: color 0.3s;
line-height: 1.2;
}
&--success {
background: linear-gradient(160deg, #fff 0%, $green-light 100%);
border-color: rgba(0, 159, 5, 0.25);
box-shadow: 0 4px 16px rgba(0, 159, 5, 0.1);
.process-node__index {
color: rgba(0, 159, 5, 0.45);
}
.process-node__icon {
background: #fff;
box-shadow: 0 0 0 2px rgba(0, 159, 5, 0.15);
}
.process-node__status {
color: $green;
}
}
&--error {
background: linear-gradient(160deg, #fff 0%, $red-light 100%);
border-color: rgba(224, 82, 82, 0.3);
box-shadow: 0 4px 16px rgba(224, 82, 82, 0.1);
.process-node__index {
color: rgba(224, 82, 82, 0.45);
}
.process-node__icon {
box-shadow: 0 0 0 2px rgba(224, 82, 82, 0.15);
}
.process-node__status {
color: $red;
}
}
}
.process-bridge {
flex: 1 1 0;
min-width: $bridge-min;
height: 25px;
padding: 0 2px;
align-self: center;
&__svg {
width: 100%;
height: 100%;
overflow: visible;
display: block;
}
&__bg {
stroke: #e8edf3;
stroke-width: 5;
stroke-linecap: round;
transition: stroke 0.4s;
}
&__active {
stroke: transparent;
stroke-width: 3;
stroke-linecap: round;
transition: stroke 0.4s;
}
&__flow {
stroke: $green;
stroke-width: 3;
stroke-linecap: round;
stroke-dasharray: 5 9;
animation: bridge-dash 0.85s linear infinite;
}
&__head {
fill: #cbd5e1;
transition: fill 0.4s;
}
&--success {
.process-bridge__active {
stroke: $green;
filter: drop-shadow(0 0 3px rgba(0, 159, 5, 0.35));
}
.process-bridge__head {
fill: $green;
}
}
&--error {
.process-bridge__active {
stroke: $red;
filter: drop-shadow(0 0 3px rgba(224, 82, 82, 0.35));
}
.process-bridge__head {
fill: $red;
}
}
&--flowing {
.process-bridge__active {
stroke: #dce3ec;
stroke-width: 2;
}
.process-bridge__head {
fill: $green;
animation: head-pulse 1.2s ease-in-out infinite;
}
}
&--idle {
.process-bridge__active {
stroke: transparent;
}
.process-bridge__head {
fill: #cbd5e1;
}
}
}
@keyframes bridge-dash {
to {
stroke-dashoffset: -14;
}
}
@keyframes head-pulse {
0%,
100% {
opacity: 0.45;
transform: translateX(0);
}
50% {
opacity: 1;
}
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main manage-realTime" :style="{ height: pageHeight.height }">
<DeviceTree @node-click="nodeClick" @init="" @deviceTypeChange=""></DeviceTree>
<DeviceTree @node-click="nodeClick" @init="nodeClick" @deviceTypeChange=""></DeviceTree>
<div class="manage-realTime-right">
<div class="time-container">
<div>
@@ -33,7 +33,7 @@ import DeviceTree from '@/components/tree/govern/deviceTree.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import { formatToDateTime } from '@/utils/dateUtil'
defineOptions({
name: 'govern/manage/realTime'
name: 'manage/realTime'
})
//页面属性

View File

@@ -1,15 +1,14 @@
<template>
<div class="default-main" :style="height">
<splitpanes style="height: 100%" id="navigation-splitpanes">
<pane :size="size">
<div class="default-main" :style="height" style="display: flex; height: 100%; overflow: hidden">
<div style="width: 280px; flex-shrink: 0; height: 100%; overflow: hidden">
<pointTree
ref="TerminalRef"
@node-click="handleNodeClick"
@init="handleNodeClick"
@pointTypeChange="pointTypeChange"
></pointTree>
</pane>
<pane style="background: #fff" :style="height">
</div>
<div style="flex: 1; min-width: 0; background: #fff" :style="height">
<TableHeader ref="TableHeaderRef" datePicker :show-search="false">
<template v-slot:select>
<el-form-item label="客户名称">
@@ -58,15 +57,12 @@
/>
</div>
</div>
</pane>
</splitpanes>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, provide } from 'vue'
import 'splitpanes/dist/splitpanes.css'
import { Splitpanes, Pane } from 'splitpanes'
import { ref, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import TableHeader from '@/components/table/header/index.vue'
@@ -82,7 +78,6 @@ defineOptions({
})
const height = mainHeight(20)
const size = ref(0)
const dictData = useDictData()
const TableHeaderRef = ref()
const dotList: any = ref({})
@@ -98,13 +93,6 @@ const tableStore = new TableStore({
})
provide('tableStore', tableStore)
onMounted(() => {
const dom = document.getElementById('navigation-splitpanes')
if (dom) {
size.value = Math.round((180 / dom.offsetHeight) * 100)
}
})
const pointTypeChange = (val: any, obj: any) => {
handleNodeClick(obj)
}
@@ -187,10 +175,6 @@ const exportEvent = () => {
}
</script>
<style lang="scss">
.splitpanes.default-theme .splitpanes__pane {
background: #eaeef1;
}
.box {
padding: 10px;
}

View File

@@ -1,12 +1,11 @@
<template>
<div class="default-main" :style="height">
<splitpanes style="height: 100%" class="default-theme" id="navigation-splitpanes">
<pane :size="size">
<div class="default-main report-zl-page" :style="height">
<div class="report-zl-sidebar">
<pointTreeWx :default-expand-all="false" template @node-click="handleNodeClick" @init="handleNodeClick"
@Policy="stencil">
</pointTreeWx>
</pane>
<pane :size="(100 - size)" style="background: #fff" :style="height">
</div>
<div class="report-zl-main">
<TableHeader ref="TableHeaderRef" :showReset="false">
<template v-slot:select>
<el-form-item label="时间:">
@@ -24,33 +23,28 @@
</template>
</TableHeader>
<div class="box" v-loading="tableStore.table.loading">
<div id="luckysheet"
:style="`height: calc(${tableStore.table.height} + 45px)`"
<div id="luckysheet" class="report-zl-sheet"
v-if="tableStore.table.data.length > 0"></div>
<el-empty :style="`height: calc(${tableStore.table.height} + 45px)`" v-else description="暂无数据" />
<el-empty class="report-zl-sheet" v-else description="暂无数据" />
</div>
</pane>
</splitpanes>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, provide } from 'vue'
import { ref, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue'
import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { exportExcel } from '@/views/system/reportForms/export.js'
import 'splitpanes/dist/splitpanes.css'
import DatePicker from '@/components/form/datePicker/time.vue'
import { Splitpanes, Pane } from 'splitpanes'
const name = ref('')
// import data from './123.json'
defineOptions({
name: 'govern/reportCore/statisticsWx/index'
})
const height = mainHeight(20)
const size = ref(0)
const dictData = useDictData()
const TableHeaderRef = ref()
const dotList: any = ref({})
@@ -63,6 +57,7 @@ const tableStore = new TableStore({
url: '/harmonic-boot/customReport/getCustomReport',
method: 'POST',
column: [],
showPage: false,
beforeSearchFun: () => {
tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id
@@ -99,17 +94,6 @@ provide('tableStore', tableStore)
tableStore.table.params.resourceType = 1
tableStore.table.params.customType = 1
const flag = ref(true)
onMounted(() => {
nextTick(() => {
const dom = document.getElementById('navigation-splitpanes')
if (dom && dom.offsetHeight > 0) {
size.value = ((280 / (dom.offsetWidth - 7)) * 100)
} else {
// 设置默认值
size.value = 20
}
})
})
const stencil = (val: any) => {
@@ -147,11 +131,36 @@ const exportEvent = () => {
}
</script>
<style lang="scss">
.splitpanes.default-theme .splitpanes__pane {
.report-zl-page {
display: flex;
overflow: hidden;
}
.report-zl-sidebar {
width: 280px;
flex-shrink: 0;
min-height: 0;
overflow: hidden;
}
.report-zl-main {
flex: 1;
min-width: 0;
min-height: 0;
background: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
}
.box {
flex: 1;
min-height: 0;
padding: 10px;
overflow: hidden;
}
.report-zl-sheet {
height: 100%;
}
</style>

View File

@@ -1,7 +1,6 @@
<template>
<div class="default-main" :style="height">
<splitpanes style="height: 100%" class="default-theme" id="navigation-splitpanes">
<pane :size="size">
<div class="default-main report-zl-page" :style="height">
<div class="report-zl-sidebar">
<!-- <pointTreeWx :default-expand-all="false" template @node-click="handleNodeClick" @init="handleNodeClick"
@Policy="stencil">
</pointTreeWx> -->
@@ -13,8 +12,8 @@
@init="handleNodeClick"
@pointTypeChange="pointTypeChange"
></pointTree>
</pane>
<pane :size="100 - size" style="background: #fff" :style="height">
</div>
<div class="report-zl-main">
<TableHeader datePicker ref="TableHeaderRef" :showReset="false">
<template v-slot:select>
<!-- <el-form-item label="时间:">
@@ -43,37 +42,33 @@
<div class="box" v-loading="tableStore.table.loading">
<div
id="luckysheet"
:style="`height: calc(${tableStore.table.height} + 45px)`"
class="report-zl-sheet"
v-if="tableStore.table.data.length > 0"
></div>
<el-empty
:style="`height: calc(${tableStore.table.height} + 45px)`"
class="report-zl-sheet"
v-else
description="暂无数据"
/>
</div>
</pane>
</splitpanes>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, provide } from 'vue'
import { ref, provide } from 'vue'
import TableStore from '@/utils/tableStore'
import pointTreeWx from '@/components/tree/govern/pointTreeWx.vue'
import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { exportExcel } from '@/views/system/reportForms/export.js'
import 'splitpanes/dist/splitpanes.css'
import DatePicker from '@/components/form/datePicker/time.vue'
import { Splitpanes, Pane } from 'splitpanes'
import pointTree from '@/components/tree/govern/pointTree.vue'
// import data from './123.json'
defineOptions({
name: 'govern/reportCore/statisticsWx/index'
})
const height = mainHeight(20)
const size = ref(0)
const dictData = useDictData()
const TableHeaderRef = ref()
const dotList: any = ref({})
@@ -86,6 +81,7 @@ const tableStore = new TableStore({
url: '/cs-harmonic-boot/customReport/getCustomReport',
method: 'POST',
column: [],
showPage: false,
beforeSearchFun: () => {
tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id
@@ -132,17 +128,6 @@ provide('tableStore', tableStore)
tableStore.table.params.resourceType = 1
tableStore.table.params.customType = null
const flag = ref(true)
onMounted(() => {
nextTick(() => {
const dom = document.getElementById('navigation-splitpanes')
if (dom && dom.offsetHeight > 0) {
size.value = (280 / (dom.offsetWidth - 7)) * 100
} else {
// 设置默认值
size.value = 20
}
})
})
const stencil = (val: any) => {
templatePolicy.value = val.filter((item: any) => item.excelType != '4')
@@ -180,11 +165,36 @@ const exportEvent = () => {
}
</script>
<style lang="scss">
.splitpanes.default-theme .splitpanes__pane {
.report-zl-page {
display: flex;
overflow: hidden;
}
.report-zl-sidebar {
width: 280px;
flex-shrink: 0;
min-height: 0;
overflow: hidden;
}
.report-zl-main {
flex: 1;
min-width: 0;
min-height: 0;
background: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
}
.box {
flex: 1;
min-height: 0;
padding: 10px;
overflow: hidden;
}
.report-zl-sheet {
height: 100%;
}
</style>

View File

@@ -1,37 +1,22 @@
<template>
<div class="default-main" :style="height">
<splitpanes style="height: 100%" class="default-theme" id="navigation-splitpanes">
<pane :size="size">
<div class="default-main report-zl-page" :style="height">
<div class="report-zl-sidebar">
<!-- <pointTreeWx :default-expand-all="false" template @node-click="handleNodeClick" @init="handleNodeClick"
@Policy="stencil">
</pointTreeWx> -->
<CloudDeviceEntryTree
ref="TerminalRef"
template
@Policy="stencil"
@node-click="handleNodeClick"
@init="handleNodeClick"
></CloudDeviceEntryTree>
</pane>
<pane :size="100 - size" style="background: #fff" :style="height">
<CloudDeviceEntryTree ref="TerminalRef" template @Policy="stencil" @node-click="handleNodeClick"
@init="handleNodeClick"></CloudDeviceEntryTree>
</div>
<div class="report-zl-main">
<TableHeader datePicker ref="TableHeaderRef" :showReset="false">
<template v-slot:select>
<!-- <el-form-item label="时间:">
<DatePicker ref="datePickerRef"></DatePicker>
</el-form-item> -->
<el-form-item label="模板策略">
<el-select
v-model.trim="Template"
@change="changetype"
placeholder="请选择模版"
value-key="id"
>
<el-option
v-for="item in templatePolicy"
:key="item.id"
:label="item.excelName"
:value="item"
></el-option>
<el-select v-model.trim="Template" @change="changetype" placeholder="请选择模版" value-key="id">
<el-option v-for="item in templatePolicy" :key="item.id" :label="item.excelName"
:value="item"></el-option>
</el-select>
</el-form-item>
<!-- <el-form-item label="监测对象">
@@ -50,19 +35,11 @@
</template>
</TableHeader>
<div class="box" v-loading="tableStore.table.loading">
<div
id="luckysheet"
:style="`height: calc(${tableStore.table.height} + 45px)`"
v-if="tableStore.table.data.length > 0"
></div>
<el-empty
:style="`height: calc(${tableStore.table.height} + 45px)`"
v-else
description="暂无数据"
/>
<div id="luckysheet" class="report-zl-sheet"
v-if="tableStore.table.data.length > 0"></div>
<el-empty class="report-zl-sheet" v-else description="暂无数据" />
</div>
</pane>
</splitpanes>
</div>
</div>
</template>
<script setup lang="ts">
@@ -73,9 +50,7 @@ import TableHeader from '@/components/table/header/index.vue'
import { useDictData } from '@/stores/dictData'
import { mainHeight } from '@/utils/layout'
import { exportExcel } from '@/views/system/reportForms/export.js'
import 'splitpanes/dist/splitpanes.css'
import DatePicker from '@/components/form/datePicker/time.vue'
import { Splitpanes, Pane } from 'splitpanes'
import CloudDeviceEntryTree from '@/components/tree/govern/cloudDeviceEntryTreeZL.vue'
import { getListByIds } from '@/api/harmonic-boot/cockpit/cockpit'
import { ElMessage } from 'element-plus'
@@ -84,7 +59,6 @@ defineOptions({
name: 'govern/reportCore/statisticsWx/indexZL'
})
const height = mainHeight(20)
const size = ref(0)
const dictData = useDictData()
const TableHeaderRef = ref()
const dotList: any = ref({})
@@ -98,6 +72,7 @@ const tableStore = new TableStore({
url: '/cs-harmonic-boot/customReport/getSensitiveUserReport',
method: 'POST',
column: [],
showPage: false,
beforeSearchFun: () => {
tableStore.table.params.tempId = Template.value.id
tableStore.table.params.lineId = dotList.value.id
@@ -142,15 +117,6 @@ tableStore.table.params.customType = 1
const flag = ref(true)
onMounted(() => {
initListByIds()
nextTick(() => {
const dom = document.getElementById('navigation-splitpanes')
if (dom && dom.offsetHeight > 0) {
size.value = (280 / (dom.offsetWidth - 7)) * 100
} else {
// 设置默认值
size.value = 20
}
})
})
const idList = ref([])
@@ -201,11 +167,36 @@ const exportEvent = () => {
}
</script>
<style lang="scss">
.splitpanes.default-theme .splitpanes__pane {
.report-zl-page {
display: flex;
overflow: hidden;
}
.report-zl-sidebar {
width: 280px;
flex-shrink: 0;
min-height: 0;
overflow: hidden;
}
.report-zl-main {
flex: 1;
min-width: 0;
min-height: 0;
background: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
}
.box {
padding: 10px;
flex: 1;
min-height: 0;
padding: 10px 0;
overflow: hidden;
}
.report-zl-sheet {
height: 100%;
}
</style>