591 lines
16 KiB
Vue
591 lines
16 KiB
Vue
<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>
|