feat(仪表板): 前端增加导出分页功能

This commit is contained in:
fit2cloud-chenyw 2023-01-12 18:52:06 +08:00
parent ff3883cd59
commit 84ae4dbb78
10 changed files with 267 additions and 3 deletions

View File

@ -165,6 +165,44 @@ public class XEmailTaskServer {
return xpackEmailCreate;
}
@DeRateLimiter
@PostMapping(value = "/screenpdf", produces = {MediaType.APPLICATION_PDF_VALUE})
public ResponseEntity<ByteArrayResource> screenpdf(@RequestBody XpackEmailViewRequest request) {
EmailXpackService emailXpackService = SpringContextUtil.getBean(EmailXpackService.class);
String url = ServletUtils.domain() + "/#/previewScreenShot/" + request.getPanelId() + "/true";
byte[] bytes = null;
try {
String currentToken = ServletUtils.getToken();
Future<?> future = priorityExecutor.submit(() -> {
try {
return emailXpackService.printPdf(url, currentToken, buildPixel(request.getPixel()));
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
DEException.throwException("预览失败,请联系管理员");
}
return null;
}, 0);
Object object = future.get();
if (ObjectUtils.isNotEmpty(object)) {
bytes = (byte[]) object;
if (ArrayUtil.isNotEmpty(bytes)) {
String fileName = request.getPanelId() + ".pdf";
ByteArrayResource bar = new ByteArrayResource(bytes);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
ContentDisposition contentDisposition = ContentDisposition.parse("attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
headers.setContentDisposition(contentDisposition);
return new ResponseEntity(bar, headers, HttpStatus.OK);
}
}
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
DEException.throwException("预览失败,请联系管理员");
}
return null;
}
@DeRateLimiter
@PostMapping(value = "/screenshot", produces = {MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE})
public ResponseEntity<ByteArrayResource> screenshot(@RequestBody XpackEmailViewRequest request) {

View File

@ -297,6 +297,9 @@ export default {
// this.$emit('handleDrop', e)
// }
handleDrop(e) {
if (!this.dragComponentInfo) {
return
}
this.dragComponentInfo.moveStatus = 'drop'
//
this.dropComponentInfo = deepCopy(this.dragComponentInfo)

View File

@ -153,6 +153,25 @@
@change="styleChange"
/>
</el-dropdown-item>
<el-dropdown-item>
<span class="icon iconfont el-icon-reading icon16" />
<el-tooltip
class="item"
:content="$t('panel.export_pdf_page_remark')"
placement="top-start"
>
<span class="text14 margin-left8">{{ $t('panel.export_pdf_page') }}</span>
</el-tooltip>
<el-switch
v-model="showPageLine"
:class="[{['grid-active']: showPageLine},'margin-left8']"
size="mini"
@change="showPageLineChange"
/>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</span>
@ -247,6 +266,7 @@ export default {
},
data() {
return {
showPageLine: false,
showGridSwitch: false,
mobileLayoutInitStatus: false,
isShowPreview: false,
@ -305,6 +325,7 @@ export default {
this.scale = this.canvasStyleData.scale
this.mobileLayoutInitStatus = this.mobileLayoutStatus
this.showGridSwitch = this.canvasStyleData.aidedDesign.showGrid
this.showPageLine = this.canvasStyleData.pdfPageLine?.showPageLine
this.autoCache()
},
beforeDestroy() {
@ -323,6 +344,7 @@ export default {
},
editPanelInit() {
this.showGridSwitch = this.canvasStyleData.aidedDesign.showGrid
this.showPageLine = this.canvasStyleData.pdfPageLine?.showPageLine
},
close() {
//
@ -589,6 +611,10 @@ export default {
this.$store.commit('canvasChange')
this.canvasStyleData.aidedDesign.showGrid = !this.canvasStyleData.aidedDesign.showGrid
},
showPageLineChange() {
this.$store.commit('canvasChange')
this.canvasStyleData.pdfPageLine.showPageLine = !this.canvasStyleData.pdfPageLine.showPageLine
},
// batch option
batchOption() {
bus.$emit('change_panel_right_draw', !this.batchOptStatus)

View File

@ -12,6 +12,11 @@
@mousedown="handleMouseDown"
@scroll="canvasScroll"
>
<page-line-editor
v-if="showPageLine"
ref="main-page-line"
:canvas-style-data="canvasStyleData"
/>
<!-- 网格线 -->
<Grid
v-if="showGrid"
@ -121,7 +126,7 @@
:canvas-id="canvasId"
/>
<!-- 右击菜单 -->
<ContextMenu/>
<ContextMenu />
<!-- 对齐标线 -->
<span
@ -157,6 +162,7 @@ import MarkLine from './MarkLine'
import Area from './Area'
import eventBus from '@/components/canvas/utils/eventBus'
import Grid from './Grid'
import PageLineEditor from './PageLineEditor'
import PGrid from './PGrid'
import { changeStyleWithScale } from '@/components/canvas/utils/translate'
import UserViewDialog from '@/components/canvas/customComponent/UserViewDialog'
@ -763,7 +769,8 @@ export default {
UserViewDialog,
DeOutWidget,
DragShadow,
LinkJumpSet
LinkJumpSet,
PageLineEditor
},
props: {
parentForbid: {
@ -944,11 +951,26 @@ export default {
return false
}
},
showPageLine() {
if (this.canvasStyleData && this.canvasStyleData.pdfPageLine) {
return this.canvasStyleData.pdfPageLine.showPageLine
}
return false
},
editStyle() {
return {
height: this.outStyle.height + this.scrollTop + 'px !important'
}
},
scrollHeight() {
let baseHeight = 0
this.componentData.forEach(item => {
const top = this.getShapeStyleIntDeDrag(item.style, 'top')
const height = this.getShapeStyleIntDeDrag(item.style, 'height')
baseHeight = Math.max(baseHeight, top + height)
})
return baseHeight
},
customStyle() {
let style = {
width: '100%',
@ -1024,6 +1046,20 @@ export default {
},
deep: true
},
scrollHeight: {
handler(newVal, oldVla) {
this.$nextTick(() => {
if (newVal !== oldVla && this.showPageLine) {
const lineRef = this.$refs['main-page-line']
if (lineRef?.init) {
lineRef.init(newVal)
}
}
console.log(newVal)
})
},
deep: true
},
outStyle: {
handler(newVal, oldVla) {
this.resizeParentBoundsRef()
@ -1251,6 +1287,9 @@ export default {
},
changeStyleWithScale,
handleMouseDown(e) {
if (this.isPageLineTarget(e)) {
return
}
// e.preventDefault() drop
if (!this.curComponent || (this.curComponent.component !== 'v-text' && this.curComponent.component !== 'rect-shape')) {
e.preventDefault()
@ -1259,6 +1298,9 @@ export default {
//
this.containerMouseDown(e)
},
isPageLineTarget(e) {
return e.target.classList && [...e.target.classList].includes('page-line-item')
},
hideArea() {
this.isShowArea = 0
@ -1517,6 +1559,9 @@ export default {
}
},
handleDragOver(e) {
if (!this.dragComponentInfo?.shadowStyle) {
return
}
this.dragComponentInfo.shadowStyle.x = e.pageX - 220
this.dragComponentInfo.shadowStyle.y = e.pageY - 90 + this.scrollTop
this.dragComponentInfo.style.left = this.dragComponentInfo.shadowStyle.x / this.scalePointWidth

View File

@ -0,0 +1,136 @@
<template>
<div class="page-line-container">
<div
v-for="(line, index) in lineLocations"
:key="index"
:ref="baseLineKey + index"
class="page-line-item"
:style="{'top': line + 'px', 'background': panelBg}"
@mousedown="handleMouseDown"
>
<span class="top-span" />
<span class="bottom-span" />
</div>
</div>
</template>
<script>
import { reverseColor } from '@/views/chart/chart/common/common'
export default {
name: 'PageLineEditor',
props: {
canvasStyleData: {
type: Object,
require: true
}
},
data() {
return {
baseLineKey: 'page-line-',
lineLocations: [],
curLineHieght: 0,
clientStartY: 0,
startTop: 0,
movingLineHeight: 0,
scrollHeight: 0,
baseLineColor: '#1F2329'
}
},
computed: {
windowHeight() {
return window.innerHeight - 56
},
panelBg() {
if (this.canvasStyleData.panel.backgroundType === 'color') {
return reverseColor(this.canvasStyleData.panel.color)
}
return this.baseLineColor
},
pdfPageLine() {
return this.$store.state.canvasStyleData.pdfPageLine
}
},
mounted() {
this.$nextTick(() => {
this.init()
})
},
created() {
},
methods: {
resize() {
this.$nextTick(() => {
this.init()
})
},
init(scrollHeight) {
this.lineLocations = []
this.scrollHeight = scrollHeight || document.getElementById('canvas-id-canvas-main').scrollHeight
if (this.pdfPageLine?.proportion) {
this.curLineHieght = this.pdfPageLine.proportion * this.scrollHeight
} else {
this.curLineHieght = this.windowHeight
this.saveLineHeight()
}
let curLineLocation = this.curLineHieght
while (curLineLocation < this.scrollHeight) {
this.lineLocations.push(curLineLocation)
curLineLocation += this.curLineHieght
}
},
handleMouseDown(e) {
this.clientStartY = e.clientY
this.startTop = parseInt(e.target.style.top)
document.onmousemove = ev => this.handleMouseMove(e, ev)
document.onmouseup = () => {
this.curLineHieght = this.movingLineHeight
this.saveLineHeight()
this.init()
document.onmousemove = document.onmouseup = null
}
},
handleMouseMove(e, ev) {
const moveInstance = ev.clientY - this.clientStartY
this.movingLineHeight = this.curLineHieght + moveInstance
e.target.style.top = this.startTop + moveInstance + 'px'
},
saveLineHeight() {
this.$store.commit('canvasChange')
this.canvasStyleData.pdfPageLine.proportion = this.curLineHieght / this.scrollHeight
this.pdfPageLine.proportion = this.curLineHieght / this.scrollHeight
}
}
}
</script>
<style lang="scss" scoped>
.page-line-container {
/* width: 100%;
height: 100%; */
}
.page-line-item {
height: 1px;
width: 100%;
margin: 2px 0;
position: absolute;
z-index: 999;
cursor: row-resize;
span {
width: 20px;
background: #bbb;
left: calc(50% - 5px);
height: 2px;
position: absolute;
}
.top-span {
top: -3px !important;
}
.bottom-span {
top: 3px !important;
}
}
</style>

View File

@ -8,7 +8,7 @@ import {
import { ApplicationContext } from '@/utils/ApplicationContext'
import { uuid } from 'vue-uuid'
import store from '@/store'
import { AIDED_DESIGN, MOBILE_SETTING, PANEL_CHART_INFO, TAB_COMMON_STYLE } from '@/views/panel/panel'
import { AIDED_DESIGN, MOBILE_SETTING, PANEL_CHART_INFO, TAB_COMMON_STYLE, PAGE_LINE_DESIGN } from '@/views/panel/panel'
import html2canvas from 'html2canvasde'
export function deepCopy(target) {
@ -83,6 +83,7 @@ export function panelDataPrepare(componentData, componentStyle, callback) {
componentStyle.refreshUnit = (componentStyle.refreshUnit || 'minute')
componentStyle.refreshViewEnable = (componentStyle.refreshViewEnable === undefined ? true : componentStyle.refreshViewEnable)
componentStyle.aidedDesign = (componentStyle.aidedDesign || deepCopy(AIDED_DESIGN))
componentStyle.pdfPageLine = (componentStyle.pdfPageLine || deepCopy(PAGE_LINE_DESIGN))
componentStyle.chartInfo = (componentStyle.chartInfo || deepCopy(PANEL_CHART_INFO))
componentStyle.chartInfo.tabStyle = (componentStyle.chartInfo.tabStyle || deepCopy(TAB_COMMON_STYLE))
componentStyle.themeId = (componentStyle.themeId || 'NO_THEME')

View File

@ -2244,6 +2244,8 @@ export default {
aided_grid: 'Aided Grid',
aided_grid_open: 'Open',
aided_grid_close: 'Close',
export_pdf_page: 'Pagination Line',
export_pdf_page_remark: 'Only valid for API export dashboard PDF pagination',
subject_no_edit: 'System Subject Can Not Edit',
subject_name_not_null: 'Subject Name Can Not Be Null And Less Than 20 charts',
is_enable: 'Enable',

View File

@ -2238,6 +2238,8 @@ export default {
aided_grid: '輔助設計網格',
aided_grid_open: '打開',
aided_grid_close: '關閉',
export_pdf_page: '分頁線',
export_pdf_page_remark: '僅對API導出儀表板PDF分頁有效',
subject_no_edit: '繫統主題不能修改',
subject_name_not_null: '主題名稱需要1~20字符',
is_enable: '是否啟用',

View File

@ -2238,6 +2238,8 @@ export default {
aided_grid: '辅助设计网格',
aided_grid_open: '打开',
aided_grid_close: '关闭',
export_pdf_page: '分页线',
export_pdf_page_remark: '仅对API导出仪表板PDF分页有效',
subject_no_edit: '系统主题不能修改',
subject_name_not_null: '主题名称需要1~20字符',
is_enable: '是否启用',

View File

@ -69,6 +69,10 @@ export const CANVAS_STYLE = {
showGrid: false,
matrixBase: 4 // 当前matrix的基数 是pcMatrixCount的几倍
}, // 辅助设计
pdfPageLine: {
showPageLine: false,
proportion: null
},
refreshViewEnable: true, // 开启视图刷新(默认开启)
refreshViewLoading: true, // 仪表板视图loading提示
refreshUnit: 'minute', // 仪表板刷新时间带外 默认 分钟
@ -82,6 +86,11 @@ export const AIDED_DESIGN = {
matrixBase: 1 // 当前matrix的基数 是pcMatrixCount的几倍
}
export const PAGE_LINE_DESIGN = {
showPageLine: false,
proportion: null
}
export const DEFAULT_COMMON_CANVAS_STYLE_STRING = {
...CANVAS_STYLE
}