feat:新增图片导出功能

This commit is contained in:
奔跑的面条 2022-04-05 19:01:52 +08:00
parent f95d940ff0
commit 496c097e5f
14 changed files with 140 additions and 53 deletions

View File

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

View File

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

View File

@ -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,
// 导出

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import * as CryptoJS from 'crypto-ts'
// 加密
const AES_KEY = 'mt'
export const cryptoEncode = (data: string): string => {

View File

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

View File

@ -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()
})
}

View File

@ -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('下载失败,数据错误!')
}

View File

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

View File

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

View File

@ -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: '发布',

View File

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