| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- <template>
- <div class="temperature-monitor">
- <!-- 搜索区域 -->
- <div class="search-section">
- <el-card class="search-card">
- <template #header>
- <div class="card-header">
- <span>温度监控查询</span>
- </div>
- </template>
-
- <el-form :model="searchForm" :inline="true" class="search-form">
- <!-- 时间范围选择 -->
- <el-form-item label="时间范围">
- <el-date-picker v-model="searchForm.timeRange" type="daterange" range-separator="至" start-placeholder="开始时间"
- end-placeholder="结束时间" format="YYYY-MM-DD" value-format="YYYY-MM-DD" class="time-picker" />
- </el-form-item>
-
- <!-- 设备选择 -->
- <el-form-item label="查询条件">
- <el-select v-model="searchForm.Info" placeholder="请选择查询条件" clearable filterable class="device-select">
- <el-option v-for="device in deviceOptions" :key="device.value" :label="device.label"
- :value="device.value" />
- </el-select>
- </el-form-item>
-
- <!-- 操作按钮 -->
- <el-form-item>
- <el-button type="primary" @click="handleSearch" :loading="loading">
- <el-icon>
- <Search />
- </el-icon>
- 查询
- </el-button>
- <el-button @click="handleReset">
- <el-icon>
- <Refresh />
- </el-icon>
- 重置
- </el-button>
- <el-button type="success" @click="handleExport">
- <el-icon>
- <Download />
- </el-icon>
- 导出
- </el-button>
- </el-form-item>
- </el-form>
- </el-card>
- </div>
-
- <!-- 结果展示区域 -->
- <div class="result-section">
- <el-card>
- <template #header>
- <div class="card-header">
- <span>查询结果</span>
- <span class="result-count">共 {{ totalCount }} 条记录</span>
- </div>
-
- </template>
-
- <!-- 使用el-tab分别展示数据表格和分析图表 -->
- <div class="result-content">
- <el-empty v-if="!hasData" description="暂无数据" />
- <div v-else>
- <el-tabs v-model="activeTab" type="border-card" class="result-tabs" @tab-change="handleTabChange">
- <!-- 数据表格标签页 -->
- <el-tab-pane label="数据表格" name="table">
- <div class="table-container">
- <el-table :data="tableData" border stripe style="width: 100%" :max-height="600" v-loading="loading">
- <!-- 序号列 -->
- <el-table-column type="index" label="序号" width="60" align="center" fixed="left" />
- <!-- 时间列 -->
- <el-table-column prop="datetime" label="时间" width="160" align="center" fixed="left" />
- <!-- 动态生成传感器列 -->
- <el-table-column v-for="sensor in sensorColumns" :key="sensor.sensorNo" :prop="sensor.sensorName"
- :label="sensor.sensorName" width="120" align="center" show-overflow-tooltip>
- <template #default="scope">
- <span>
- {{ scope.row[sensor.sensorName] ? Number(scope.row[sensor.sensorName]).toFixed(2) : '-' }}
- </span>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <!-- 分页 -->
- <div class="pagination-container">
- <el-pagination background v-model:current-page="queryParams.pageNumber"
- v-model:page-size="queryParams.pageSize" :total="totalCount" layout="total, prev, pager, next"
- @current-change="handleCurrentChange" />
- </div>
- </el-tab-pane>
-
- <!-- 分析图表标签页 -->
- <el-tab-pane label="分析图表" name="chart">
- <div class="chart-container">
- <TemperatureChart ref="chartRef" :chart-data="chartData" :sensor-columns="chartSensorColumns" />
- </div>
- </el-tab-pane>
- </el-tabs>
- </div>
- </div>
- </el-card>
- </div>
- </div>
- </template>
-
- <script setup>
- import { ref, reactive, computed, onMounted, nextTick, getCurrentInstance } from 'vue'
- import { ElMessage } from 'element-plus'
- import { Search, Refresh, Download } from '@element-plus/icons-vue'
- import { listTemperature, listByInfo, listAllByInfo } from '@/api/monitoring/temperature'
- import { recentOperLog } from '@/api/system/operlog'
- import { listChannel, channelInfoList } from '@/api/monitoring/channel'
- import TemperatureChart from './TemperatureChart.vue'
- import useUserStore from '@/store/modules/user'
- import { parseTime } from '@/utils/date'
-
- // 响应式数据
- const loading = ref(false)
- const totalCount = ref(0)
- const activeTab = ref('table')
- const chartRef = ref(null)
- const { proxy } = getCurrentInstance()
- const userStore = useUserStore()
-
- // 搜索表单数据
- const today = ref(parseTime(new Date(), '{y}-{m}-{d}'))
- const yesterday = ref(parseTime(new Date(new Date().setDate(new Date().getDate() - 1)), '{y}-{m}-{d}'))
- const searchForm = reactive({
- timeRange: [yesterday.value, today.value],
- Info: '',
- queryType: '',
- deviceId: '',
- minTemp: null,
- maxTemp: null
- })
- const queryParams = ref({
- pageNumber: 1,
- pageSize: 48
- })
-
- // 设备选项
- const deviceOptions = ref([])
-
- // 计算属性
- const hasData = computed(() => {
- return totalCount.value > 0
- })
-
- // 表格数据相关
- const tableData = ref([])
- const sensorColumns = ref([])
-
- // 图表数据相关
- const chartData = ref([])
- const chartSensorColumns = ref([])
- const allDataList = ref([])
-
- const getDeviceList = async () => {
- try {
- const deviceRes = await channelInfoList()
- if (deviceRes && Array.isArray(deviceRes)) {
- deviceOptions.value = deviceRes.map(item => ({
- label: item,
- value: item
- }))
- }
- }
- catch (error) {
- ElMessage.warning('监测数据已更新,请刷新当前页面')
- }
- }
- // 在组件挂载时调用
- onMounted(() => {
- getDeviceList()
- recentOperLog({ operName: userStore.name }).then(res => {
- if (res.operParam) {
- searchForm.Info = res.operParam.split('Info\', values=[')[1].split(']')[0]
- }
- })
- })
-
- // 处理图表数据,使用全部数据
- const processChartData = (dataList) => {
- if (!dataList || dataList.length === 0) {
- chartData.value = []
- chartSensorColumns.value = []
- return
- }
-
- // 获取所有唯一的传感器编号和对应的设备信息
- const sensorMap = new Map()
- dataList.forEach(item => {
- if (!sensorMap.has(item.sensorNo)) {
- sensorMap.set(item.sensorNo, {
- sensorNo: item.sensorNo,
- sensorName: item.channel?.info || `传感器${item.sensorNo}`
- })
- }
- })
-
- // 生成传感器列配置
- chartSensorColumns.value = Array.from(sensorMap.values()).sort((a, b) => a.sensorNo - b.sensorNo)
-
- // 按时间分组数据
- const timeGroups = {}
- dataList.forEach(item => {
- if (!timeGroups[item.datetime]) {
- timeGroups[item.datetime] = {
- datetime: item.datetime,
- ...chartSensorColumns.value.reduce((acc, sensor) => {
- acc[sensor.sensorName] = null
- return acc
- }, {})
- }
- }
- const sensorName = item.channel?.info || `传感器${item.sensorNo}`
- timeGroups[item.datetime][sensorName] = item.data
- })
-
- // 转换为数组并排序
- chartData.value = Object.values(timeGroups)
- }
-
- // 处理表格数据,使用全部数据并进行前端分页
- const processTableData = () => {
- if (!allDataList.value || allDataList.value.length === 0) {
- tableData.value = []
- sensorColumns.value = []
- return
- }
-
- // 获取所有唯一的传感器编号和对应的设备信息
- const sensorMap = new Map()
- allDataList.value.forEach(item => {
- if (!sensorMap.has(item.sensorNo)) {
- sensorMap.set(item.sensorNo, {
- sensorNo: item.sensorNo,
- sensorName: item.channel?.info || `传感器${item.sensorNo}`
- })
- }
- })
-
- // 生成传感器列配置
- sensorColumns.value = Array.from(sensorMap.values()).sort((a, b) => a.sensorNo - b.sensorNo)
-
- // 按时间分组数据
- const timeGroups = {}
- allDataList.value.forEach(item => {
- if (!timeGroups[item.datetime]) {
- timeGroups[item.datetime] = {
- datetime: item.datetime,
- ...sensorColumns.value.reduce((acc, sensor) => {
- acc[sensor.sensorName] = null
- return acc
- }, {})
- }
- }
- const sensorName = item.channel?.info || `传感器${item.sensorNo}`
- timeGroups[item.datetime][sensorName] = item.data
- })
-
- // 转换为数组并排序
- const allTableData = Object.values(timeGroups)
-
- // 前端分页处理
- const startIndex = (queryParams.value.pageNumber - 1) * queryParams.value.pageSize
- const endIndex = startIndex + queryParams.value.pageSize
- tableData.value = allTableData.slice(startIndex, endIndex)
- }
-
- const handleCurrentChange = (page) => {
- queryParams.value.pageNumber = page
- // 前端分页,直接重新处理表格数据
- processTableData()
- }
-
- // 查询监控数据
- const handleSearch = async () => {
- // 验证表单
- if (!searchForm.timeRange || searchForm.timeRange.length === 0) {
- ElMessage.warning('请选择时间范围')
- return
- }
- if (!searchForm.Info || searchForm.Info.trim() === '') {
- ElMessage.warning('请选择查询条件')
- return
- }
-
- // 检查时间范围是否超过31天
- const startDate = new Date(searchForm.timeRange[0])
- const endDate = new Date(searchForm.timeRange[1])
- const daysDiff = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24)) + 1
-
- if (daysDiff > 31) {
- // 使用 Element Plus 的确认对话框
- const { ElMessageBox } = await import('element-plus')
- try {
- await ElMessageBox.confirm(
- '查询超过1个月,数据量过大,可能会导致卡顿或崩溃,是否继续?',
- '查询确认',
- {
- confirmButtonText: '继续查询',
- cancelButtonText: '取消',
- type: 'warning',
- }
- )
- } catch (error) {
- // 用户点击取消
- return
- }
- }
-
- loading.value = true
- queryParams.value.pageNumber = 1
- try {
- let startTime = searchForm.timeRange[0] + ' 00:00:00'
- let endTime = searchForm.timeRange[1] + ' 23:59:59'
-
- // 使用 listAllByInfo 获取全部数据
- const allDataRes = await listAllByInfo({
- startTime: startTime,
- endTime: endTime,
- Info: searchForm.Info
- })
-
- if (allDataRes) {
- // 为图表和表格准备全部数据
- const allData = allDataRes.list || allDataRes
- totalCount.value = Number(allDataRes.total)
-
- // 存储全部数据
- allDataList.value = allData
-
- // 处理全部数据,生成图表数据
- processChartData(allData)
-
- // 处理全部数据,生成表格数据(前端分页)
- processTableData()
-
- ElMessage.success('查询成功')
- }
- } catch (error) {
- ElMessage.warning('监测数据已更新,请刷新当前页面')
- } finally {
- loading.value = false
- }
- }
-
- const handleReset = () => {
- searchForm.timeRange = []
- searchForm.Info = ''
- totalCount.value = 0
- tableData.value = []
- sensorColumns.value = []
- chartData.value = []
- chartSensorColumns.value = []
- allDataList.value = []
- // 重置分页参数
- queryParams.value.pageNumber = 1
- ElMessage.info('已重置搜索条件')
- }
-
- const handleExport = () => {
- if (totalCount.value === 0) {
- ElMessage.warning('暂无数据可导出')
- return
- }
-
- // ElMessage.success('导出功能开发中...')
- // 这里可以添加导出逻辑
- proxy.$download('monitoring/temperature/export', {
- startTime: searchForm.timeRange[0] + ' 00:00:00',
- endTime: searchForm.timeRange[1] + ' 23:59:59',
- Info: searchForm.Info
- }, `水温监测_${searchForm.timeRange[0] + "-" + searchForm.timeRange[1]}.xls`)
- }
-
-
-
- // 监听标签页切换
- const handleTabChange = (tabName) => {
- if (tabName === 'chart') {
- // 延迟触发图表重新渲染,确保DOM已更新
- nextTick(() => {
- setTimeout(() => {
- // 调用图表组件的resize方法
- if (chartRef.value) {
- chartRef.value.resizeChart()
- }
- }, 200)
- })
- }
- }
-
- </script>
-
- <style lang="scss" scoped>
- .temperature-monitor {
- padding: 20px;
- background-color: #f5f5f5;
- min-height: 100vh;
-
- .search-section {
- margin-bottom: 20px;
-
- .search-card {
- .card-header {
- font-weight: bold;
- color: #303133;
- }
-
- .search-form {
- .time-picker {
- width: 400px;
- }
-
- .query-select,
- .device-select {
- width: 200px;
- }
-
- .temp-input {
- width: 120px;
- }
-
- .temp-separator {
- margin: 0 10px;
- color: #909399;
- }
- }
- }
- }
-
- .result-section {
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- font-weight: bold;
- color: #303133;
-
- .result-count {
- font-size: 14px;
- color: #909399;
- font-weight: normal;
- }
- }
-
- .result-content {
- min-height: 200px;
-
- .pagination-container {
- margin-top: 20px;
- text-align: center;
- }
-
- // 标签页样式
- .result-tabs {
- .el-tabs__content {
- padding: 20px 0;
- }
- }
-
- // 表格容器样式
- .table-container {
- overflow-x: auto;
- width: 100%;
-
- // 自定义滚动条样式
- &::-webkit-scrollbar {
- height: 8px;
- }
-
- &::-webkit-scrollbar-track {
- background: #f1f1f1;
- border-radius: 4px;
- }
-
- &::-webkit-scrollbar-thumb {
- background: #c1c1c1;
- border-radius: 4px;
-
- &:hover {
- background: #a8a8a8;
- }
- }
-
- .el-table {
- .el-table__body-wrapper {
- overflow-x: auto;
- }
- }
- }
-
- // 图表容器样式
- .chart-container {
- width: 100%;
- height: 800px;
- min-height: 600px;
- display: flex;
- flex-direction: row;
- }
- }
- }
- }
-
- // 响应式设计
- @media (max-width: 768px) {
- .temperature-monitor {
- padding: 10px;
-
- .search-section {
- .search-card {
- .search-form {
- .time-picker {
- width: 100%;
- }
-
- .query-select,
- .device-select {
- width: 100%;
- }
- }
- }
- }
- }
- }
- </style>
|