|
|
@@ -137,8 +137,9 @@
|
|
|
|
|
|
<!-- 首页内容 -->
|
|
|
<template v-if="isHomePage">
|
|
|
+ <div class="home-grid">
|
|
|
<!-- 核心水情数据卡片 -->
|
|
|
- <section class="glass-card stats-section">
|
|
|
+ <section class="glass-card stats-section home-full">
|
|
|
<div class="card-header">
|
|
|
<span class="card-title">实时水情</span>
|
|
|
<div class="card-actions">
|
|
|
@@ -166,207 +167,278 @@
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
- <!-- 中间区域:预警 + 工单 + 设备状态 -->
|
|
|
- <section class="middle-section">
|
|
|
- <!-- 左侧:预警信息 + 待办工单 -->
|
|
|
- <div class="middle-left">
|
|
|
- <!-- 预警信息 -->
|
|
|
- <div class="glass-card warning-panel">
|
|
|
- <div class="card-header">
|
|
|
- <span class="card-title">预警信息</span>
|
|
|
- <el-badge :value="warningList.length" :max="99" class="warning-badge">
|
|
|
- <el-button type="danger" size="small" plain>查看全部</el-button>
|
|
|
- </el-badge>
|
|
|
+ <!-- 每日简报 -->
|
|
|
+ <section class="glass-card daily-stats-card">
|
|
|
+ <div class="card-header">
|
|
|
+ <span class="card-title">每日简报</span>
|
|
|
+ <div class="card-actions">
|
|
|
+ <el-button type="primary" size="small" plain @click="exportDailyReport('excel')">
|
|
|
+ <el-icon><Upload /></el-icon> Excel
|
|
|
+ </el-button>
|
|
|
+ <el-button type="primary" size="small" plain @click="showPdfPreview">
|
|
|
+ <el-icon><Document /></el-icon> PDF
|
|
|
+ </el-button>
|
|
|
+ <el-button type="primary" size="small" plain @click="archiveBrief">
|
|
|
+ <el-icon><Folder /></el-icon> 归档
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-body">
|
|
|
+ <div class="daily-stats-list">
|
|
|
+ <div class="daily-stat-row">
|
|
|
+ <span class="ds-label">值班信息</span>
|
|
|
+ <span class="ds-value">{{ dailyStats.dutyPerson }} / {{ dailyStats.shiftTime }}</span>
|
|
|
</div>
|
|
|
- <div class="card-body">
|
|
|
- <div class="warning-list">
|
|
|
- <div v-for="(item, index) in warningList" :key="index"
|
|
|
- :class="['warning-item', `warning-${item.level}`]">
|
|
|
- <div class="warning-icon">
|
|
|
- <el-icon><Warning /></el-icon>
|
|
|
- </div>
|
|
|
- <div class="warning-content">
|
|
|
- <div class="warning-title">{{ item.title }}</div>
|
|
|
- <div class="warning-meta">
|
|
|
- <span>{{ item.location }}</span>
|
|
|
- <span>{{ item.time }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <el-tag :type="getWarningType(item.level)" size="small" effect="dark">
|
|
|
- {{ item.levelText }}
|
|
|
- </el-tag>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <div class="daily-stat-row">
|
|
|
+ <span class="ds-label">气象数据</span>
|
|
|
+ <span class="ds-value">{{ dailyStats.weather.temp }}°C {{ dailyStats.weather.desc }}</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 月度指标统计 -->
|
|
|
- <div class="glass-card stats-table-panel">
|
|
|
- <div class="card-header">
|
|
|
- <span class="card-title">月度指标统计</span>
|
|
|
- <div class="card-actions">
|
|
|
- <el-button type="primary" size="small" plain>导出Excel</el-button>
|
|
|
- <el-button type="primary" size="small" plain>导出PDF</el-button>
|
|
|
- </div>
|
|
|
+ <div class="daily-stat-row">
|
|
|
+ <span class="ds-label">库水位</span>
|
|
|
+ <span class="ds-value">
|
|
|
+ <span class="tp-group" v-for="(wl, idx) in dailyStats.waterLevels" :key="idx">
|
|
|
+ <span class="tp-time">{{ wl.time }}</span>
|
|
|
+ <span class="tp-val">{{ wl.value }}m</span>
|
|
|
+ <span v-if="idx < dailyStats.waterLevels.length - 1" class="tp-sep">|</span>
|
|
|
+ </span>
|
|
|
+ </span>
|
|
|
</div>
|
|
|
- <div class="card-body">
|
|
|
- <el-table :data="monthlyStats" stripe style="width: 100%" size="small">
|
|
|
- <el-table-column prop="name" label="统计项" width="120" />
|
|
|
- <el-table-column label="本月数值">
|
|
|
- <template #default="{ row }">
|
|
|
- <span class="value-highlight">{{ row.value }}</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="unit" label="单位" width="80" />
|
|
|
- <el-table-column label="环比变化" width="100">
|
|
|
- <template #default="{ row }">
|
|
|
- <span :class="['trend', row.trend > 0 ? 'trend-up' : 'trend-down']">
|
|
|
- {{ row.trend > 0 ? '+' : '' }}{{ row.trend }}%
|
|
|
- </span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="趋势" width="80">
|
|
|
- <template #default="{ row }">
|
|
|
- <div class="mini-chart">
|
|
|
- <div class="chart-bar" :style="{ height: Math.abs(row.trend) + '%', background: row.trend > 0 ? '#52c41a' : '#ff4d4f' }"></div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
+ <div class="daily-stat-row">
|
|
|
+ <span class="ds-label">雨量(8:00~次日8:00)</span>
|
|
|
+ <span class="ds-value">{{ dailyStats.rainfall }} mm</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 待办工单 -->
|
|
|
- <div class="glass-card todo-panel">
|
|
|
- <div class="card-header">
|
|
|
- <span class="card-title">待办工单</span>
|
|
|
- <el-button type="primary" size="small" plain>查看全部</el-button>
|
|
|
+ <div class="daily-stat-row">
|
|
|
+ <span class="ds-label">出入库</span>
|
|
|
+ <span class="ds-value">
|
|
|
+ 最大 <b>{{ dailyStats.inout.max }}</b> |
|
|
|
+ 最小 <b>{{ dailyStats.inout.min }}</b> |
|
|
|
+ 平均 <b>{{ dailyStats.inout.avg }}</b> |
|
|
|
+ 日水量 <b>{{ dailyStats.inout.total }}</b> 万m³
|
|
|
+ </span>
|
|
|
</div>
|
|
|
- <div class="card-body">
|
|
|
- <el-table :data="todoList" stripe style="width: 100%" size="small" header-row-class-name="table-header" height="150">
|
|
|
- <el-table-column prop="type" label="工单类型" width="100" />
|
|
|
- <el-table-column prop="location" label="关联点位" />
|
|
|
- <el-table-column prop="reportTime" label="上报时间" width="140" />
|
|
|
- <el-table-column prop="responsible" label="责任人" width="80" />
|
|
|
- <el-table-column label="状态" width="80">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-tag :type="getStatusType(row.status)" size="small" effect="light" round>
|
|
|
- {{ row.status }}
|
|
|
- </el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="操作" width="70">
|
|
|
- <template #default>
|
|
|
- <el-button type="primary" link size="small">处理</el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
+ <div class="daily-stat-row">
|
|
|
+ <span class="ds-label">瞬时流量</span>
|
|
|
+ <span class="ds-value">
|
|
|
+ <span class="tp-group" v-for="(flow, idx) in dailyStats.instantFlow" :key="idx">
|
|
|
+ <span class="tp-time">{{ flow.time }}</span>
|
|
|
+ <span class="tp-val">{{ flow.value }}</span>
|
|
|
+ <span v-if="idx < dailyStats.instantFlow.length - 1" class="tp-sep">|</span>
|
|
|
+ </span>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="daily-stat-row">
|
|
|
+ <span class="ds-label">其他</span>
|
|
|
+ <span class="ds-value">
|
|
|
+ 降雨 {{ dailyStats.other.rainfall }}mm |
|
|
|
+ 水质 {{ dailyStats.other.waterQuality }} |
|
|
|
+ 管线压力 {{ dailyStats.other.pipelinePressure }}MPa
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="daily-stat-row">
|
|
|
+ <span class="ds-label">自定义检查</span>
|
|
|
+ <span class="ds-value check-list">
|
|
|
+ <el-checkbox v-for="(item, idx) in dailyStats.checkItems" :key="idx" v-model="item.checked" size="small" @change="onCheckChange">
|
|
|
+ {{ item.label }}
|
|
|
+ </el-checkbox>
|
|
|
+ <el-button type="primary" link size="small" @click="addCheckItem">
|
|
|
+ <el-icon><Plus /></el-icon> 添加
|
|
|
+ </el-button>
|
|
|
+ </span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ </section>
|
|
|
|
|
|
- <!-- 右侧:设备状态 + 今日调度 + 月度统计 -->
|
|
|
- <div class="middle-right">
|
|
|
- <!-- 设备运行状态 -->
|
|
|
- <div class="glass-card device-panel">
|
|
|
- <div class="card-header">
|
|
|
- <span class="card-title">设备状态</span>
|
|
|
- <el-button type="primary" size="small" link>设备管理</el-button>
|
|
|
- </div>
|
|
|
- <div class="device-stats">
|
|
|
- <div class="device-stat-item" v-for="(item, index) in deviceStats" :key="index">
|
|
|
- <div class="device-stat-icon" :style="{ background: item.color }">
|
|
|
- <span>{{ item.icon }}</span>
|
|
|
- </div>
|
|
|
- <div class="device-stat-info">
|
|
|
- <span class="device-stat-value">{{ item.value }}</span>
|
|
|
- <span class="device-stat-label">{{ item.label }}</span>
|
|
|
- </div>
|
|
|
+ <!-- 预警信息 -->
|
|
|
+ <section class="glass-card warning-panel">
|
|
|
+ <div class="card-header">
|
|
|
+ <span class="card-title">预警信息</span>
|
|
|
+ <el-badge :value="warningList.length" :max="99" class="warning-badge">
|
|
|
+ <el-button type="danger" size="small" plain>查看全部</el-button>
|
|
|
+ </el-badge>
|
|
|
+ </div>
|
|
|
+ <div class="card-body">
|
|
|
+ <div class="warning-list">
|
|
|
+ <div v-for="(item, index) in warningList" :key="index"
|
|
|
+ :class="['warning-item', `warning-${item.level}`]">
|
|
|
+ <div class="warning-icon">
|
|
|
+ <el-icon><Warning /></el-icon>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- <div class="device-list">
|
|
|
- <div v-for="(device, index) in deviceList" :key="index" class="device-item">
|
|
|
- <div class="device-info">
|
|
|
- <span class="device-name">{{ device.name }}</span>
|
|
|
- <span class="device-location">{{ device.location }}</span>
|
|
|
+ <div class="warning-content">
|
|
|
+ <div class="warning-title">{{ item.title }}</div>
|
|
|
+ <div class="warning-meta">
|
|
|
+ <span>{{ item.location }}</span>
|
|
|
+ <span>{{ item.time }}</span>
|
|
|
</div>
|
|
|
- <el-tag :type="getDeviceStatusType(device.status)" size="small" effect="light">
|
|
|
- {{ device.statusText }}
|
|
|
- </el-tag>
|
|
|
</div>
|
|
|
+ <el-tag :type="getWarningType(item.level)" size="small" effect="dark">
|
|
|
+ {{ item.levelText }}
|
|
|
+ </el-tag>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
|
|
|
- <!-- 今日调度计划 -->
|
|
|
- <div class="glass-card schedule-panel">
|
|
|
- <div class="card-header">
|
|
|
- <span class="card-title">今日调度</span>
|
|
|
- <el-button type="primary" size="small" plain>调度管理</el-button>
|
|
|
- </div>
|
|
|
- <div class="card-body">
|
|
|
- <div class="schedule-list">
|
|
|
- <div v-for="(item, index) in todaySchedule" :key="index" class="schedule-item">
|
|
|
- <div class="schedule-time">{{ item.time }}</div>
|
|
|
- <div class="schedule-content">
|
|
|
- <div class="schedule-title">{{ item.title }}</div>
|
|
|
- <div class="schedule-desc">{{ item.description }}</div>
|
|
|
- </div>
|
|
|
- <el-tag :type="getScheduleType(item.status)" size="small" effect="light">
|
|
|
- {{ item.statusText }}
|
|
|
- </el-tag>
|
|
|
+ <!-- 月度指标统计 -->
|
|
|
+ <section class="glass-card stats-table-panel">
|
|
|
+ <div class="card-header">
|
|
|
+ <span class="card-title">月度指标统计</span>
|
|
|
+ <div class="card-actions">
|
|
|
+ <el-button type="primary" size="small" plain>导出Excel</el-button>
|
|
|
+ <el-button type="primary" size="small" plain>导出PDF</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-body">
|
|
|
+ <el-table :data="monthlyStats" stripe style="width: 100%" size="small">
|
|
|
+ <el-table-column prop="name" label="统计项" width="120" />
|
|
|
+ <el-table-column label="本月数值">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <span class="value-highlight">{{ row.value }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="unit" label="单位" width="80" />
|
|
|
+ <el-table-column label="环比变化" width="100">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <span :class="['trend', row.trend > 0 ? 'trend-up' : 'trend-down']">
|
|
|
+ {{ row.trend > 0 ? '+' : '' }}{{ row.trend }}%
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="趋势" width="80">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <div class="mini-chart">
|
|
|
+ <div class="chart-bar" :style="{ height: Math.abs(row.trend) + '%', background: row.trend > 0 ? '#52c41a' : '#ff4d4f' }"></div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <!-- 设备运行状态 -->
|
|
|
+ <section class="glass-card device-panel">
|
|
|
+ <div class="card-header">
|
|
|
+ <span class="card-title">设备状态</span>
|
|
|
+ <el-button type="primary" size="small" link>设备管理</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="device-stats">
|
|
|
+ <div class="device-stat-item" v-for="(item, index) in deviceStats" :key="index">
|
|
|
+ <div class="device-stat-icon" :style="{ background: item.color }">
|
|
|
+ <span>{{ item.icon }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="device-stat-info">
|
|
|
+ <span class="device-stat-value">{{ item.value }}</span>
|
|
|
+ <span class="device-stat-label">{{ item.label }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
+ <div class="device-list">
|
|
|
+ <div v-for="(device, index) in deviceList" :key="index" class="device-item">
|
|
|
+ <div class="device-info">
|
|
|
+ <span class="device-name">{{ device.name }}</span>
|
|
|
+ <span class="device-location">{{ device.location }}</span>
|
|
|
+ </div>
|
|
|
+ <el-tag :type="getDeviceStatusType(device.status)" size="small" effect="light">
|
|
|
+ {{ device.statusText }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
|
|
|
+ <!-- 待办工单 -->
|
|
|
+ <section class="glass-card todo-panel">
|
|
|
+ <div class="card-header">
|
|
|
+ <span class="card-title">待办工单</span>
|
|
|
+ <el-button type="primary" size="small" plain>查看全部</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="card-body">
|
|
|
+ <el-table :data="todoList" stripe style="width: 100%" size="small" header-row-class-name="table-header" height="150">
|
|
|
+ <el-table-column prop="type" label="工单类型" width="100" />
|
|
|
+ <el-table-column prop="location" label="关联点位" />
|
|
|
+ <el-table-column prop="reportTime" label="上报时间" width="140" />
|
|
|
+ <el-table-column prop="responsible" label="责任人" width="80" />
|
|
|
+ <el-table-column label="状态" width="80">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-tag :type="getStatusType(row.status)" size="small" effect="light" round>
|
|
|
+ {{ row.status }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="70">
|
|
|
+ <template #default>
|
|
|
+ <el-button type="primary" link size="small">处理</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
</div>
|
|
|
</section>
|
|
|
|
|
|
- <!-- 底部:快捷功能 + 气象信息 -->
|
|
|
- <section class="bottom-section">
|
|
|
- <!-- 快捷功能 -->
|
|
|
- <div class="glass-card quick-panel">
|
|
|
- <div class="card-header">
|
|
|
- <span class="card-title">快捷功能</span>
|
|
|
- </div>
|
|
|
- <div class="quick-grid">
|
|
|
- <div class="quick-item" v-for="(item, index) in quickAccess" :key="index" @click="handleQuickAccess(item.name)">
|
|
|
- <div class="quick-icon" :style="{ background: item.gradient }">
|
|
|
- <span>{{ item.icon }}</span>
|
|
|
+ <!-- 今日调度计划 -->
|
|
|
+ <section class="glass-card schedule-panel">
|
|
|
+ <div class="card-header">
|
|
|
+ <span class="card-title">今日调度</span>
|
|
|
+ <el-button type="primary" size="small" plain>调度管理</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="card-body">
|
|
|
+ <div class="schedule-list">
|
|
|
+ <div v-for="(item, index) in todaySchedule" :key="index" class="schedule-item">
|
|
|
+ <div class="schedule-time">{{ item.time }}</div>
|
|
|
+ <div class="schedule-content">
|
|
|
+ <div class="schedule-title">{{ item.title }}</div>
|
|
|
+ <div class="schedule-desc">{{ item.description }}</div>
|
|
|
</div>
|
|
|
- <span class="quick-label">{{ item.name }}</span>
|
|
|
+ <el-tag :type="getScheduleType(item.status)" size="small" effect="light">
|
|
|
+ {{ item.statusText }}
|
|
|
+ </el-tag>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+ </section>
|
|
|
|
|
|
- <!-- 气象信息 -->
|
|
|
- <div class="glass-card weather-panel">
|
|
|
- <div class="card-header">
|
|
|
- <span class="card-title">气象信息</span>
|
|
|
- <span class="weather-update">更新于 {{ weatherData.updateTime }}</span>
|
|
|
+ <!-- 快捷功能 -->
|
|
|
+ <section class="glass-card quick-panel">
|
|
|
+ <div class="card-header">
|
|
|
+ <span class="card-title">快捷功能</span>
|
|
|
+ </div>
|
|
|
+ <div class="quick-grid">
|
|
|
+ <div class="quick-item" v-for="(item, index) in quickAccess" :key="index" @click="handleQuickAccess(item.name)">
|
|
|
+ <div class="quick-icon" :style="{ background: item.gradient }">
|
|
|
+ <span>{{ item.icon }}</span>
|
|
|
+ </div>
|
|
|
+ <span class="quick-label">{{ item.name }}</span>
|
|
|
</div>
|
|
|
- <div class="weather-content">
|
|
|
- <div class="weather-main">
|
|
|
- <div class="weather-icon">{{ weatherData.icon }}</div>
|
|
|
- <div class="weather-info">
|
|
|
- <span class="weather-temp">{{ weatherData.temperature }}°C</span>
|
|
|
- <span class="weather-desc">{{ weatherData.description }}</span>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <!-- 实时天气 + 未来预报 -->
|
|
|
+ <section class="glass-card weather-current-panel">
|
|
|
+ <div class="card-header">
|
|
|
+ <span class="card-title">实时天气</span>
|
|
|
+ <span class="weather-update">更新于 {{ weatherData.updateTime }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="weather-current-body">
|
|
|
+ <div class="weather-main">
|
|
|
+ <div class="weather-icon">{{ weatherData.icon }}</div>
|
|
|
+ <div class="weather-info">
|
|
|
+ <span class="weather-temp">{{ weatherData.temperature }}°C</span>
|
|
|
+ <span class="weather-desc">{{ weatherData.description }}</span>
|
|
|
</div>
|
|
|
- <div class="weather-details">
|
|
|
- <div class="weather-detail-item">
|
|
|
- <span class="detail-label">湿度</span>
|
|
|
- <span class="detail-value">{{ weatherData.humidity }}%</span>
|
|
|
- </div>
|
|
|
- <div class="weather-detail-item">
|
|
|
- <span class="detail-label">风速</span>
|
|
|
- <span class="detail-value">{{ weatherData.windSpeed }}m/s</span>
|
|
|
- </div>
|
|
|
- <div class="weather-detail-item">
|
|
|
- <span class="detail-label">降雨概率</span>
|
|
|
- <span class="detail-value">{{ weatherData.rainProbability }}%</span>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ <div class="weather-details">
|
|
|
+ <div class="weather-detail-item">
|
|
|
+ <span class="detail-label">湿度</span>
|
|
|
+ <span class="detail-value">{{ weatherData.humidity }}%</span>
|
|
|
</div>
|
|
|
+ <div class="weather-detail-item">
|
|
|
+ <span class="detail-label">风速</span>
|
|
|
+ <span class="detail-value">{{ weatherData.windSpeed }}m/s</span>
|
|
|
+ </div>
|
|
|
+ <div class="weather-detail-item">
|
|
|
+ <span class="detail-label">降雨概率</span>
|
|
|
+ <span class="detail-value">{{ weatherData.rainProbability }}%</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="weather-merged-forecast">
|
|
|
<div class="weather-forecast">
|
|
|
<div v-for="(day, index) in weatherForecast" :key="index" class="forecast-item">
|
|
|
<span class="forecast-day">{{ day.day }}</span>
|
|
|
@@ -377,14 +449,72 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
</main>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <!-- 简报预览弹窗 -->
|
|
|
+ <el-dialog v-model="previewVisible" title="每日简报预览" width="800px" top="20px" :close-on-click-modal="false" destroy-on-close>
|
|
|
+ <div ref="previewContent" class="brief-preview-wrapper">
|
|
|
+ <div class="brief-paper" v-if="previewData">
|
|
|
+ <!-- 红头 -->
|
|
|
+ <div class="brief-red-header">乌拉海沟水库</div>
|
|
|
+ <div class="brief-doc-number">{{ previewDate.getFullYear() }}(第{{ String(previewDate.getFullYear()).slice(-2) }}号)文</div>
|
|
|
+ <!-- 红色分隔线 -->
|
|
|
+ <div class="brief-red-line"></div>
|
|
|
+ <!-- 标题 -->
|
|
|
+ <div class="brief-title">水库每日工作简报</div>
|
|
|
+ <div class="brief-body">
|
|
|
+ <p class="brief-paragraph"><b>一、水情概况</b></p>
|
|
|
+ <p class="brief-paragraph">
|
|
|
+ 1. 定时库水位:{{ fmtTime(previewData.waterLevels[0].time) }}{{ previewData.waterLevels[0].value }}m、{{ fmtTime(previewData.waterLevels[1].time) }}{{ previewData.waterLevels[1].value }}m、{{ fmtTime(previewData.waterLevels[2].time) }}{{ previewData.waterLevels[2].value }}m、{{ fmtTime(previewData.waterLevels[3].time) }}{{ previewData.waterLevels[3].value }}m,今日水位整体平稳,处于正常蓄水区间,人畜饮水、农田灌溉库容保障充足。
|
|
|
+ </p>
|
|
|
+ <p class="brief-paragraph">
|
|
|
+ 2. 降雨量:今日时段累计降雨量{{ previewData.rainfall }}mm。
|
|
|
+ </p>
|
|
|
+ <p class="brief-paragraph">
|
|
|
+ 3. 出入库流量数据:瞬时流量({{ fmtTime(previewData.instantFlow[0].time) }}{{ previewData.instantFlow[0].value }}m³/s、{{ fmtTime(previewData.instantFlow[1].time) }}{{ previewData.instantFlow[1].value }}m³/s、{{ fmtTime(previewData.instantFlow[2].time) }}{{ previewData.instantFlow[2].value }}m³/s、{{ fmtTime(previewData.instantFlow[3].time) }}{{ previewData.instantFlow[3].value }}m³/s);日流量最大{{ previewData.inout.max }}m³/s、最小{{ previewData.inout.min }}m³/s、平均{{ previewData.inout.avg }}m³/s;今日总入库水量{{ (previewData.inout.total * 10000).toLocaleString() }}m³、总出库水量{{ (previewData.inout.total * 10000).toLocaleString() }}m³,水量收支平衡,供水调度稳定。
|
|
|
+ </p>
|
|
|
+ <p class="brief-paragraph"><b>二、供水水质及管线运行情况</b></p>
|
|
|
+ <p class="brief-paragraph">
|
|
|
+ 1. 水质指标及评价:今日对库区水源开展常态化水质监测,水体水面整洁,无漂浮杂物、无外源污水汇入、无藻类异常繁殖。综合水质评价:本次所有监测指标均符合《地表水环境质量标准》Ⅲ类水质标准,水质整体优良。
|
|
|
+ </p>
|
|
|
+ <p class="brief-paragraph">
|
|
|
+ 2. 管线压力运行情况:今日对库区供水主管、灌溉支管压力进行常态化监测,管网运行压力维持在{{ previewData.other.pipelinePressure }}MPa,处于安全合理运行区间,压力波动平稳、工况稳定。供水管网、输水管道无渗漏、无泄压、无堵管现象,阀门、接口等附属设施运行正常,输水供水效能良好。
|
|
|
+ </p>
|
|
|
+ <p class="brief-paragraph"><b>三、工程运维巡查</b></p>
|
|
|
+ <p class="brief-paragraph">
|
|
|
+ 今日严格落实日常值守及常态化巡查制度,对大坝、输水渠道、供水管道、启闭设备、取水设施及监测设备开展全面排查。坝体无渗漏、裂缝、塌陷等异常,输水、供水通道畅通无淤积堵塞,各类设备运行稳定、监测数据完整。今日完成库区保洁、设备日常养护、渠道疏通等工作,工程设施整体运行良好,无隐患、无设备故障,供水灌溉保障能力稳定。
|
|
|
+ </p>
|
|
|
+ <p class="brief-paragraph"><b>四、其他</b></p>
|
|
|
+ <p class="brief-paragraph">
|
|
|
+ 值班人员{{ previewData.dutyPerson }}全员在岗在位,实时监测水位、库容、出水流量,规范完善台账记录,按时完成信息报送。库区供水、管护物资储备充足、器材完好可用。常态化开展库区安全巡查管控,严禁违规垂钓、下水、逗留等危险行为,杜绝破坏供水灌溉设施行为,今日库区运行平稳,无安全隐患、无突发事件及安全事故,供水灌溉秩序正常。
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ <div class="brief-footer-info">
|
|
|
+ <p>报送:上级水利部门</p>
|
|
|
+ <p>抄送:水库管理处</p>
|
|
|
+ <p>日期:{{ previewDate.getFullYear() }}年{{ previewDate.getMonth() + 1 }}月{{ previewDate.getDate() }}日 星期{{ weekDayNames[previewDate.getDay()] }}</p>
|
|
|
+ </div>
|
|
|
+ <div class="brief-footer-line"></div>
|
|
|
+ <div class="brief-footer-note">系统自动生成,仅供参考</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="previewVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmExportPdf">确认导出PDF</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { Search, Warning, VideoCamera } from '@element-plus/icons-vue'
|
|
|
+import { Search, Warning, VideoCamera, Upload, Document, Plus, Folder } from '@element-plus/icons-vue'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import * as XLSX from 'xlsx'
|
|
|
+import html2pdf from 'html2pdf.js'
|
|
|
+import { briefArchiveStore } from '../../stores/briefArchiveStore.js'
|
|
|
import DeviceMaintainView from '../admin/DeviceMaintainView.vue'
|
|
|
import PatrolPlanView from '../admin/PatrolPlanView.vue'
|
|
|
import PatrolRecordView from '../admin/PatrolRecordView.vue'
|
|
|
@@ -403,7 +533,7 @@ import SysLogView from '../admin/SysLogView.vue'
|
|
|
|
|
|
export default {
|
|
|
name: 'WorkspaceView',
|
|
|
- components: { Search, Warning, VideoCamera, DeviceMaintainView, PatrolPlanView, PatrolRecordView, PatrolHiddenView, WaterRainView, HydroHistoryView, WaterResourceAllocationView, GateControlAdminView, BenefitSummaryView, ArchiveFilesView, ArchiveOrdersView, ArchiveLifecycleView, SysRoleView, SysUserView, SysLogView },
|
|
|
+ components: { Search, Warning, VideoCamera, Upload, Document, Plus, Folder, DeviceMaintainView, PatrolPlanView, PatrolRecordView, PatrolHiddenView, WaterRainView, HydroHistoryView, WaterResourceAllocationView, GateControlAdminView, BenefitSummaryView, ArchiveFilesView, ArchiveOrdersView, ArchiveLifecycleView, SysRoleView, SysUserView, SysLogView },
|
|
|
data() {
|
|
|
return {
|
|
|
currentDate: '',
|
|
|
@@ -477,6 +607,43 @@ export default {
|
|
|
{ day: '明天', icon: '🌧️', high: 28, low: 20 },
|
|
|
{ day: '后天', icon: '☀️', high: 35, low: 24 }
|
|
|
],
|
|
|
+ // 每日实时数据统计
|
|
|
+ dailyStats: {
|
|
|
+ dutyPerson: '张三',
|
|
|
+ shiftTime: '08:00 ~ 18:00',
|
|
|
+ weather: { temp: 28, desc: '多云' },
|
|
|
+ waterLevels: [
|
|
|
+ { time: '02:00', value: '976.82' },
|
|
|
+ { time: '08:00', value: '977.15' },
|
|
|
+ { time: '14:00', value: '977.08' },
|
|
|
+ { time: '20:00', value: '976.95' }
|
|
|
+ ],
|
|
|
+ yesterdayWaterLevel: '976.50',
|
|
|
+ rainfall: 12.5,
|
|
|
+ inout: {
|
|
|
+ max: 15.6,
|
|
|
+ min: 3.2,
|
|
|
+ avg: 8.4,
|
|
|
+ total: 72.6
|
|
|
+ },
|
|
|
+ instantFlow: [
|
|
|
+ { time: '02:00', value: 6.8 },
|
|
|
+ { time: '08:00', value: 8.2 },
|
|
|
+ { time: '14:00', value: 10.5 },
|
|
|
+ { time: '20:00', value: 7.3 }
|
|
|
+ ],
|
|
|
+ other: {
|
|
|
+ rainfall: 12.5,
|
|
|
+ waterQuality: 'Ⅱ类',
|
|
|
+ pipelinePressure: 0.65
|
|
|
+ },
|
|
|
+ checkItems: [
|
|
|
+ { label: '大坝坝体巡查正常', checked: true },
|
|
|
+ { label: '闸门启闭设备正常', checked: true },
|
|
|
+ { label: '通信设备运行正常', checked: false },
|
|
|
+ { label: '排水沟渠畅通', checked: true }
|
|
|
+ ]
|
|
|
+ },
|
|
|
// 待办工单
|
|
|
todoList: [
|
|
|
{ type: '巡检待完成', location: '大坝坝顶', reportTime: '2024-01-15 09:00', responsible: '张三', status: '待处理' },
|
|
|
@@ -501,6 +668,12 @@ export default {
|
|
|
{ name: '隐患台账', icon: '📋', gradient: 'linear-gradient(135deg, #f3e8ff, #e9d5ff)' },
|
|
|
{ name: '报表导出', icon: '📊', gradient: 'linear-gradient(135deg, #fce7f3, #fbcfe8)' }
|
|
|
],
|
|
|
+ // 简报预览
|
|
|
+ previewVisible: false,
|
|
|
+ previewData: null,
|
|
|
+ previewDate: new Date(),
|
|
|
+ previewTime: '',
|
|
|
+ weekDayNames: ['日', '一', '二', '三', '四', '五', '六'],
|
|
|
// 月度统计
|
|
|
monthlyStats: [
|
|
|
{ name: '月度灌溉水量', value: '125.6', unit: '万m³', trend: 5.2 },
|
|
|
@@ -544,6 +717,13 @@ export default {
|
|
|
cells.push({ name: label })
|
|
|
}
|
|
|
return cells
|
|
|
+ },
|
|
|
+ waterLevelChange() {
|
|
|
+ const current = parseFloat(this.dailyStats.waterLevels[2]?.value || 0)
|
|
|
+ const yesterday = parseFloat(this.dailyStats.yesterdayWaterLevel || 0)
|
|
|
+ const diff = (current - yesterday).toFixed(2)
|
|
|
+ const direction = diff >= 0 ? '涨' : '落'
|
|
|
+ return { diff: Math.abs(diff).toString(), direction }
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
@@ -579,6 +759,178 @@ export default {
|
|
|
},
|
|
|
selectCamera(index) {
|
|
|
this.activeCameraIndex = index
|
|
|
+ },
|
|
|
+ // 格式化时间:02:00 → 2时
|
|
|
+ fmtTime(timeStr) {
|
|
|
+ if (!timeStr) return ''
|
|
|
+ const h = parseInt(timeStr.split(':')[0], 10)
|
|
|
+ return h + '时'
|
|
|
+ },
|
|
|
+ // 计算日总水量(万m³ → m³)
|
|
|
+ dailyInoutTotal() {
|
|
|
+ const total = this.dailyStats.inout?.total || 0
|
|
|
+ return (total * 10000).toLocaleString()
|
|
|
+ },
|
|
|
+ // 导出每日简报
|
|
|
+ exportDailyReport(type) {
|
|
|
+ const data = this.dailyStats
|
|
|
+ const now = new Date()
|
|
|
+ const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
|
|
+
|
|
|
+ if (type === 'excel') {
|
|
|
+ this.exportToExcel(data, dateStr)
|
|
|
+ } else if (type === 'pdf') {
|
|
|
+ this.exportToPdf(data, dateStr)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // Excel 导出
|
|
|
+ exportToExcel(data, dateStr) {
|
|
|
+ const rows = [
|
|
|
+ ['项目', '内容'],
|
|
|
+ ['值班人员', data.dutyPerson],
|
|
|
+ ['交接班时间', data.shiftTime],
|
|
|
+ ['气温', `${data.weather.temp}°C`],
|
|
|
+ ['天气状况', data.weather.desc],
|
|
|
+ ['', ''],
|
|
|
+ ['库水位', ''],
|
|
|
+ ['02:00 水位', `${data.waterLevels[0]?.value || ''} m`],
|
|
|
+ ['08:00 水位', `${data.waterLevels[1]?.value || ''} m`],
|
|
|
+ ['14:00 水位', `${data.waterLevels[2]?.value || ''} m`],
|
|
|
+ ['20:00 水位', `${data.waterLevels[3]?.value || ''} m`],
|
|
|
+ ['', ''],
|
|
|
+ ['雨量(8:00~次日8:00)', `${data.rainfall} mm`],
|
|
|
+ ['', ''],
|
|
|
+ ['出入库', ''],
|
|
|
+ ['最大流量', `${data.inout.max} m³/s`],
|
|
|
+ ['最小流量', `${data.inout.min} m³/s`],
|
|
|
+ ['平均流量', `${data.inout.avg} m³/s`],
|
|
|
+ ['总出入库日水量', `${data.inout.total} 万m³`],
|
|
|
+ ['', ''],
|
|
|
+ ['瞬时流量', ''],
|
|
|
+ ['02:00 瞬时流量', `${data.instantFlow[0]?.value || ''} m³/s`],
|
|
|
+ ['08:00 瞬时流量', `${data.instantFlow[1]?.value || ''} m³/s`],
|
|
|
+ ['14:00 瞬时流量', `${data.instantFlow[2]?.value || ''} m³/s`],
|
|
|
+ ['20:00 瞬时流量', `${data.instantFlow[3]?.value || ''} m³/s`],
|
|
|
+ ['', ''],
|
|
|
+ ['其他', ''],
|
|
|
+ ['降雨', `${data.other.rainfall} mm`],
|
|
|
+ ['水质', data.other.waterQuality],
|
|
|
+ ['管线压力', `${data.other.pipelinePressure} MPa`],
|
|
|
+ ['', ''],
|
|
|
+ ['自定义检查', ''],
|
|
|
+ ...data.checkItems.map(item => [item.label, item.checked ? '✅' : '❌'])
|
|
|
+ ]
|
|
|
+
|
|
|
+ const wb = XLSX.utils.book_new()
|
|
|
+ const ws = XLSX.utils.aoa_to_sheet(rows)
|
|
|
+
|
|
|
+ // 设置列宽
|
|
|
+ ws['!cols'] = [{ wch: 25 }, { wch: 20 }]
|
|
|
+
|
|
|
+ XLSX.utils.book_append_sheet(wb, ws, '每日数据统计')
|
|
|
+ XLSX.writeFile(wb, `每日数据统计_${dateStr}.xlsx`)
|
|
|
+ },
|
|
|
+ // 显示简报预览
|
|
|
+ showPdfPreview() {
|
|
|
+ this.previewDate = new Date()
|
|
|
+ const now = new Date()
|
|
|
+ this.previewTime = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
|
|
+ this.previewData = JSON.parse(JSON.stringify(this.dailyStats))
|
|
|
+ this.previewVisible = true
|
|
|
+ },
|
|
|
+ // 归档简报
|
|
|
+ archiveBrief() {
|
|
|
+ briefArchiveStore.addRecord(this.dailyStats)
|
|
|
+ ElMessage.success('简报已归档至「资料档案 → 工作简报」')
|
|
|
+ },
|
|
|
+ // 从预览确认导出 PDF
|
|
|
+ confirmExportPdf() {
|
|
|
+ this.previewVisible = false
|
|
|
+ const data = this.previewData || this.dailyStats
|
|
|
+ const now = new Date()
|
|
|
+ const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
|
|
+ this.exportToPdf(data, dateStr)
|
|
|
+ },
|
|
|
+ // PDF 导出
|
|
|
+ exportToPdf(data, dateStr) {
|
|
|
+ const checkItemsHtml = data.checkItems.map(item =>
|
|
|
+ `<tr><td style="padding:5px 10px;border:1px solid #d0d5dd;font-size:12px;">${item.label}</td><td style="padding:5px 10px;border:1px solid #d0d5dd;text-align:center;font-size:12px;">${item.checked ? '✅' : '⬜'}</td></tr>`
|
|
|
+ ).join('')
|
|
|
+
|
|
|
+ const waterLevelsHtml = data.waterLevels.map((wl, i) =>
|
|
|
+ `${wl.time}:${wl.value}m${i < data.waterLevels.length - 1 ? ';' : ''}`
|
|
|
+ ).join('')
|
|
|
+ const instantFlowHtml = data.instantFlow.map((f, i) =>
|
|
|
+ `${f.time}:${f.value} m³/s${i < data.instantFlow.length - 1 ? ';' : ''}`
|
|
|
+ ).join('')
|
|
|
+
|
|
|
+ const year = dateStr.slice(0, 4)
|
|
|
+ const docNum = `乌海水库〔${year}〕${year.slice(-2)}号`
|
|
|
+
|
|
|
+ const now = new Date()
|
|
|
+ const weekDayNames = ['日', '一', '二', '三', '四', '五', '六']
|
|
|
+ const weekDay = weekDayNames[now.getDay()]
|
|
|
+ const timeStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
|
|
+
|
|
|
+ const html = `
|
|
|
+ <div style="font-family: 'SimSun', 'Microsoft YaHei', serif; padding: 40px 50px; max-width: 800px; margin: 0 auto; line-height: 1.8;">
|
|
|
+ <div style="text-align:center;font-size:24px;font-weight:700;color:#cc0000;letter-spacing:8px;margin-bottom:2px;">乌拉海沟水库</div>
|
|
|
+ <div style="text-align:center;font-size:13px;color:#E80000;margin-bottom:6px;">${dateStr.slice(0,4)}(第${dateStr.slice(2,4)}号)文</div>
|
|
|
+ <div style="height:2px;background:#cc0000;margin:4px 0 14px;"></div>
|
|
|
+ <div style="text-align:center;font-size:18px;font-weight:700;color:#1e293b;margin-bottom:14px;">水库每日工作简报</div>
|
|
|
+ <div style="font-size:14px;line-height:2;color:#1e293b;">
|
|
|
+ <p style="margin:8px 0;"><b>一、水情概况</b></p>
|
|
|
+ <p style="margin:4px 0;text-indent:2em;">1. 定时库水位:${parseInt(data.waterLevels[0].time)}时${data.waterLevels[0].value}m、${parseInt(data.waterLevels[1].time)}时${data.waterLevels[1].value}m、${parseInt(data.waterLevels[2].time)}时${data.waterLevels[2].value}m、${parseInt(data.waterLevels[3].time)}时${data.waterLevels[3].value}m,今日水位整体平稳,处于正常蓄水区间,人畜饮水、农田灌溉库容保障充足。</p>
|
|
|
+ <p style="margin:4px 0;text-indent:2em;">2. 降雨量:今日时段累计降雨量${data.rainfall}mm。</p>
|
|
|
+ <p style="margin:4px 0;text-indent:2em;">3. 出入库流量数据:瞬时流量(${parseInt(data.instantFlow[0].time)}时${data.instantFlow[0].value}m³/s、${parseInt(data.instantFlow[1].time)}时${data.instantFlow[1].value}m³/s、${parseInt(data.instantFlow[2].time)}时${data.instantFlow[2].value}m³/s、${parseInt(data.instantFlow[3].time)}时${data.instantFlow[3].value}m³/s);日流量最大${data.inout.max}m³/s、最小${data.inout.min}m³/s、平均${data.inout.avg}m³/s;今日总入库水量${(data.inout.total * 10000).toLocaleString()}m³、总出库水量${(data.inout.total * 10000).toLocaleString()}m³,水量收支平衡,供水调度稳定。</p>
|
|
|
+ <p style="margin:8px 0;"><b>二、供水水质及管线运行情况</b></p>
|
|
|
+ <p style="margin:4px 0;text-indent:2em;">1. 水质指标及评价:今日对库区水源开展常态化水质监测,水体水面整洁,无漂浮杂物、无外源污水汇入、无藻类异常繁殖。综合水质评价:本次所有监测指标均符合《地表水环境质量标准》Ⅲ类水质标准,水质整体优良。</p>
|
|
|
+ <p style="margin:4px 0;text-indent:2em;">2. 管线压力运行情况:今日对库区供水主管、灌溉支管压力进行常态化监测,管网运行压力维持在${data.other.pipelinePressure}MPa,处于安全合理运行区间,压力波动平稳、工况稳定。供水管网、输水管道无渗漏、无泄压、无堵管现象,阀门、接口等附属设施运行正常,输水供水效能良好。</p>
|
|
|
+ <p style="margin:8px 0;"><b>三、工程运维巡查</b></p>
|
|
|
+ <p style="margin:4px 0;text-indent:2em;">今日严格落实日常值守及常态化巡查制度,对大坝、输水渠道、供水管道、启闭设备、取水设施及监测设备开展全面排查。坝体无渗漏、裂缝、塌陷等异常,输水、供水通道畅通无淤积堵塞,各类设备运行稳定、监测数据完整。今日完成库区保洁、设备日常养护、渠道疏通等工作,工程设施整体运行良好,无隐患、无设备故障,供水灌溉保障能力稳定。</p>
|
|
|
+ <p style="margin:8px 0;"><b>四、其他</b></p>
|
|
|
+ <p style="margin:4px 0;text-indent:2em;">值班人员${data.dutyPerson}全员在岗在位,实时监测水位、库容、出水流量,规范完善台账记录,按时完成信息报送。库区供水、管护物资储备充足、器材完好可用。常态化开展库区安全巡查管控,严禁违规垂钓、下水、逗留等危险行为,杜绝破坏供水灌溉设施行为,今日库区运行平稳,无安全隐患、无突发事件及安全事故,供水灌溉秩序正常。</p>
|
|
|
+ </div>
|
|
|
+ <div style="text-align:right;font-size:13px;color:#333;margin-top:20px;line-height:2;">
|
|
|
+ <p>报送:上级水利部门</p>
|
|
|
+ <p>抄送:水库管理处</p>
|
|
|
+ <p>日期:${dateStr} 星期${weekDay}</p>
|
|
|
+ </div>
|
|
|
+ <div style="height:2px;background:#cc0000;margin:10px 0 6px;"></div>
|
|
|
+ <div style="text-align:center;font-size:11px;color:#94a3b8;">系统自动生成,仅供参考</div>
|
|
|
+ </div>
|
|
|
+ `
|
|
|
+
|
|
|
+ const element = document.createElement('div')
|
|
|
+ element.innerHTML = html
|
|
|
+ document.body.appendChild(element)
|
|
|
+
|
|
|
+ const opt = {
|
|
|
+ margin: [12, 15],
|
|
|
+ filename: `每日简报_${dateStr}.pdf`,
|
|
|
+ image: { type: 'jpeg', quality: 0.98 },
|
|
|
+ html2canvas: { scale: 2, useCORS: true, letterRendering: true },
|
|
|
+ jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' },
|
|
|
+ pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
|
|
|
+ }
|
|
|
+
|
|
|
+ html2pdf().set(opt).from(element).save().then(() => {
|
|
|
+ document.body.removeChild(element)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 检查项变更
|
|
|
+ onCheckChange() {
|
|
|
+ // 可以在此处持久化保存检查项状态
|
|
|
+ },
|
|
|
+ // 添加自定义检查项
|
|
|
+ addCheckItem() {
|
|
|
+ const label = prompt('请输入检查事项:')
|
|
|
+ if (label && label.trim()) {
|
|
|
+ this.dailyStats.checkItems.push({
|
|
|
+ label: label.trim(),
|
|
|
+ checked: false
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -763,8 +1115,8 @@ export default {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- padding: 20px;
|
|
|
- gap: 20px;
|
|
|
+ padding: 10px;
|
|
|
+ gap: 10px;
|
|
|
overflow-y: auto;
|
|
|
overflow-x: hidden;
|
|
|
}
|
|
|
@@ -941,14 +1293,29 @@ export default {
|
|
|
}
|
|
|
.quick-label { font-size: 13px; font-weight: 500; color: #475569; }
|
|
|
|
|
|
-/* ==================== 中间区域布局 ==================== */
|
|
|
-.middle-section { display: flex; gap: 20px; min-height: 600px; flex-shrink: 0; }
|
|
|
-.middle-left { flex: 55; display: flex; flex-direction: column; gap: 20px; }
|
|
|
-.middle-right { flex: 45; display: flex; flex-direction: column; gap: 20px; }
|
|
|
-.warning-panel { min-height: 220px; }
|
|
|
-.todo-panel { flex: 1; min-height: 0; }
|
|
|
+/* ==================== 首页瀑布流布局 ==================== */
|
|
|
+.home-grid {
|
|
|
+ column-count: 3;
|
|
|
+ column-gap: 10px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+.home-grid > .glass-card {
|
|
|
+ break-inside: avoid;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+.home-grid > .home-full {
|
|
|
+ column-span: all;
|
|
|
+}
|
|
|
+.home-grid .quick-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ gap: 10px;
|
|
|
+ padding: 12px 16px;
|
|
|
+}
|
|
|
+.warning-panel { }
|
|
|
+.todo-panel { }
|
|
|
.todo-panel .card-body { overflow: hidden; }
|
|
|
-.device-panel { min-height: 280px; }
|
|
|
+.device-panel { }
|
|
|
.weather-panel { flex-shrink: 0; }
|
|
|
.quick-panel { flex-shrink: 0; }
|
|
|
|
|
|
@@ -1091,45 +1458,50 @@ export default {
|
|
|
color: #94a3b8;
|
|
|
}
|
|
|
|
|
|
-/* ==================== 气象信息样式 ==================== */
|
|
|
-.weather-content {
|
|
|
+/* ==================== 实时天气样式 ==================== */
|
|
|
+.weather-current-body {
|
|
|
padding: 16px 20px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ flex: 1;
|
|
|
}
|
|
|
-.weather-main {
|
|
|
+.weather-current-body .weather-main {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 16px;
|
|
|
margin-bottom: 16px;
|
|
|
}
|
|
|
-.weather-icon {
|
|
|
+.weather-current-body .weather-main .weather-icon {
|
|
|
font-size: 48px;
|
|
|
line-height: 1;
|
|
|
}
|
|
|
-.weather-info {
|
|
|
+.weather-current-body .weather-main .weather-info {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
-.weather-temp {
|
|
|
+.weather-current-body .weather-main .weather-temp {
|
|
|
font-size: 32px;
|
|
|
font-weight: 700;
|
|
|
color: #1e293b;
|
|
|
line-height: 1.2;
|
|
|
}
|
|
|
-.weather-desc {
|
|
|
+.weather-current-body .weather-main .weather-desc {
|
|
|
font-size: 14px;
|
|
|
color: #64748b;
|
|
|
}
|
|
|
-.weather-update {
|
|
|
- font-size: 12px;
|
|
|
- color: #94a3b8;
|
|
|
-}
|
|
|
-.weather-details {
|
|
|
+.weather-current-body .weather-details {
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
gap: 12px;
|
|
|
- padding-bottom: 16px;
|
|
|
- border-bottom: 1px solid rgba(226, 232, 240, 0.6);
|
|
|
- margin-bottom: 16px;
|
|
|
+ padding-bottom: 12px;
|
|
|
+ border-bottom: 1px solid rgba(226, 232, 240, 0.5);
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+.weather-merged-forecast .weather-forecast {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 8px;
|
|
|
}
|
|
|
.weather-detail-item {
|
|
|
display: flex;
|
|
|
@@ -1146,17 +1518,18 @@ export default {
|
|
|
font-weight: 600;
|
|
|
color: #334155;
|
|
|
}
|
|
|
-.weather-forecast {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(3, 1fr);
|
|
|
- gap: 12px;
|
|
|
+.weather-update {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #94a3b8;
|
|
|
}
|
|
|
+
|
|
|
+/* ==================== 未来预报样式 ==================== */
|
|
|
.forecast-item {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
gap: 6px;
|
|
|
- padding: 10px;
|
|
|
+ padding: 16px 10px;
|
|
|
border-radius: 8px;
|
|
|
background: rgba(248, 250, 252, 0.8);
|
|
|
}
|
|
|
@@ -1166,34 +1539,19 @@ export default {
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
.forecast-icon {
|
|
|
- font-size: 24px;
|
|
|
+ font-size: 28px;
|
|
|
}
|
|
|
.forecast-temp {
|
|
|
- font-size: 12px;
|
|
|
+ font-size: 13px;
|
|
|
color: #475569;
|
|
|
}
|
|
|
|
|
|
/* ==================== 今日调度样式 ==================== */
|
|
|
.schedule-panel {
|
|
|
- min-height: 200px;
|
|
|
}
|
|
|
.stats-table-panel {
|
|
|
- min-height: 250px;
|
|
|
}
|
|
|
|
|
|
-/* ==================== 底部区域(快捷功能 + 气象) ==================== */
|
|
|
-.bottom-section {
|
|
|
- display: flex;
|
|
|
- gap: 20px;
|
|
|
- min-height: 200px;
|
|
|
- flex-shrink: 0;
|
|
|
-}
|
|
|
-.bottom-section .quick-panel {
|
|
|
- flex: 40;
|
|
|
-}
|
|
|
-.bottom-section .weather-panel {
|
|
|
- flex: 60;
|
|
|
-}
|
|
|
.schedule-list {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
@@ -1251,21 +1609,6 @@ export default {
|
|
|
min-height: 4px;
|
|
|
border-radius: 2px 2px 0 0;
|
|
|
}
|
|
|
-
|
|
|
-/* ==================== 底部区域 ==================== */
|
|
|
-.bottom-section .card-header {
|
|
|
- height: 48px;
|
|
|
- padding: 0 20px;
|
|
|
- border-bottom: 1px solid rgba(226, 232, 240, 0.6);
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
-}
|
|
|
-.bottom-section .card-body {
|
|
|
- flex: 1;
|
|
|
- overflow: auto;
|
|
|
- padding: 12px 16px;
|
|
|
-}
|
|
|
.trend { font-weight: 600; font-size: 13px; }
|
|
|
|
|
|
/* ==================== 滚动条美化 ==================== */
|
|
|
@@ -1389,4 +1732,153 @@ export default {
|
|
|
font-size: 13px;
|
|
|
color: #334155;
|
|
|
}
|
|
|
+
|
|
|
+/* ==================== 每日实时数据统计卡片(紧凑单行) ==================== */
|
|
|
+.daily-stats-card {
|
|
|
+ flex-shrink: 0;
|
|
|
+ max-width: 860px;
|
|
|
+}
|
|
|
+.daily-stats-card .card-body {
|
|
|
+ padding: 4px 16px;
|
|
|
+}
|
|
|
+.daily-stats-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+.daily-stat-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ padding: 6px 0;
|
|
|
+ border-bottom: 1px solid rgba(226, 232, 240, 0.3);
|
|
|
+ min-height: 34px;
|
|
|
+}
|
|
|
+.daily-stat-row:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+.ds-label {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #64748b;
|
|
|
+ min-width: 120px;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+.ds-value {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #1e293b;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ line-height: 1.5;
|
|
|
+}
|
|
|
+.ds-value b {
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1d4ed8;
|
|
|
+}
|
|
|
+.tp-group {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 3px;
|
|
|
+}
|
|
|
+.tp-time {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #94a3b8;
|
|
|
+}
|
|
|
+.tp-val {
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1d4ed8;
|
|
|
+}
|
|
|
+.tp-sep {
|
|
|
+ color: #d0d5dd;
|
|
|
+ margin: 0 2px;
|
|
|
+}
|
|
|
+.check-list {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 4px 12px;
|
|
|
+}
|
|
|
+.check-list :deep(.el-checkbox__label) {
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ==================== 简报预览弹窗样式 ==================== */
|
|
|
+ .brief-preview-wrapper {
|
|
|
+ background: #fff;
|
|
|
+ padding: 20px;
|
|
|
+ max-height: 75vh;
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+ .brief-paper {
|
|
|
+ font-family: 'SimSun', 'Microsoft YaHei', serif;
|
|
|
+ max-width: 700px;
|
|
|
+ margin: 0 auto;
|
|
|
+ padding: 30px 35px;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ box-shadow: 0 2px 12px rgba(0,0,0,0.06);
|
|
|
+ line-height: 1.8;
|
|
|
+ }
|
|
|
+ .brief-red-header {
|
|
|
+ text-align: center;
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #cc0000;
|
|
|
+ letter-spacing: 8px;
|
|
|
+ margin-bottom: 2px;
|
|
|
+ }
|
|
|
+ .brief-doc-number {
|
|
|
+ text-align: center;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #E80000;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ }
|
|
|
+ .brief-red-line {
|
|
|
+ height: 2px;
|
|
|
+ background: #cc0000;
|
|
|
+ margin: 4px 0 14px;
|
|
|
+ }
|
|
|
+ .brief-title {
|
|
|
+ text-align: center;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #1e293b;
|
|
|
+ margin-bottom: 14px;
|
|
|
+ }
|
|
|
+ .brief-body {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #1e293b;
|
|
|
+ line-height: 2;
|
|
|
+ }
|
|
|
+ .brief-paragraph {
|
|
|
+ margin: 6px 0;
|
|
|
+ text-indent: 2em;
|
|
|
+ }
|
|
|
+ .brief-paragraph b {
|
|
|
+ font-weight: 700;
|
|
|
+ color: #1e293b;
|
|
|
+ }
|
|
|
+ .brief-footer-info {
|
|
|
+ text-align: right;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #333;
|
|
|
+ margin-top: 20px;
|
|
|
+ line-height: 2;
|
|
|
+ }
|
|
|
+ .brief-footer-info p {
|
|
|
+ margin: 2px 0;
|
|
|
+ }
|
|
|
+ .brief-footer-line {
|
|
|
+ height: 2px;
|
|
|
+ background: #cc0000;
|
|
|
+ margin: 10px 0 6px;
|
|
|
+ }
|
|
|
+ .brief-footer-note {
|
|
|
+ text-align: center;
|
|
|
+ font-size: 11px;
|
|
|
+ color: #94a3b8;
|
|
|
+ }
|
|
|
+
|
|
|
</style>
|
|
|
+
|