|
|
@@ -56,6 +56,14 @@
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
|
|
|
+ <!-- 近七天舆情趋势图 -->
|
|
|
+ <el-card shadow="hover" class="mb-[10px]">
|
|
|
+ <template #header>
|
|
|
+ <span class="font-bold">近七天舆情趋势</span>
|
|
|
+ </template>
|
|
|
+ <div ref="trendChartRef" style="width: 100%; height: 350px;"></div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
|
|
<div v-show="showSearch" class="mb-[10px]">
|
|
|
<el-card shadow="hover">
|
|
|
@@ -236,8 +244,9 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup name="PublicOpinion" lang="ts">
|
|
|
-import { listPublicOpinion, getPublicOpinion, delPublicOpinion, addPublicOpinion, updatePublicOpinion, handlePublicOpinion, getOpinionStats } from '@/api/riskctrl/publicOpinion';
|
|
|
-import { PublicOpinionForm, PublicOpinionQuery, PublicOpinionVO, OpinionStats } from '@/api/riskctrl/publicOpinion/types';
|
|
|
+import { listPublicOpinion, getPublicOpinion, delPublicOpinion, addPublicOpinion, updatePublicOpinion, handlePublicOpinion, getOpinionStats, getOpinionTrend } from '@/api/riskctrl/publicOpinion';
|
|
|
+import { PublicOpinionForm, PublicOpinionQuery, PublicOpinionVO, OpinionStats, OpinionTrend } from '@/api/riskctrl/publicOpinion/types';
|
|
|
+import * as echarts from 'echarts';
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
const { sys_normal_disable } = proxy.useDict('sys_normal_disable');
|
|
|
@@ -329,6 +338,10 @@ const getStatusType = (status: string): TagType => {
|
|
|
return map[status] || 'info';
|
|
|
};
|
|
|
|
|
|
+const trendChartRef = ref<HTMLDivElement>();
|
|
|
+let trendChart: echarts.ECharts | null = null;
|
|
|
+const trendData = ref<OpinionTrend[]>([]);
|
|
|
+
|
|
|
const loadStats = async () => {
|
|
|
try {
|
|
|
const res = await getOpinionStats();
|
|
|
@@ -336,6 +349,95 @@ const loadStats = async () => {
|
|
|
} catch { /* ignore */ }
|
|
|
};
|
|
|
|
|
|
+const loadTrend = async () => {
|
|
|
+ try {
|
|
|
+ const res = await getOpinionTrend();
|
|
|
+ const raw = res.data || [];
|
|
|
+ // 兼容旧后端 {month, count} 或空数据 → 统一为 {date, total, major, processed}
|
|
|
+ trendData.value = raw.map((item: any) => ({
|
|
|
+ date: item.date || item.month?.substring(5) || '--',
|
|
|
+ total: item.total ?? item.count ?? 0,
|
|
|
+ major: item.major ?? 0,
|
|
|
+ processed: item.processed ?? 0
|
|
|
+ }));
|
|
|
+ } catch { /* ignore */ }
|
|
|
+ await nextTick();
|
|
|
+ initChart();
|
|
|
+};
|
|
|
+
|
|
|
+const initChart = () => {
|
|
|
+ if (!trendChartRef.value) return;
|
|
|
+ if (trendChart) trendChart.dispose();
|
|
|
+ trendChart = echarts.init(trendChartRef.value);
|
|
|
+
|
|
|
+ const dates = trendData.value.map((item) => item.date);
|
|
|
+ const totals = trendData.value.map((item) => item.total);
|
|
|
+ const majors = trendData.value.map((item) => item.major);
|
|
|
+ const processed = trendData.value.map((item) => item.processed);
|
|
|
+
|
|
|
+ trendChart.setOption({
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: { type: 'cross' }
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['总舆情', '重大舆情', '已处理'],
|
|
|
+ bottom: 0
|
|
|
+ },
|
|
|
+ grid: {
|
|
|
+ left: '3%',
|
|
|
+ right: '4%',
|
|
|
+ bottom: '40px',
|
|
|
+ containLabel: true
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ boundaryGap: false,
|
|
|
+ data: dates
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ minInterval: 1
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '总舆情',
|
|
|
+ data: totals,
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: { color: '#409EFF' },
|
|
|
+ areaStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: 'rgba(64,158,255,0.3)' },
|
|
|
+ { offset: 1, color: 'rgba(64,158,255,0.05)' }
|
|
|
+ ])
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '重大舆情',
|
|
|
+ data: majors,
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: { color: '#F56C6C' },
|
|
|
+ lineStyle: { type: 'dashed' }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '已处理',
|
|
|
+ data: processed,
|
|
|
+ type: 'line',
|
|
|
+ smooth: true,
|
|
|
+ itemStyle: { color: '#67C23A' },
|
|
|
+ areaStyle: {
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
+ { offset: 0, color: 'rgba(103,194,58,0.3)' },
|
|
|
+ { offset: 1, color: 'rgba(103,194,58,0.05)' }
|
|
|
+ ])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ } as echarts.EChartsOption);
|
|
|
+};
|
|
|
+
|
|
|
const getList = async () => {
|
|
|
loading.value = true;
|
|
|
const res = await listPublicOpinion(queryParams);
|
|
|
@@ -432,6 +534,7 @@ const handleDelete = async (row?: PublicOpinionVO) => {
|
|
|
|
|
|
onMounted(() => {
|
|
|
loadStats();
|
|
|
+ loadTrend();
|
|
|
getList();
|
|
|
});
|
|
|
</script>
|