mirror of
https://gitee.com/dromara/go-view.git
synced 2025-02-23 07:42:56 +08:00
feat:新增图片导出功能
This commit is contained in:
parent
f95d940ff0
commit
496c097e5f
@ -15,6 +15,7 @@
|
||||
"crypto-ts": "^1.0.2",
|
||||
"echarts-liquidfill": "3",
|
||||
"highlight.js": "^11.5.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"naive-ui": "^2.25.2",
|
||||
"pinia": "^2.0.6",
|
||||
"screenfull": "^6.0.0",
|
||||
|
@ -24,6 +24,7 @@ specifiers:
|
||||
eslint-plugin-prettier: ^4.0.0
|
||||
eslint-plugin-vue: ^8.2.0
|
||||
highlight.js: ^11.5.0
|
||||
html2canvas: ^1.4.1
|
||||
lodash: ~4.17.21
|
||||
mockjs: ^1.1.0
|
||||
naive-ui: ^2.25.2
|
||||
@ -56,6 +57,7 @@ dependencies:
|
||||
crypto-ts: r2.cnpmjs.org/crypto-ts/1.0.2
|
||||
echarts-liquidfill: r2.cnpmjs.org/echarts-liquidfill/3.1.0_echarts@5.3.0
|
||||
highlight.js: registry.npmjs.org/highlight.js/11.5.0
|
||||
html2canvas: 1.4.1
|
||||
naive-ui: registry.npmjs.org/naive-ui/2.25.2_vue@3.2.24
|
||||
pinia: rg.cnpmjs.org/pinia/2.0.6_typescript@4.5.2+vue@3.2.24
|
||||
screenfull: rg.cnpmjs.org/screenfull/6.0.0
|
||||
@ -100,6 +102,17 @@ devDependencies:
|
||||
|
||||
packages:
|
||||
|
||||
/base64-arraybuffer/1.0.2:
|
||||
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
dev: false
|
||||
|
||||
/css-line-break/2.1.0:
|
||||
resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==}
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
dev: false
|
||||
|
||||
/esbuild-android-arm64/0.13.15:
|
||||
resolution: {integrity: sha512-m602nft/XXeO8YQPUDVoHfjyRVPdPgjyyXOxZ44MK/agewFFkPa8tUo6lAzSWh5Ui5PB4KR9UIFTSBKh/RrCmg==}
|
||||
cpu: [arm64]
|
||||
@ -261,6 +274,14 @@ packages:
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
|
||||
/html2canvas/1.4.1:
|
||||
resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
dependencies:
|
||||
css-line-break: 2.1.0
|
||||
text-segmentation: 1.0.3
|
||||
dev: false
|
||||
|
||||
/jest-diff/27.4.6:
|
||||
resolution: {integrity: sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==}
|
||||
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||
@ -290,6 +311,12 @@ packages:
|
||||
resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==}
|
||||
dev: false
|
||||
|
||||
/text-segmentation/1.0.3:
|
||||
resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==}
|
||||
dependencies:
|
||||
utrie: 1.0.2
|
||||
dev: false
|
||||
|
||||
/uglify-js/3.15.1:
|
||||
resolution: {integrity: sha512-FAGKF12fWdkpvNJZENacOH0e/83eG6JyVQyanIJaBXCN1J11TUQv1T1/z8S+Z0CG0ZPk1nPcreF/c7lrTd0TEQ==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
@ -298,6 +325,12 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/utrie/1.0.2:
|
||||
resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==}
|
||||
dependencies:
|
||||
base64-arraybuffer: 1.0.2
|
||||
dev: false
|
||||
|
||||
/vfonts/0.0.3:
|
||||
resolution: {integrity: sha512-nguyw8L6Un8eelg1vQ31vIU2ESxqid7EYmy8V+MDeMaHBqaRSkg3dTBToC1PR00D89UzS/SLkfYPnx0Wf23IQQ==}
|
||||
dev: false
|
||||
|
@ -8,7 +8,8 @@ import {
|
||||
Pencil as PencilIcon,
|
||||
HammerOutline as HammerIcon,
|
||||
DesktopOutline as DesktopOutlineIcon,
|
||||
DownloadOutline as DownloadIcon,
|
||||
Download as DownloadIcon,
|
||||
DownloadOutline as DownloadOutlineIcon,
|
||||
Open as OpenIcon,
|
||||
Send as SendIcon,
|
||||
InformationCircleOutline as InformationCircleIcon,
|
||||
@ -122,6 +123,7 @@ const ionicons5 = {
|
||||
DesktopOutlineIcon,
|
||||
// 下载
|
||||
DownloadIcon,
|
||||
DownloadOutlineIcon,
|
||||
// 导出
|
||||
OpenIcon,
|
||||
// 导出
|
||||
|
@ -508,16 +508,6 @@ export const useChartEditStore = defineStore({
|
||||
}
|
||||
},
|
||||
// ----------------
|
||||
// * 设置页面变换时候的 Class
|
||||
setPageSizeClass(): void {
|
||||
const dom = this.getEditCanvas.editContentDom
|
||||
if (dom) {
|
||||
dom.classList.add('content-resize')
|
||||
setTimeout(() => {
|
||||
dom.classList.remove('content-resize')
|
||||
}, 600)
|
||||
}
|
||||
},
|
||||
// * 设置页面大小
|
||||
setPageSize(scale: number): void {
|
||||
this.setPageStyle('height', `${this.editCanvasConfig.height * scale}px`)
|
||||
@ -572,15 +562,16 @@ export const useChartEditStore = defineStore({
|
||||
}
|
||||
return remove
|
||||
},
|
||||
// * 设置缩放
|
||||
setScale(scale: number, sys = true): void {
|
||||
if (!this.getEditCanvas.lockScale) {
|
||||
this.setPageSizeClass()
|
||||
/**
|
||||
* * 设置缩放
|
||||
* @param scale 0~1 number 缩放比例;
|
||||
* @param force boolean 强制缩放
|
||||
*/
|
||||
setScale(scale: number, force = false): void {
|
||||
if (!this.getEditCanvas.lockScale || force) {
|
||||
this.setPageSize(scale)
|
||||
this.getEditCanvas.userScale = scale
|
||||
if (sys) {
|
||||
this.getEditCanvas.scale = scale
|
||||
}
|
||||
this.getEditCanvas.scale = scale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,6 @@ $dark: (
|
||||
// hover 边框颜色
|
||||
hover-border-color: $--color-dark-bg-5,
|
||||
// 阴影
|
||||
box-shadow: 0 8px 20px #5252521f
|
||||
box-shadow: 0 8px 10px #1e1e1e1f
|
||||
|
||||
);
|
||||
|
@ -32,5 +32,5 @@ $light: (
|
||||
// hover 边框颜色
|
||||
hover-border-color: $--color-light-bg-4,
|
||||
// 阴影
|
||||
box-shadow: 0 8px 20px #0000001a
|
||||
box-shadow: 0 8px 10px #00000012
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as CryptoJS from 'crypto-ts'
|
||||
|
||||
// 加密
|
||||
const AES_KEY = 'mt'
|
||||
|
||||
export const cryptoEncode = (data: string): string => {
|
||||
|
@ -19,25 +19,36 @@ export const readFile = (file: File) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载数据
|
||||
* @param { string } content 数据内容
|
||||
* @param { ?string } filename 文件名称(默认随机字符)
|
||||
* @param { ?string } fileSuffix 文件名称(默认随机字符)
|
||||
* * 通过 a 标签下载数据
|
||||
* @param url
|
||||
* @param filename
|
||||
* @param fileSuffix
|
||||
*/
|
||||
export const downloadFile = (
|
||||
content: string,
|
||||
filename = new Date().getDate().toString(),
|
||||
fileSuffix?: string
|
||||
) => {
|
||||
export const downloadByA = (url: string, filename = new Date().getDate().toString(), fileSuffix?: string) => {
|
||||
const ele = document.createElement('a') // 创建下载链接
|
||||
ele.download = `${filename}.${fileSuffix}` //设置下载的名称
|
||||
ele.style.display = 'none' // 隐藏的可下载链接
|
||||
// 字符内容转变成blob地址
|
||||
const blob = new Blob([content])
|
||||
ele.href = URL.createObjectURL(blob)
|
||||
ele.href = url
|
||||
// 绑定点击时间
|
||||
document.body.appendChild(ele)
|
||||
ele.click()
|
||||
// 然后移除
|
||||
document.body.removeChild(ele)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载数据
|
||||
* @param { string } content 数据内容
|
||||
* @param { ?string } filename 文件名称(默认随机字符)
|
||||
* @param { ?string } fileSuffix 文件名称(默认随机字符)
|
||||
*/
|
||||
export const downloadTextFile = (
|
||||
content: string,
|
||||
filename = new Date().getDate().toString(),
|
||||
fileSuffix?: string
|
||||
) => {
|
||||
// 字符内容转变成blob地址
|
||||
const blob = new Blob([content])
|
||||
downloadByA(URL.createObjectURL(blob), filename, fileSuffix)
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ import { NIcon } from 'naive-ui'
|
||||
import screenfull from 'screenfull'
|
||||
import throttle from 'lodash/throttle'
|
||||
import Image_404 from '../assets/images/exception/image-404.png'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { downloadByA } from './file'
|
||||
|
||||
/**
|
||||
* * 判断是否是开发环境
|
||||
@ -146,7 +148,7 @@ export const addEventListener = <K extends keyof WindowEventMap>(
|
||||
type,
|
||||
throttle(listener, 300, {
|
||||
leading: true,
|
||||
trailing: false
|
||||
trailing: false,
|
||||
}),
|
||||
options
|
||||
)
|
||||
@ -163,3 +165,21 @@ export const removeEventListener = <K extends keyof WindowEventMap>(
|
||||
if (!target) return
|
||||
target.removeEventListener(type, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* * 截取画面为图片
|
||||
* @param html 需要截取的 DOM
|
||||
*/
|
||||
export const canvasCut = (html: HTMLElement | null, callback?: Function) => {
|
||||
if (!html) {
|
||||
window['$message'].error('导出失败!')
|
||||
if (callback) callback()
|
||||
return
|
||||
}
|
||||
|
||||
html2canvas(html).then((canvas: HTMLCanvasElement) => {
|
||||
window['$message'].success('导出成功!')
|
||||
downloadByA(canvas.toDataURL(), undefined, 'png')
|
||||
if (callback) callback()
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ref, toRef, nextTick } from 'vue'
|
||||
import { UploadCustomRequestOptions } from 'naive-ui'
|
||||
import { FileTypeEnum } from '@/enums/fileTypeEnum'
|
||||
import { readFile, downloadFile } from '@/utils'
|
||||
import { readFile, downloadTextFile } from '@/utils'
|
||||
|
||||
export const useFile = (targetData: any) => {
|
||||
const uploadFileListRef = ref()
|
||||
@ -35,7 +35,7 @@ export const useFile = (targetData: any) => {
|
||||
const download = () => {
|
||||
try {
|
||||
window['$message'].success('下载中,请耐心等待...')
|
||||
downloadFile(JSON.stringify(targetData.value.option.dataset), undefined, 'json')
|
||||
downloadTextFile(JSON.stringify(targetData.value.option.dataset), undefined, 'json')
|
||||
} catch (error) {
|
||||
window['$message'].error('下载失败,数据错误!')
|
||||
}
|
||||
|
@ -54,12 +54,8 @@ const rangeStyle = computed(() => {
|
||||
<style lang="scss" scoped>
|
||||
@include go(edit-range) {
|
||||
position: relative;
|
||||
border: 1px solid;
|
||||
border-radius: 15px;
|
||||
transform-origin: left top;
|
||||
@include fetch-theme('box-shadow');
|
||||
@include filter-border-color('hover-border-color');
|
||||
@include fetch-theme-custom('border-color', 'background-color4');
|
||||
@include filter-bg-color('background-color2');
|
||||
}
|
||||
</style>
|
||||
|
@ -87,20 +87,18 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@include goId(chart-edit-layout) {
|
||||
@include goId('chart-edit-layout') {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@include background-image('background-point');
|
||||
@extend .go-point-bg;
|
||||
@include goId(chart-edit-content) {
|
||||
padding: 20px;
|
||||
border: 1px solid rgba(0, 0, 0, 0);
|
||||
@include background-image('background-point');
|
||||
@include goId('chart-edit-content') {
|
||||
border-radius: 5px;
|
||||
margin: 15px;
|
||||
overflow: hidden;
|
||||
@extend .go-transition;
|
||||
&.content-resize {
|
||||
border-radius: 15px;
|
||||
@include hover-border-color('hover-border-color');
|
||||
}
|
||||
@include fetch-theme('box-shadow');
|
||||
.edit-content-chart {
|
||||
position: absolute;
|
||||
}
|
||||
|
@ -11,14 +11,16 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { shallowReactive } from 'vue'
|
||||
import { renderIcon, fetchPathByName, routerTurnByPath,setSessionStorage, getLocalStorage } from '@/utils'
|
||||
import { renderIcon, fetchPathByName, routerTurnByPath, setSessionStorage, getLocalStorage } from '@/utils'
|
||||
import { PreviewEnum } from '@/enums/pageEnum'
|
||||
import { StorageEnum } from '@/enums/storageEnum'
|
||||
import { icon } from '@/plugins'
|
||||
import { canvasCut } from '@/utils'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
|
||||
import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
|
||||
|
||||
const { BrowsersOutlineIcon, SendIcon } = icon.ionicons5
|
||||
const { BrowsersOutlineIcon, SendIcon, DownloadIcon } = icon.ionicons5
|
||||
const chartEditStore = useChartEditStore()
|
||||
|
||||
// TODO 我也不知道为什么不能实时获取,必须初始化获取
|
||||
@ -53,6 +55,34 @@ const previewHandle = () => {
|
||||
routerTurnByPath(path, [previewId], undefined, true)
|
||||
}
|
||||
|
||||
// 导出
|
||||
const exportHandle = () => {
|
||||
const ruler = document.getElementById('mb-ruler')
|
||||
const range = document.querySelector('.go-edit-range') as HTMLElement
|
||||
|
||||
// 隐藏边距线
|
||||
if (!ruler || !range) {
|
||||
window['$message'].error('导出失败!')
|
||||
return
|
||||
}
|
||||
// 记录缩放比例
|
||||
const scaleTemp = chartEditStore.getEditCanvas.scale
|
||||
// 去除标尺Dom
|
||||
ruler.style.display = 'none'
|
||||
// 百分百展示页面
|
||||
chartEditStore.setScale(1, true)
|
||||
|
||||
window['$message'].warning('生成截图和数据中, 请耐心等待...')
|
||||
setTimeout(() => {
|
||||
canvasCut(range, () => {
|
||||
// 放开边距线
|
||||
if (ruler) ruler.style.display = 'block'
|
||||
// 还原页面大小
|
||||
chartEditStore.setScale(scaleTemp, true)
|
||||
})
|
||||
}, 600)
|
||||
}
|
||||
|
||||
// 发布
|
||||
const sendHandle = () => {
|
||||
window['$message'].warning('该功能暂未实现(因为压根没有后台)')
|
||||
@ -60,12 +90,17 @@ const sendHandle = () => {
|
||||
|
||||
const btnList = shallowReactive([
|
||||
{
|
||||
key: '',
|
||||
select: true,
|
||||
title: '预览',
|
||||
icon: renderIcon(BrowsersOutlineIcon),
|
||||
event: previewHandle
|
||||
},
|
||||
{
|
||||
select: true,
|
||||
title: '下载',
|
||||
icon: renderIcon(DownloadIcon),
|
||||
event: exportHandle
|
||||
},
|
||||
{
|
||||
select: true,
|
||||
title: '发布',
|
||||
|
@ -93,7 +93,7 @@ const {
|
||||
TrashIcon,
|
||||
PencilIcon,
|
||||
BrowsersOutlineIcon,
|
||||
DownloadIcon,
|
||||
DownloadOutlineIcon,
|
||||
HammerIcon,
|
||||
SendIcon
|
||||
} = icon.ionicons5
|
||||
@ -153,7 +153,7 @@ const selectOptions = ref([
|
||||
{
|
||||
label: renderLang('global.r_download'),
|
||||
key: 'download',
|
||||
icon: renderIcon(DownloadIcon)
|
||||
icon: renderIcon(DownloadOutlineIcon)
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
|
Loading…
Reference in New Issue
Block a user