This commit is contained in:
lcw
2025-08-27 17:26:29 +08:00
parent 42f5e37f65
commit f4c108b4b4
47 changed files with 4087 additions and 300 deletions

View 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) {
}

View File

@ -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>

View File

@ -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>

View 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>