diff --git a/src/hooks/useExport.ts b/src/hooks/useExport.ts index 173d1bbe..a5f52dc2 100644 --- a/src/hooks/useExport.ts +++ b/src/hooks/useExport.ts @@ -15,6 +15,9 @@ import { svg2Base64 } from '@/utils/svg2Base64' import { message } from 'ant-design-vue' import useAddSlidesOrElements from '@/hooks/useAddSlidesOrElements' +const INCH_PX_RATIO = 100 +const PT_PX_RATIO = 0.75 + interface ExportImageConfig { quality: number width: number @@ -170,7 +173,7 @@ export default () => { const options: pptxgen.TextPropsOptions = {} if (styleObj['font-size']) { - options.fontSize = parseInt(styleObj['font-size']) * 0.75 + options.fontSize = parseInt(styleObj['font-size']) * PT_PX_RATIO } if (styleObj['color']) { options.color = formatColor(styleObj['color']).color @@ -211,12 +214,12 @@ export default () => { if (styleObj['href']) options.hyperlink = { url: styleObj['href'] } if (bulletFlag && styleObj['list-type'] === 'ol') { - options.bullet = { type: 'number', indent: 20 * 0.75 } + options.bullet = { type: 'number', indent: 20 * PT_PX_RATIO } options.paraSpaceBefore = 0.1 bulletFlag = false } if (bulletFlag && styleObj['list-type'] === 'ul') { - options.bullet = { indent: 20 * 0.75 } + options.bullet = { indent: 20 * PT_PX_RATIO } options.paraSpaceBefore = 0.1 bulletFlag = false } @@ -250,40 +253,40 @@ export default () => { } else if (point.type === 'M') { return { - x: point.x / 100 * scale.x, - y: point.y / 100 * scale.y, + x: point.x / INCH_PX_RATIO * scale.x, + y: point.y / INCH_PX_RATIO * scale.y, moveTo: true, } } else if (point.curve) { if (point.curve.type === 'cubic') { return { - x: point.x / 100 * scale.x, - y: point.y / 100 * scale.y, + x: point.x / INCH_PX_RATIO * scale.x, + y: point.y / INCH_PX_RATIO * scale.y, curve: { type: 'cubic', - x1: (point.curve.x1 as number) / 100 * scale.x, - y1: (point.curve.y1 as number) / 100 * scale.y, - x2: (point.curve.x2 as number) / 100 * scale.x, - y2: (point.curve.y2 as number) / 100 * scale.y, + x1: (point.curve.x1 as number) / INCH_PX_RATIO * scale.x, + y1: (point.curve.y1 as number) / INCH_PX_RATIO * scale.y, + x2: (point.curve.x2 as number) / INCH_PX_RATIO * scale.x, + y2: (point.curve.y2 as number) / INCH_PX_RATIO * scale.y, }, } } else if (point.curve.type === 'quadratic') { return { - x: point.x / 100 * scale.x, - y: point.y / 100 * scale.y, + x: point.x / INCH_PX_RATIO * scale.x, + y: point.y / INCH_PX_RATIO * scale.y, curve: { type: 'quadratic', - x1: (point.curve.x1 as number) / 100 * scale.x, - y1: (point.curve.y1 as number) / 100 * scale.y, + x1: (point.curve.x1 as number) / INCH_PX_RATIO * scale.x, + y1: (point.curve.y1 as number) / INCH_PX_RATIO * scale.y, }, } } } return { - x: point.x / 100 * scale.x, - y: point.y / 100 * scale.y, + x: point.x / INCH_PX_RATIO * scale.x, + y: point.y / INCH_PX_RATIO * scale.y, } }) } @@ -341,7 +344,7 @@ export default () => { type: 'outer', color: c.color.replace('#', ''), opacity: c.alpha, - blur: shadow.blur * 0.75, + blur: shadow.blur * PT_PX_RATIO, offset, angle, } @@ -353,7 +356,7 @@ export default () => { return { color: c.color, transparency: (1 - c.alpha) * 100, - width: (outline.width || 1) * 0.75, + width: (outline.width || 1) * PT_PX_RATIO, dashType: outline.style === 'solid' ? 'solid' : 'dash', } } @@ -415,21 +418,21 @@ export default () => { const textProps = formatHTML(el.content) const options: pptxgen.TextPropsOptions = { - x: el.left / 100, - y: el.top / 100, - w: el.width / 100, - h: el.height / 100, - fontSize: 20 * 0.75, + x: el.left / INCH_PX_RATIO, + y: el.top / INCH_PX_RATIO, + w: el.width / INCH_PX_RATIO, + h: el.height / INCH_PX_RATIO, + fontSize: 20 * PT_PX_RATIO, fontFace: '微软雅黑', color: '#000000', valign: 'top', - margin: 10 * 0.75, - paraSpaceBefore: 5 * 0.75, + margin: 10 * PT_PX_RATIO, + paraSpaceBefore: 5 * PT_PX_RATIO, lineSpacingMultiple: 1.5 / 1.25, autoFit: true, } if (el.rotate) options.rotate = el.rotate - if (el.wordSpace) options.charSpacing = el.wordSpace * 0.75 + if (el.wordSpace) options.charSpacing = el.wordSpace * PT_PX_RATIO if (el.lineHeight) options.lineSpacingMultiple = el.lineHeight / 1.25 if (el.fill) { const c = formatColor(el.fill) @@ -441,7 +444,7 @@ export default () => { if (el.shadow) options.shadow = getShadowOption(el.shadow) if (el.outline?.width) options.line = getOutlineOption(el.outline) if (el.opacity !== undefined) options.transparency = (1 - el.opacity) * 100 - if (el.paragraphSpace !== undefined) options.paraSpaceBefore = el.paragraphSpace * 0.75 + if (el.paragraphSpace !== undefined) options.paraSpaceBefore = el.paragraphSpace * PT_PX_RATIO if (el.vertical) options.vert = 'eaVert' pptxSlide.addText(textProps, options) @@ -450,10 +453,10 @@ export default () => { else if (el.type === 'image') { const options: pptxgen.ImageProps = { path: el.src, - x: el.left / 100, - y: el.top / 100, - w: el.width / 100, - h: el.height / 100, + x: el.left / INCH_PX_RATIO, + y: el.top / INCH_PX_RATIO, + w: el.width / INCH_PX_RATIO, + h: el.height / INCH_PX_RATIO, } if (el.flipH) options.flipH = el.flipH if (el.flipV) options.flipV = el.flipV @@ -470,18 +473,18 @@ export default () => { const [startX, startY] = start const [endX, endY] = end - const originW = el.width / ((endX - startX) / 100) - const originH = el.height / ((endY - startY) / 100) + const originW = el.width / ((endX - startX) / INCH_PX_RATIO) + const originH = el.height / ((endY - startY) / INCH_PX_RATIO) - options.w = originW / 100 - options.h = originH / 100 + options.w = originW / INCH_PX_RATIO + options.h = originH / INCH_PX_RATIO options.sizing = { type: 'crop', - x: startX / 100 * originW / 100, - y: startY / 100 * originH / 100, - w: (endX - startX) / 100 * originW / 100, - h: (endY - startY) / 100 * originH / 100, + x: startX / INCH_PX_RATIO * originW / INCH_PX_RATIO, + y: startY / INCH_PX_RATIO * originH / INCH_PX_RATIO, + w: (endX - startX) / INCH_PX_RATIO * originW / INCH_PX_RATIO, + h: (endY - startY) / INCH_PX_RATIO * originH / INCH_PX_RATIO, } } @@ -495,10 +498,10 @@ export default () => { const options: pptxgen.ImageProps = { data: base64SVG, - x: el.left / 100, - y: el.top / 100, - w: el.width / 100, - h: el.height / 100, + x: el.left / INCH_PX_RATIO, + y: el.top / INCH_PX_RATIO, + w: el.width / INCH_PX_RATIO, + h: el.height / INCH_PX_RATIO, } if (el.rotate) options.rotate = el.rotate if (el.link) { @@ -519,10 +522,10 @@ export default () => { const opacity = el.opacity === undefined ? 1 : el.opacity const options: pptxgen.ShapeProps = { - x: el.left / 100, - y: el.top / 100, - w: el.width / 100, - h: el.height / 100, + x: el.left / INCH_PX_RATIO, + y: el.top / INCH_PX_RATIO, + w: el.width / INCH_PX_RATIO, + h: el.height / INCH_PX_RATIO, fill: { color: fillColor.color, transparency: (1 - fillColor.alpha * opacity) * 100 }, points, } @@ -541,14 +544,14 @@ export default () => { const textProps = formatHTML(el.text.content) const options: pptxgen.TextPropsOptions = { - x: el.left / 100, - y: el.top / 100, - w: el.width / 100, - h: el.height / 100, - fontSize: 20 * 0.75, + x: el.left / INCH_PX_RATIO, + y: el.top / INCH_PX_RATIO, + w: el.width / INCH_PX_RATIO, + h: el.height / INCH_PX_RATIO, + fontSize: 20 * PT_PX_RATIO, fontFace: '微软雅黑', color: '#000000', - paraSpaceBefore: 5 * 0.75, + paraSpaceBefore: 5 * PT_PX_RATIO, valign: el.text.align, } if (el.rotate) options.rotate = el.rotate @@ -566,14 +569,14 @@ export default () => { const c = formatColor(el.color) const options: pptxgen.ShapeProps = { - x: el.left / 100, - y: el.top / 100, - w: (maxX - minX) / 100, - h: (maxY - minY) / 100, + x: el.left / INCH_PX_RATIO, + y: el.top / INCH_PX_RATIO, + w: (maxX - minX) / INCH_PX_RATIO, + h: (maxY - minY) / INCH_PX_RATIO, line: { color: c.color, transparency: (1 - c.alpha) * 100, - width: el.width * 0.75, + width: el.width * PT_PX_RATIO, dashType: el.style === 'solid' ? 'solid' : 'dash', beginArrowType: el.points[0] ? 'arrow' : 'none', endArrowType: el.points[1] ? 'arrow' : 'none', @@ -606,19 +609,19 @@ export default () => { } const options: pptxgen.IChartOpts = { - x: el.left / 100, - y: el.top / 100, - w: el.width / 100, - h: el.height / 100, + x: el.left / INCH_PX_RATIO, + y: el.top / INCH_PX_RATIO, + w: el.width / INCH_PX_RATIO, + h: el.height / INCH_PX_RATIO, chartColors: el.chartType === 'pie' ? chartColors : chartColors.slice(0, el.data.series.length), } - if (el.fill) options.fill = formatColor(el.fill).color + if (el.fill) options.plotArea = { fill: { color: formatColor(el.fill).color } } if (el.legend) { options.showLegend = true options.legendPos = el.legend === 'top' ? 't' : 'b' options.legendColor = formatColor(el.gridColor || '#000000').color - options.legendFontSize = 14 * 0.75 + options.legendFontSize = 14 * PT_PX_RATIO } let type = pptx.ChartType.bar @@ -689,7 +692,7 @@ export default () => { align: cell.style?.align || 'left', valign: 'middle', fontFace: cell.style?.fontname || '微软雅黑', - fontSize: (cell.style?.fontsize ? parseInt(cell.style?.fontsize) : 14) * 0.75, + fontSize: (cell.style?.fontsize ? parseInt(cell.style?.fontsize) : 14) * PT_PX_RATIO, } if (theme && themeColor) { let c: FormatColor @@ -720,17 +723,17 @@ export default () => { } const options: pptxgen.TableProps = { - x: el.left / 100, - y: el.top / 100, - w: el.width / 100, - h: el.height / 100, - colW: el.colWidths.map(item => el.width * item / 100), + x: el.left / INCH_PX_RATIO, + y: el.top / INCH_PX_RATIO, + w: el.width / INCH_PX_RATIO, + h: el.height / INCH_PX_RATIO, + colW: el.colWidths.map(item => el.width * item / INCH_PX_RATIO), } if (el.theme) options.fill = { color: '#ffffff' } if (el.outline.width && el.outline.color) { options.border = { type: el.outline.style === 'solid' ? 'solid' : 'dash', - pt: el.outline.width * 0.75, + pt: el.outline.width * PT_PX_RATIO, color: formatColor(el.outline.color).color, } } @@ -744,10 +747,10 @@ export default () => { const options: pptxgen.ImageProps = { data: base64SVG, - x: el.left / 100, - y: el.top / 100, - w: el.width / 100, - h: el.height / 100, + x: el.left / INCH_PX_RATIO, + y: el.top / INCH_PX_RATIO, + w: el.width / INCH_PX_RATIO, + h: el.height / INCH_PX_RATIO, } if (el.link) { const linkOption = getLinkOption(el.link) @@ -758,10 +761,13 @@ export default () => { } } } - pptx.writeFile({ fileName: `pptist.pptx` }).then(() => exporting.value = false).catch(() => { - exporting.value = false - message.error('导出失败') - }) + + setTimeout(() => { + pptx.writeFile({ fileName: `pptist.pptx` }).then(() => exporting.value = false).catch(() => { + exporting.value = false + message.error('导出失败') + }) + }, 200) } return {