lcw
This commit is contained in:
78
src/views/backOfficeSystem/AnalysisReport/components/a.js
Normal file
78
src/views/backOfficeSystem/AnalysisReport/components/a.js
Normal file
@ -0,0 +1,78 @@
|
||||
// import jsPDF from 'jspdf';
|
||||
// import html2canvas from 'html2canvas';
|
||||
|
||||
|
||||
// export function useExportToPDF() {
|
||||
// const exportToImage = async (element, filename = 'screenshot.png') => {
|
||||
// const canvas = await html2canvas(element, {
|
||||
// scale: 2,
|
||||
// useCORS: true,
|
||||
// });
|
||||
|
||||
// // 转换为图片并下载
|
||||
// const link = document.createElement('a');
|
||||
// link.download = filename;
|
||||
// link.href = canvas.toDataURL('image/png');
|
||||
// link.click();
|
||||
// };
|
||||
|
||||
// // 将div导出为PDF的方法
|
||||
// const exportDivToPDF = async (element, filename = 'document.pdf') => {
|
||||
// try {
|
||||
// // 使用html2canvas将div转换为canvas
|
||||
// const canvas = await html2canvas(element, {
|
||||
// scale: 2, // 提高清晰度
|
||||
// useCORS: true, // 允许跨域图片
|
||||
// logging: false, // 关闭日志
|
||||
// width: element.offsetWidth,
|
||||
// height: element.offsetHeight,
|
||||
// windowWidth: element.scrollWidth,
|
||||
// windowHeight: element.scrollHeight,
|
||||
// backgroundColor: '#ffffff' // 设置背景色为白色
|
||||
// });
|
||||
|
||||
// // 获取canvas的宽高
|
||||
// const imgWidth = 210; // A4纸宽度(mm)
|
||||
// const pageHeight = 297; // A4纸高度(mm)
|
||||
// const imgHeight = canvas.height * imgWidth / canvas.width;
|
||||
// let heightLeft = imgHeight;
|
||||
// let position = 0;
|
||||
|
||||
// // 创建PDF文档
|
||||
// const pdf = new jsPDF({
|
||||
// orientation: 'portrait',
|
||||
// unit: 'mm',
|
||||
// format: 'a4'
|
||||
// });
|
||||
|
||||
// // 将canvas转换为图片并添加到PDF
|
||||
// const imgData = canvas.toDataURL('image/png');
|
||||
// pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
|
||||
// heightLeft -= pageHeight;
|
||||
|
||||
// // 如果内容超出一页,添加新页面
|
||||
// while (heightLeft > 0) {
|
||||
// position = heightLeft - imgHeight;
|
||||
// pdf.addPage();
|
||||
// pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
|
||||
// heightLeft -= pageHeight;
|
||||
// }
|
||||
|
||||
// // 下载PDF
|
||||
// pdf.save(filename);
|
||||
// } catch (error) {
|
||||
// console.error('导出PDF失败:', error);
|
||||
// alert('导出PDF失败,请稍后重试');
|
||||
// }
|
||||
// };
|
||||
|
||||
// return {
|
||||
// exportToImage,
|
||||
// exportDivToPDF
|
||||
// };
|
||||
// }
|
||||
// exportToImage
|
||||
// };
|
||||
// }
|
||||
export function exportDivToPDF(divId, filename) {
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div class="box" ref="chartDom"></div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted,watch } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
let chartDom = ref(null); //注意变量名 和 ref名字要对应
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '2023年1月1日至2023年12月31日'
|
||||
},
|
||||
xAxisData: {
|
||||
type: Array,
|
||||
default: () => ['A', 'B', 'C', 'D', 'E', 'F']
|
||||
},
|
||||
seriesData: {
|
||||
type: Array,
|
||||
default: () => [[2549], [12421], [2637],[ 3146],[ 15189], [9562]]
|
||||
},
|
||||
// yAxisData: {
|
||||
// type: Array,
|
||||
// default: () => [2549, 12421, 2637, 3146, 15189, 9562]
|
||||
// },
|
||||
})
|
||||
const chartInstance = ref(null);
|
||||
onMounted(() => {
|
||||
|
||||
initChart();
|
||||
});
|
||||
watch([
|
||||
() => props.seriesData,
|
||||
() => props.xAxisData,
|
||||
], () => {
|
||||
initChart();
|
||||
});
|
||||
const initChart = () => {
|
||||
chartInstance.value = props.seriesData.map((item, index) => {
|
||||
console.log(item,"xxxxxxxx");
|
||||
|
||||
return {
|
||||
name: props.xAxisData[index],
|
||||
data: item,
|
||||
type: "bar",
|
||||
barWidth: "20",
|
||||
// barGap: 20, // 系列之间的间距
|
||||
barCategoryGap: 100, // 类别之间的间距
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter: function (params) {
|
||||
// 值为0或null时不显示
|
||||
if (params.value === 0 || params.value === null) {
|
||||
return '';
|
||||
}
|
||||
// 格式化数值显示
|
||||
return params.value.toLocaleString();
|
||||
},
|
||||
color: '#2c3e50',
|
||||
fontSize: 12,
|
||||
fontWeight: 'bold',
|
||||
distance: 10 // 标签与柱子的距离
|
||||
},
|
||||
}
|
||||
})
|
||||
console.log(chartInstance.value);
|
||||
|
||||
var myChart = echarts.init(chartDom.value);
|
||||
var option = {
|
||||
title: {
|
||||
text: props.title,
|
||||
left: 'center'
|
||||
},
|
||||
tooltip: {
|
||||
// 鼠标悬浮提示数据
|
||||
trigger: "axis",
|
||||
backgroundColor: "rgba(32, 33, 36,.7)",
|
||||
borderColor: "rgba(32, 33, 36,0.20)",
|
||||
borderWidth: 15,
|
||||
textStyle: {
|
||||
// 文字提示样式
|
||||
color: "#fff",
|
||||
fontSize: "12",
|
||||
},
|
||||
axisPointer: {
|
||||
// 坐标轴虚线
|
||||
type: "cross",
|
||||
label: {
|
||||
backgroundColor: "#6a7985",
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
right: 0
|
||||
},
|
||||
// },
|
||||
grid: {
|
||||
// 控制图表的位置
|
||||
left: "5%",
|
||||
right: "5%",
|
||||
top: "18%",
|
||||
bottom: "5%",
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
data: props.xAxisData,
|
||||
},
|
||||
yAxis: {
|
||||
// axisLabel: {
|
||||
// // y轴线 标签修改
|
||||
// textStyle: {
|
||||
// color: "white", //坐标值得具体的颜色
|
||||
// },
|
||||
// },
|
||||
// data: props.xAxisData,
|
||||
},
|
||||
series: chartInstance.value
|
||||
};
|
||||
option && myChart.setOption(option);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.box {
|
||||
width: 100%;
|
||||
height: 60vh;
|
||||
/* background-color: #031a67; */
|
||||
}
|
||||
</style>
|
@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<!-- 将固定id改为ref引用 -->
|
||||
<div ref="chartContainer" style="height: 100%;"></div>
|
||||
</template>
|
||||
<script setup>
|
||||
import * as echarts from 'echarts';
|
||||
import { onMounted, onUnmounted, ref, watch, defineProps } from 'vue';
|
||||
|
||||
// 定义组件的属性,使其可复用
|
||||
const props = defineProps({
|
||||
// 图表数据
|
||||
chartData: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ value: 40, name: 'rose 1' },
|
||||
{ value: 38, name: 'rose 2' },
|
||||
{ value: 32, name: 'rose 3' },
|
||||
{ value: 30, name: 'rose 4' },
|
||||
{ value: 28, name: 'rose 5' },
|
||||
{ value: 26, name: 'rose 6' },
|
||||
{ value: 22, name: 'rose 7' },
|
||||
{ value: 18, name: 'rose 8' }
|
||||
]
|
||||
},
|
||||
// 图表标题
|
||||
chartName: {
|
||||
type: String,
|
||||
default: 'Nightingale Chart'
|
||||
},
|
||||
// 是否显示工具箱
|
||||
showToolbox: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示图例
|
||||
showLegend: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 内半径
|
||||
innerRadius: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 外半径
|
||||
outerRadius: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
// 扇形间距
|
||||
padAngle: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
title: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
text: 'Nightingale Chart',
|
||||
left: 'center'
|
||||
})
|
||||
},
|
||||
roseType: {
|
||||
type: String,
|
||||
default: 'radius'
|
||||
}
|
||||
});
|
||||
|
||||
// 图表容器引用
|
||||
const chartContainer = ref(null);
|
||||
// 图表实例引用
|
||||
const chartInstance = ref(null);
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
const chartDom = chartContainer.value;
|
||||
if (!chartDom) return;
|
||||
|
||||
// 如果已经存在实例,先销毁
|
||||
if (chartInstance.value) {
|
||||
chartInstance.value.dispose();
|
||||
}
|
||||
|
||||
// 创建新实例
|
||||
chartInstance.value = echarts.init(chartDom);
|
||||
|
||||
// 设置图表选项
|
||||
const option = {
|
||||
title: props.title,
|
||||
legend: props.showLegend ? {
|
||||
top: 'bottom'
|
||||
} : false,
|
||||
toolbox: props.showToolbox ? {
|
||||
show: true,
|
||||
feature: {
|
||||
mark: { show: true },
|
||||
dataView: { show: true, readOnly: false },
|
||||
restore: { show: true },
|
||||
saveAsImage: { show: true }
|
||||
}
|
||||
} : false,
|
||||
series: [
|
||||
{
|
||||
name: props.chartName,
|
||||
type: 'pie',
|
||||
radius: [props.innerRadius, props.outerRadius],
|
||||
center: ['50%', '50%'],
|
||||
roseType: props.roseType,
|
||||
// 在 ECharts 5.3.3 中,当 roseType 为 'area' 时,padAngle 需要特殊处理
|
||||
// 为了确保间距生效,我们需要添加一个小的非零值来替代0
|
||||
// padAngle: props.padAngle === 0 ? 0.1 : props.padAngle,
|
||||
itemStyle: {
|
||||
borderColor: '#fff',
|
||||
borderWidth:props.padAngle
|
||||
},
|
||||
// 显示百分比
|
||||
label: {
|
||||
show: true,
|
||||
formatter: function(params) {
|
||||
// 对于值为0的项,显示0%而不是极小值计算出的百分比
|
||||
if (props.chartData.find(d => d.name === params.name && d.value === 0)) {
|
||||
return params.name + ': 0%';
|
||||
}
|
||||
return params.name + ': ' + params.percent + '%';
|
||||
}
|
||||
},
|
||||
// 鼠标悬停时的样式
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '16',
|
||||
fontWeight: 'bold',
|
||||
formatter: function(params) {
|
||||
// 对于值为0的项,显示0%而不是极小值计算出的百分比
|
||||
if (props.chartData.find(d => d.name === params.name && d.value === 0)) {
|
||||
return params.name + ': 0%';
|
||||
}
|
||||
return params.name + ': ' + params.percent + '%';
|
||||
}
|
||||
}
|
||||
},
|
||||
// 处理数据,确保值为0的项也能显示
|
||||
data: props.chartData.map(item => ({
|
||||
...item,
|
||||
// 对于值为0的数据,设置一个极小值来确保它在饼图中显示
|
||||
value: item.value === 0 ? 0.0000001 : item.value
|
||||
}))
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 应用选项
|
||||
chartInstance.value.setOption(option);
|
||||
};
|
||||
|
||||
// 处理窗口大小变化,自动调整图表大小
|
||||
const handleResize = () => {
|
||||
if (chartInstance.value) {
|
||||
chartInstance.value.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时初始化图表
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
// 组件卸载时销毁图表实例并移除事件监听
|
||||
onUnmounted(() => {
|
||||
if (chartInstance.value) {
|
||||
chartInstance.value.dispose();
|
||||
}
|
||||
window.removeEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
// 监听数据变化,更新图表
|
||||
watch(() => props.chartData, () => {
|
||||
initChart();
|
||||
}, { deep: true });
|
||||
|
||||
// 监听其他配置变化,更新图表
|
||||
watch([
|
||||
() => props.chartName,
|
||||
() => props.showToolbox,
|
||||
() => props.showLegend,
|
||||
() => props.innerRadius,
|
||||
() => props.outerRadius,
|
||||
() => props.padAngle,
|
||||
()=> props.roseType,
|
||||
], () => {
|
||||
initChart();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
:deep(div) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* min-height: 400px; */
|
||||
}
|
||||
</style>
|
500
src/views/backOfficeSystem/AnalysisReport/index.vue
Normal file
500
src/views/backOfficeSystem/AnalysisReport/index.vue
Normal file
@ -0,0 +1,500 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="titleBox">
|
||||
<PageTitle title="警情分析报告">
|
||||
<el-button type="primary" @click="generatePDF()">
|
||||
<el-icon style="vertical-align: middle">
|
||||
<CirclePlus />
|
||||
</el-icon>
|
||||
<span style="vertical-align: middle">导出</span>
|
||||
</el-button>
|
||||
</PageTitle>
|
||||
</div>
|
||||
<!-- 搜索 -->
|
||||
<div ref="searchBox">
|
||||
<Search :searchArr="searchConfiger" @submit="onSearch">
|
||||
<template #defaultSlot>
|
||||
<!-- -->
|
||||
<el-select v-model="bjlbList" placeholder="请选择情报类型" multiple collapse-tags>
|
||||
<el-option v-for="item in dictItemList" :key="item.dm" :label="item.zdmc" :value="item.dm" />
|
||||
</el-select>
|
||||
</template>
|
||||
</Search>
|
||||
</div>
|
||||
<div style="background-color: #fff;color: black;padding: 15px;overflow: auto;" :style="{ height: tabHeight + 'px' }"
|
||||
ref="tableBox">
|
||||
<div style="border-bottom: 1px #ccc solid;padding-bottom: 30px;">
|
||||
<h1 class="headline">{{ nd }}年度西藏公安执法数据分析</h1>
|
||||
<div style="display: flex;align-items: center;justify-content: space-between; color: red;margin-top: 30px;padding: 0 30px;">
|
||||
<div>{{ deptId.name }}</div>
|
||||
<div>{{ deptId?.ord }}</div>
|
||||
<div>{{ deptId?.time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>为全面、客观、准确掌握全区公安机关的执法状况,自治区
|
||||
公安厅基于数据统计,对全区公安机关{{ nd }}年度的执法状况作
|
||||
了客观分析。</p>
|
||||
<h2>一、执法状况总体分析</h2>
|
||||
<h2>1.接处警情况</h2>
|
||||
<h2>1.1接报警情</h2>
|
||||
<p>
|
||||
|
||||
{{ timeValidate(TimeValue.startTime, 'td') }}至{{ timeValidate(TimeValue.endTime, 'td') }}
|
||||
|
||||
|
||||
|
||||
,全区公安机关共
|
||||
接报各类警情{{ dataList.XsfxTj.total }}起,同比{{ `${dataList.XsfxTj.tbbsb > 0 ? "下降" : "上升"}
|
||||
${dataList.XsfxTj.tbbsb}%,同比${dataList.XsfxTj.tbbsb > 0 ? "下降" : "上升"}${dataList.XsfxTj.tbbsb}%` }}。</p>
|
||||
<h2>1.1.1类型维度</h2>
|
||||
<p>
|
||||
从警情类型来看,{{ sortingRatioValue.Ydfx[0]?.name }}警情最多,占到{{ sortingRatioValue.Ydfx[0]?.ratio }};其次为{{
|
||||
sortingRatioValue.Ydfx[1]?.name }}警情,占到{{ sortingRatioValue.Ydfx[1]?.ratio }}。
|
||||
</p>
|
||||
<MaleNightingalePicture roseType="area" style="height: 550px;" :title="{ text: '接警情类型', left: 'center' }"
|
||||
:chartData="dataList.jqlxTj" :chartName="'接警情类型'" :innerRadius="0" :padAngle="4" />
|
||||
<h2>1.1.2来源维度</h2>
|
||||
<p>
|
||||
从警情来源来看,{{ sortingRatioValue.Jqlx[0]?.name }}警情最多,占到{{ sortingRatioValue.Jqlx[0]?.ratio }};其次为{{
|
||||
sortingRatioValue.Jqlx[1]?.name }}警情,占到{{ sortingRatioValue.Jqlx[1]?.ratio }}。
|
||||
</p>
|
||||
<MaleNightingalePicture roseType="area" :title="{ text: '警情来源', left: 'center' }" style="height: 550px;"
|
||||
:chartData="dataList.jqlyTj" :innerRadius="50" :outerRadius="150" :padAngle="4" />
|
||||
|
||||
<h2>1.1.3地域维度</h2>
|
||||
<p>从地市分布地市来看,{{ sortingRatioValue.Dywdtj[0]?.ssbm }}警情量最大,占到全区警情总
|
||||
量的{{ sortingRatioValue.Dywdtj[0]?.ratio }}。
|
||||
<span v-if="sortingRatioValue.Dywdtj.length > 1">
|
||||
其次为{{ sortingRatioValue.Dywdtj[1]?.ssbm }}
|
||||
<span v-if="sortingRatioValue.Dywdtj.length > 2">
|
||||
<span v-if="sortingRatioValue.Dywdtj.length > 3">
|
||||
和{{ sortingRatioValue.Dywdtj[3]?.ssbm }},两市警情量较为接近。
|
||||
</span>
|
||||
<span>
|
||||
警情量最少的为{{ sortingRatioValue.Dywdtj[sortingRatioValue.Dywdtj.length - 1]?.ssbm }}。
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</p>
|
||||
<Histogram title="地市分布" :xAxisData="dataList.dyTj.xAxisData" :seriesData="dataList.dyTj.seriesData" />
|
||||
<h2>1.1.4时间维度</h2>
|
||||
<p>
|
||||
我们将所有警情按照月份划分进行统计发现,每月警情分布
|
||||
较为平均。最高月份为
|
||||
{{ sortingRatioValue.Ydfx[0]?.month }}月,占到{{ sortingRatioValue.Ydfx[0]?.ratio }};
|
||||
<span v-if="sortingRatioValue.Ydfx.length > 1">
|
||||
最低月份为{{ sortingRatioValue.Ydfx[sortingRatioValue.Ydfx.length - 1]?.month }}月,
|
||||
占到{{ sortingRatioValue.Ydfx[sortingRatioValue.Ydfx.length - 1]?.ratio }}。
|
||||
考虑是因为{{ sortingRatioValue.Ydfx[0]?.month }}月为我区传统旅游旺季,进藏人员较多。
|
||||
<!-- {{sortingRatioValue.Ydfx[sortingRatioValue.Ydfx.length-1]?.month}}月一般春节及藏历新年期间,在藏人员较少。 -->
|
||||
</span>
|
||||
</p>
|
||||
<MaleNightingalePicture :title="{ text: '警情分布', left: 'center' }" style="height: 550px;"
|
||||
:chartData="dataList.ydTj" :innerRadius="10" :outerRadius="150" :padAngle="2" />
|
||||
<!-- <p>
|
||||
按照24小时每小时时段进行划分后发现,警情多发时段集
|
||||
中在9时至20时,每小时均在1000起以上。
|
||||
</p> -->
|
||||
<h2>1.2警情处置</h2>
|
||||
<h2>1.2.1结果维度</h2>
|
||||
<p>
|
||||
从警情处置结果来看,
|
||||
<span v-for="(item, index) in sortingRatioValue.Cljgf">
|
||||
{{ `${item.name}占到${item.ratio},` }}
|
||||
</span>
|
||||
|
||||
<!--
|
||||
{{sortingRatioValue.Cljgf[0]?.name}}的,占到{{sortingRatioValue.Cljgf[0]?.ratio}};拟作行
|
||||
政案件处理的占比17.63%;拟作刑事案件处理的,占到6.39%。 -->
|
||||
</p>
|
||||
<MaleNightingalePicture :title="{ text: '警情处置', left: 'center' }" style="height: 550px;"
|
||||
:chartData="dataList.CljgfTj" :innerRadius="0" :outerRadius="150" :padAngle="2" />
|
||||
<h2>1.2.2效率维度</h2>
|
||||
<p>从处警效率来看,{{ sortingRatioValue.Czlfx[1]?.name }}处警的,占到{{ sortingRatioValue.Czlfx[0]?.ratio
|
||||
}}。表明我区公安机关在接警之后能够在第一时间处警。
|
||||
</p>
|
||||
<MaleNightingalePicture :title="{ text: '效率维度', left: 'center' }" style="height: 550px;"
|
||||
:chartData="dataList.withinTj" :innerRadius="0" :outerRadius="150" :padAngle="0" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<AddForm ref="addForm" :dict="{ D_GS_XS_LX }" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import PageTitle from "@/components/aboutTable/PageTitle.vue";
|
||||
// import { useExportToPDF } from './components/a.js';
|
||||
import Search from "@/components/aboutTable/Search.vue";
|
||||
import html2canvas from 'html2canvas';
|
||||
import jsPDF from 'jspdf';
|
||||
|
||||
import MaleNightingalePicture from './components/maleNightingalePicture.vue'
|
||||
import Histogram from './components/histogram.vue'
|
||||
import { timeValidate } from '@/utils/tools.js'
|
||||
import { getItem, setItem } from '@/utils/storage'
|
||||
import { fxbgDywdtj, getDictItem, fxbgJqlxtj, fxbgJqlytj, fxbgYdfx, fxbgXsfx, fxgbCljgf, fxgbCzlfx, fxbgTj } from '@/api/semanticAnalysis'
|
||||
// import AddForm from './components/a/addForm.vue'
|
||||
import { qcckGet, qcckPost } from "@/api/qcckApi.js";
|
||||
// import ItemXs from './components/itemXs/itemXs.vue'
|
||||
import { reactive, ref, onMounted, getCurrentInstance, nextTick, computed, watch } from "vue";
|
||||
const { proxy } = getCurrentInstance();
|
||||
const { D_GS_XS_LX } = proxy.$dict("D_GS_XS_LX"); //获取字典数据
|
||||
const dictItemList = ref([])
|
||||
const searchConfiger = reactive([
|
||||
{
|
||||
label: "时间",
|
||||
showType: "daterange",
|
||||
prop: "startTime",
|
||||
placeholder: "请输入警情名称",
|
||||
},
|
||||
{
|
||||
showType: "defaultSlot",
|
||||
// prop: "bjlbList",
|
||||
placeholder: "请选择情报类型",
|
||||
label: "警情类别"
|
||||
},
|
||||
])
|
||||
const bjlbList = ref([])
|
||||
onMounted(() => {
|
||||
tabHeightFn()
|
||||
})
|
||||
const tabHeight = ref(0)
|
||||
// 表格高度计算
|
||||
const tabHeightFn = () => {
|
||||
tabHeight.value = window.innerHeight - 300
|
||||
window.onresize = function () {
|
||||
tabHeightFn();
|
||||
};
|
||||
};
|
||||
const pageData = reactive({
|
||||
parameter: {},
|
||||
total: 0,
|
||||
loading: false,
|
||||
tableData: []
|
||||
})
|
||||
const onSearch = (val) => {
|
||||
const promes = {
|
||||
startTime: val.startTime ? val.startTime[0] : '',
|
||||
endTime: val.startTime ? val.startTime[1] : '',
|
||||
}
|
||||
const bjlbLists = bjlbList.value ? bjlbList.value.join(',') : ""
|
||||
pageData.parameter = { bjlbList: bjlbLists, ...promes }
|
||||
funAll()
|
||||
}
|
||||
|
||||
// 数据处理
|
||||
|
||||
const dataList = reactive({
|
||||
dyTj: {
|
||||
xAxisData: [],
|
||||
seriesData: [],
|
||||
},
|
||||
jqlxTj: [],
|
||||
jqlyTj: [],
|
||||
CljgfTj: [],
|
||||
XsfxTj: [],
|
||||
})
|
||||
const sortingRatioValue = reactive({
|
||||
Dywdtj: [],
|
||||
Ydfx: [],
|
||||
Cljgf: [],
|
||||
Czlfx: [],
|
||||
Lyfx: [],
|
||||
Jqlx: []
|
||||
})
|
||||
const sortingRatio = (data) => {
|
||||
// 提取所有number值
|
||||
// 计算总数
|
||||
const total = data.reduce((sum, item) => sum + item.number, 0);
|
||||
|
||||
// 找出第一大和第二大的值及其对应的name
|
||||
const sortedData = [...data].sort((a, b) => b.number - a.number);
|
||||
|
||||
const dataValue = sortedData.map(item => {
|
||||
return {
|
||||
...item,
|
||||
ratio: total > 0 ? (item.number / total * 100).toFixed(2) + '%' : '0%',
|
||||
}
|
||||
})
|
||||
|
||||
return dataValue
|
||||
}
|
||||
// 地域统计
|
||||
const getfxbgDywdtj = () => {
|
||||
let params = {
|
||||
...pageData.parameter,
|
||||
}
|
||||
fxbgDywdtj(params).then(res => {
|
||||
dataList.dyTj.xAxisData = res.map(it => it.ssbm)
|
||||
dataList.dyTj.seriesData = [];
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
dataList.dyTj.seriesData[i] = [];
|
||||
for (let j = 0; j < res.length; j++) {
|
||||
dataList.dyTj.seriesData[i][j] = 0;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < dataList.dyTj.seriesData.length; i++) {
|
||||
dataList.dyTj.seriesData[i][i] = res[i].number
|
||||
}
|
||||
sortingRatioValue.Dywdtj = sortingRatio(res)
|
||||
})
|
||||
}
|
||||
// 警情类型统计
|
||||
const getfxbgJqlxtj = () => {
|
||||
let params
|
||||
if (pageData.parameter.bjlbList) {
|
||||
params = {
|
||||
...pageData.parameter,
|
||||
}
|
||||
} else {
|
||||
params = {
|
||||
...pageData.parameter,
|
||||
bjlbList: dictItemList.value.map(item => item.dm).join(',')
|
||||
}
|
||||
}
|
||||
fxbgJqlxtj(params).then(res => {
|
||||
sortingRatioValue.Jqlx = sortingRatio(res)
|
||||
dataList.jqlxTj = res.map(item => {
|
||||
return {
|
||||
name: item.name,
|
||||
value: item.number
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
// 警情来源统计
|
||||
const getfxbgJqlytj = () => {
|
||||
let params = {
|
||||
...pageData.parameter,
|
||||
}
|
||||
fxbgJqlytj(params).then(res => {
|
||||
sortingRatioValue.Lyfx = sortingRatio(res)
|
||||
dataList.jqlyTj = res.map(item => {
|
||||
return {
|
||||
name: item.name,
|
||||
value: item.number
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//分析报告-时间维度-月分析
|
||||
const getfxbgYdfx = () => {
|
||||
let params = {
|
||||
...pageData.parameter,
|
||||
}
|
||||
fxbgYdfx(params).then(res => {
|
||||
sortingRatioValue.Ydfx = sortingRatio(res)
|
||||
dataList.ydTj = res.map(item => {
|
||||
return {
|
||||
name: item.month,
|
||||
value: item.number
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
// 分析报告-处理结果分析
|
||||
const getfxgbCljgf = () => {
|
||||
let params = {
|
||||
...pageData.parameter,
|
||||
}
|
||||
|
||||
fxgbCljgf(params).then(res => {
|
||||
sortingRatioValue.Cljgf = sortingRatio(res)
|
||||
dataList.CljgfTj = res.map(item => {
|
||||
return {
|
||||
name: item.name,
|
||||
value: item.number
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
// 分析报告-处置率分析
|
||||
const getfxgbCzlfx = () => {
|
||||
let params = {
|
||||
...pageData.parameter,
|
||||
}
|
||||
fxgbCzlfx(params).then(res => {
|
||||
dataList.withinTj = [{
|
||||
name: "超时分流(超过24小时)",
|
||||
value: res.within24h ? res.within24h : 0
|
||||
}, {
|
||||
name: "按时分流(24小时内)",
|
||||
value: res.over24h ? res.over24h : 0
|
||||
}]
|
||||
sortingRatioValue.Czlfx = sortingRatio(dataList.withinTj)
|
||||
})
|
||||
}
|
||||
|
||||
const getfxbgTj = () => {
|
||||
let params = {
|
||||
...pageData.parameter,
|
||||
}
|
||||
fxbgTj(params).then(res => {
|
||||
console.log(res, "xxx");
|
||||
dataList.XsfxTj = res
|
||||
})
|
||||
}
|
||||
const TimeValue = ref({
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
})
|
||||
const nd = ref()
|
||||
|
||||
watch(() => pageData.parameter, (newVal) => {
|
||||
if (newVal.startTime) {
|
||||
TimeValue.value.startTime = newVal.startTime
|
||||
TimeValue.value.endTime = newVal.endTime
|
||||
if (timeValidate(newVal.startTime, 'yd') == timeValidate(newVal.endTime, 'yd')) {
|
||||
nd.value = timeValidate(newVal.startTime, 'yd')
|
||||
} else {
|
||||
nd.value = `${timeValidate(newVal.startTime, 'yd')}至${timeValidate(newVal.endTime, 'yd')}`
|
||||
}
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
const deptId = ref({
|
||||
name: ''
|
||||
})
|
||||
const Time = () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
const startOfYear = new Date(currentYear, 0, 1); // 今年1月1日
|
||||
const endOfYear = new Date(currentYear, 11, 31, 23); // 今年12月31日23:59:59.999
|
||||
const year = startOfYear.getFullYear();
|
||||
const month = String(startOfYear.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(startOfYear.getDate()).padStart(2, '0');
|
||||
const endYear = endOfYear.getFullYear();
|
||||
const endMonth = String(endOfYear.getMonth() + 1).padStart(2, '0');
|
||||
const endDay = String(endOfYear.getDate()).padStart(2, '0');
|
||||
const devt = getItem('deptId')
|
||||
deptId.value.name = devt[0].deptName
|
||||
nd.value = timeValidate('', 'yd')
|
||||
deptId.value.time = timeValidate('', 'ydm')
|
||||
deptId.value.ord=timeValidate('', 'mm')+ Math.floor(Math.random() * 90000) + 10000;
|
||||
TimeValue.value.startTime = `${year}-${month}-${day}`
|
||||
TimeValue.value.endTime = `${endYear}-${endMonth}-${endDay}`
|
||||
|
||||
}
|
||||
Time()
|
||||
const funAll = () => {
|
||||
getfxbgDywdtj()
|
||||
getfxbgJqlxtj()
|
||||
getfxbgJqlytj()
|
||||
getfxgbCljgf()
|
||||
getfxbgYdfx()
|
||||
getfxgbCzlfx()
|
||||
getfxbgTj()
|
||||
}
|
||||
|
||||
const getDictItemList = () => {
|
||||
const promes = {
|
||||
dictCode: "00000000"
|
||||
}
|
||||
getDictItem(promes).then(res => {
|
||||
dictItemList.value = res
|
||||
funAll()
|
||||
})
|
||||
}
|
||||
|
||||
const data = ref()
|
||||
watch(() => dictItemList.value, (val) => {
|
||||
data.value = val
|
||||
}, { deep: true })
|
||||
getDictItemList()
|
||||
|
||||
|
||||
const tableBox = ref(null);
|
||||
|
||||
async function generatePDF(filename = 'document.pdf') {
|
||||
// try {
|
||||
const element = tableBox.value;
|
||||
console.log();
|
||||
|
||||
// // 保存原始滚动位置和样式
|
||||
// const originalScrollTop = element.scrollTop;
|
||||
// const originalOverflow = element.style.overflow;
|
||||
|
||||
// // 临时允许元素滚动并获取完整高度
|
||||
// element.style.overflow = 'visible';
|
||||
|
||||
// const canvas = await html2canvas(element, {
|
||||
// scale: 2, // 提高清晰度
|
||||
// useCORS: true, // 允许跨域图片
|
||||
// logging: false, // 关闭日志
|
||||
// scrollY: 0, // 禁止窗口滚动
|
||||
// scrollX: 0,
|
||||
// windowWidth: element.scrollWidth, // 设置窗口大小为元素大小
|
||||
// windowHeight: element.scrollHeight,
|
||||
// width: element.scrollWidth, // 设置canvas大小为元素大小
|
||||
// height: element.scrollHeight
|
||||
// });
|
||||
|
||||
// // 恢复原始样式和滚动位置
|
||||
// element.style.overflow = originalOverflow;
|
||||
// element.scrollTop = originalScrollTop;
|
||||
|
||||
// const imgData = canvas.toDataURL('image/png');
|
||||
// const pdf = new jsPDF('p', 'mm', 'a4');
|
||||
|
||||
// const imgProps = pdf.getImageProperties(imgData);
|
||||
// const pdfWidth = pdf.internal.pageSize.getWidth();
|
||||
// const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
|
||||
|
||||
// // 计算需要多少页
|
||||
// const pageHeight = pdf.internal.pageSize.getHeight();
|
||||
// let heightLeft = pdfHeight;
|
||||
// let position = 0;
|
||||
|
||||
// // 添加第一页
|
||||
// pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, pdfHeight);
|
||||
// heightLeft -= pageHeight;
|
||||
|
||||
// // 添加更多页(如果需要)
|
||||
// while (heightLeft > 0) {
|
||||
// position = -heightLeft;
|
||||
// pdf.addPage();
|
||||
// pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, pdfHeight);
|
||||
// heightLeft -= pageHeight;
|
||||
// }
|
||||
|
||||
// pdf.save(filename);
|
||||
|
||||
// } catch (error) {
|
||||
// console.error('生成PDF时出错:', error);
|
||||
// alert('生成PDF时出错,请查看控制台获取详细信息');
|
||||
// }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.headline {
|
||||
text-align: center;
|
||||
color: red;
|
||||
}
|
||||
|
||||
p {
|
||||
text-indent: 2em;
|
||||
/* 首行缩进2个汉字 */
|
||||
margin: 1em 0;
|
||||
/* 段落间距 */
|
||||
line-height: 1.6;
|
||||
/* 行高 */
|
||||
font-size: 16px;
|
||||
/* 字体大小 */
|
||||
text-align: justify;
|
||||
/* 两端对齐 */
|
||||
}
|
||||
|
||||
/* 特殊情况处理 */
|
||||
p.no-indent {
|
||||
text-indent: 0;
|
||||
/* 不需要缩进的段落 */
|
||||
}
|
||||
|
||||
p.first-no-indent:first-of-type {
|
||||
text-indent: 0;
|
||||
/* 第一个段落不缩进 */
|
||||
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user