591 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref, onMounted } from 'vue';
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import { Plus, Document, Download, TrendCharts, Connection } from '@element-plus/icons-vue';
import * as echarts from 'echarts';
// 示例数据
const tableData = ref([
{
id: 1,
title: "A区巡护日报",
reporter: "张三",
department: "巡护部",
type: "巡护报告",
content: "今日巡护发现...",
summary: "今日巡护总体情况良好发现并处理2处异常情况",
data: [
{ type: "巡护路线", value: "3条" },
{ type: "巡护时长", value: "4小时" },
{ type: "发现问题", value: 2, trend: "down" },
{ type: "处理完成", value: 2 }
],
attachments: ["report1.pdf", "image1.jpg"],
status: "已提交",
createTime: "2024-03-20 10:30",
},
// ... 其他数据
]);
// 分页
const currentPage = ref(1);
const pageSize = ref(10);
// 新建报告
const dialogVisible = ref(false);
const formData = ref({
title: "",
type: "",
content: "",
attachments: []
});
// 查看详情
const detailVisible = ref(false);
const currentReport = ref(null);
const handleView = (row) => {
currentReport.value = row;
detailVisible.value = true;
};
const handleCreate = () => {
dialogVisible.value = true;
};
const handleSubmit = () => {
// TODO: 提交表单
ElMessage.success('提交成功');
dialogVisible.value = false;
};
// 趋势图表
let trendChart = null;
// 初始化趋势图表
const initTrendChart = () => {
const chartDom = document.getElementById('trendChart');
if (!chartDom) return;
const myChart = echarts.init(chartDom);
const option = {
backgroundColor: 'transparent',
title: {
text: '近7天报告提交趋势',
textStyle: {
fontSize: 16,
fontWeight: 500,
color: '#303133'
},
padding: [20, 0]
},
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(255, 255, 255, 0.95)',
borderColor: '#eee',
padding: [10, 15],
textStyle: {
color: '#666'
},
axisPointer: {
type: 'line',
lineStyle: {
color: '#409EFF',
opacity: 0.2
}
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['3-14', '3-15', '3-16', '3-17', '3-18', '3-19', '3-20'],
boundaryGap: false,
axisLine: {
lineStyle: {
color: '#E5E7EB'
}
},
axisTick: {
show: false
},
axisLabel: {
color: '#666',
padding: [8, 0]
}
},
yAxis: {
type: 'value',
splitLine: {
lineStyle: {
color: '#E5E7EB',
type: 'dashed'
}
},
axisLabel: {
color: '#666'
}
},
series: [
{
name: '巡护报告',
type: 'line',
data: [5, 6, 4, 8, 7, 6, 5],
smooth: true,
symbol: 'circle',
symbolSize: 8,
lineStyle: {
width: 3,
color: '#409EFF'
},
itemStyle: {
color: '#409EFF',
borderWidth: 2,
borderColor: '#fff'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(64, 158, 255, 0.2)'
},
{
offset: 1,
color: 'rgba(64, 158, 255, 0.02)'
}
])
}
},
{
name: '监测报告',
type: 'line',
data: [3, 4, 3, 5, 4, 5, 4],
smooth: true,
symbol: 'circle',
symbolSize: 8,
lineStyle: {
width: 3,
color: '#67C23A'
},
itemStyle: {
color: '#67C23A',
borderWidth: 2,
borderColor: '#fff'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(103, 194, 58, 0.2)'
},
{
offset: 1,
color: 'rgba(103, 194, 58, 0.02)'
}
])
}
}
],
legend: {
data: ['巡护报告', '监测报告'],
right: 20,
top: 20,
itemWidth: 12,
itemHeight: 12,
textStyle: {
color: '#666'
}
}
};
myChart.setOption(option);
window.addEventListener('resize', () => myChart.resize());
};
onMounted(() => {
initTrendChart();
});
// 生成报告
const handleGenerate = () => {
ElMessageBox.confirm('是否生成今日巡护报告?系统将自动汇总数据。', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}).then(() => {
const loadingInstance = ElLoading.service({
text: '正在汇总数据,请稍候...'
});
setTimeout(() => {
loadingInstance.close();
ElMessage.success('报告生成成功,已自动整合今日巡护记录和监测数据');
}, 2000);
});
};
// 分析趋势
const handleAnalyze = () => {
ElMessageBox.confirm('是否开始分析近期报告数据趋势?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}).then(() => {
detailVisible.value = true;
currentReport.value = null;
showTrendAnalysis.value = true;
});
};
const showTrendAnalysis = ref(false);
</script>
<template>
<div class="daily-report">
<el-card>
<template #header>
<div class="card-header">
<span>日常报告</span>
<div class="header-btns">
<el-button :icon="Document" @click="handleGenerate">生成报告</el-button>
<el-button :icon="TrendCharts" @click="handleAnalyze">趋势分析</el-button>
<el-button type="primary" :icon="Plus" @click="handleCreate">新建报告</el-button>
</div>
</div>
</template>
<!-- 统计卡片 -->
<el-row :gutter="20" class="mb-20">
<el-col :span="6">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">256</div>
<div class="stat-label">报告总数</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">68</div>
<div class="stat-label">本月新增</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">12</div>
<div class="stat-label">待审核</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">98%</div>
<div class="stat-label">按时提交率</div>
</el-card>
</el-col>
</el-row>
<!-- 趋势图表 -->
<el-card class="mb-20">
<template #header>
<div class="chart-header">
<span>报告趋势分析</span>
<el-tooltip content="系统自动分析近期报告数据,预测未来趋势">
<el-icon><Connection /></el-icon>
</el-tooltip>
</div>
</template>
<div id="trendChart" style="height: 300px"></div>
</el-card>
<!-- 报告列表 -->
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="title" label="报告标题" min-width="180" />
<el-table-column prop="reporter" label="提交人" width="100" />
<el-table-column prop="summary" label="报告摘要" min-width="200" show-overflow-tooltip />
<el-table-column prop="type" label="类型" width="100">
<template #default="{ row }">
<el-tag :type="row.type === '巡护报告' ? 'success' : 'primary'">
{{ row.type }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === '已提交' ? 'success' : 'warning'">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="提交时间" width="180" />
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleView(row)">查看</el-button>
<el-button type="success" link @click="handleDownload(row)">下载</el-button>
<el-button type="warning" link @click="handleExport(row)">导出</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页器 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
:total="tableData.length"
/>
</div>
</el-card>
<!-- 新建报告弹窗 -->
<el-dialog v-model="dialogVisible" title="新建日常报告" width="700px">
<el-form :model="formData" label-width="100px">
<el-form-item label="报告标题" required>
<el-input v-model="formData.title" placeholder="请输入报告标题" />
</el-form-item>
<el-form-item label="报告类型" required>
<el-radio-group v-model="formData.type">
<el-radio label="巡护报告">巡护报告</el-radio>
<el-radio label="监测报告">监测报告</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="报告内容" required>
<el-input
v-model="formData.content"
type="textarea"
rows="6"
placeholder="请输入报告内容"
/>
</el-form-item>
<el-form-item label="附件">
<el-upload action="#" multiple :auto-upload="false">
<el-button type="primary">选择文件</el-button>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</span>
</template>
</el-dialog>
<!-- 详情弹窗 -->
<el-dialog v-model="detailVisible" title="报告详情" width="800px">
<el-descriptions :column="2" border v-if="currentReport">
<el-descriptions-item label="报告标题" :span="2">
{{ currentReport.title }}
</el-descriptions-item>
<el-descriptions-item label="提交人">
{{ currentReport.reporter }}
</el-descriptions-item>
<el-descriptions-item label="所属部门">
{{ currentReport.department }}
</el-descriptions-item>
<el-descriptions-item label="报告类型">
{{ currentReport.type }}
</el-descriptions-item>
<el-descriptions-item label="提交时间">
{{ currentReport.createTime }}
</el-descriptions-item>
<el-descriptions-item label="报告内容" :span="2">
{{ currentReport.content }}
</el-descriptions-item>
<el-descriptions-item label="附件" :span="2">
<el-link
v-for="file in currentReport.attachments"
:key="file"
type="primary"
style="margin-right: 16px"
>
{{ file }}
</el-link>
</el-descriptions-item>
<el-descriptions-item label="数据统计" :span="2">
<el-row :gutter="20" class="stat-grid">
<el-col :span="6" v-for="item in currentReport.data" :key="item.type">
<div class="stat-item">
<div class="stat-item-label">{{ item.type }}</div>
<div class="stat-item-value">
{{ item.value }}
<el-icon v-if="item.trend" :class="item.trend">
<component :is="item.trend === 'up' ? 'ArrowUp' : 'ArrowDown'" />
</el-icon>
</div>
</div>
</el-col>
</el-row>
</el-descriptions-item>
</el-descriptions>
<template #footer>
<span class="dialog-footer">
<el-button @click="detailVisible = false">关闭</el-button>
<el-button type="warning" @click="handleExport(currentReport)">导出</el-button>
</span>
</template>
</el-dialog>
<!-- 趋势分析弹窗 -->
<el-dialog v-model="showTrendAnalysis" title="报告趋势分析" width="900px">
<div class="trend-analysis">
<el-descriptions :column="2" border>
<el-descriptions-item label="数据周期" :span="2">
近30天报告数据
</el-descriptions-item>
<el-descriptions-item label="报告总量">
增长趋势 <el-tag type="success">上升 12%</el-tag>
</el-descriptions-item>
<el-descriptions-item label="提交及时率">
稳定趋势 <el-tag type="info">98%</el-tag>
</el-descriptions-item>
<el-descriptions-item label="问题发现率">
下降趋势 <el-tag type="success">-15%</el-tag>
</el-descriptions-item>
<el-descriptions-item label="处理及时率">
上升趋势 <el-tag type="success">+8%</el-tag>
</el-descriptions-item>
<el-descriptions-item label="预测分析" :span="2">
<div class="prediction">
<p>1. 根据当前趋势预计下月报告总量将增加约10%</p>
<p>2. 问题发现率持续下降表明整体环境质量改善</p>
<p>3. 建议继续保持当前的巡护频率和监测力度</p>
</div>
</el-descriptions-item>
</el-descriptions>
</div>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
@use "./styles/variables" as v;
.daily-report {
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.mb-20 {
margin-bottom: 20px;
}
.stat-card {
padding: 20px;
text-align: center;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
}
.stat-value {
font-size: 28px;
font-weight: 600;
color: $primary-color;
margin-bottom: 8px;
}
.stat-label {
font-size: 14px;
color: $text-secondary;
}
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
.header-btns {
display: flex;
gap: 12px;
}
.stat-grid {
padding: 12px;
.stat-item {
text-align: center;
padding: 12px;
background: #f8f9fa;
border-radius: 4px;
.stat-item-label {
font-size: 13px;
color: $text-secondary;
margin-bottom: 8px;
}
.stat-item-value {
font-size: 16px;
font-weight: 500;
color: $text-primary;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
.up {
color: $success-color;
}
.down {
color: $danger-color;
}
}
}
}
.chart-header {
display: flex;
align-items: center;
gap: 8px;
.el-icon {
color: $text-secondary;
cursor: help;
}
}
.trend-analysis {
.prediction {
padding: 12px;
background: #f8f9fa;
border-radius: 4px;
p {
margin: 8px 0;
color: $text-regular;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
}
}
}
.chart-card {
height: 400px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
margin-bottom: 24px;
transition: all 0.3s;
&:hover {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
}
</style>