From c7d9bb087cbe6cc2644645783e3ab642c74b33b7 Mon Sep 17 00:00:00 2001 From: lyc Date: Fri, 17 Jan 2025 09:57:35 +0800 Subject: [PATCH 01/10] edit --- src/renderer/src/router/index.js | 6 +++ src/renderer/src/views/mindMap/index.vue | 51 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/renderer/src/views/mindMap/index.vue diff --git a/src/renderer/src/router/index.js b/src/renderer/src/router/index.js index 55d8f3d..c696c2b 100644 --- a/src/renderer/src/router/index.js +++ b/src/renderer/src/router/index.js @@ -102,6 +102,12 @@ export const constantRoutes = [ name: 'aiVoice', meta: { title: '语音生成', showBread: true } }, + { + path: 'mindmap', + component: () => import('@/views/mindMap/index.vue'), + name: 'mindmap', + meta: { title: 'AI思维导图' } + }, ] }, diff --git a/src/renderer/src/views/mindMap/index.vue b/src/renderer/src/views/mindMap/index.vue new file mode 100644 index 0000000..97f4bbd --- /dev/null +++ b/src/renderer/src/views/mindMap/index.vue @@ -0,0 +1,51 @@ + + + + + \ No newline at end of file From 57af105c9028eda23082c9ec8737c477d9862290 Mon Sep 17 00:00:00 2001 From: lyc Date: Mon, 20 Jan 2025 09:49:02 +0800 Subject: [PATCH 02/10] edit --- src/renderer/src/api/ai/index.js | 4 +- src/renderer/src/api/mode/index.js | 14 ++ src/renderer/src/main.js | 11 +- src/renderer/src/views/mindMap/index.vue | 164 +++++++++++++++--- src/renderer/src/views/model/index.vue | 1 + .../views/teachingDesign/container/center.vue | 4 +- 6 files changed, 164 insertions(+), 34 deletions(-) diff --git a/src/renderer/src/api/ai/index.js b/src/renderer/src/api/ai/index.js index 5e491d3..13e0ff5 100644 --- a/src/renderer/src/api/ai/index.js +++ b/src/renderer/src/api/ai/index.js @@ -1,12 +1,10 @@ import request from '@/utils/request' // 创建对话 -export const createChart = ({ headers, data }) => { +export const createChart = () => { return request({ url: '/qf/createChart', method: 'post', - headers, - data, }) } // 大模型对话 diff --git a/src/renderer/src/api/mode/index.js b/src/renderer/src/api/mode/index.js index 40c4ce5..a0e7b9d 100644 --- a/src/renderer/src/api/mode/index.js +++ b/src/renderer/src/api/mode/index.js @@ -29,6 +29,20 @@ export function completion(data) { }) } +// 大模型对话 +export function modelChat(data) { + return axios({ + url: '/mind/chat', + method: 'post', + headers: { + Authorization: 'Bearer ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm', + 'Content-Type': 'application/json', + Accept: '*/*' + }, + data: data + }) +} + // 添加提示词 (系统预设) export function addKeyWords(data) { return request({ diff --git a/src/renderer/src/main.js b/src/renderer/src/main.js index 16f16ec..49da22f 100644 --- a/src/renderer/src/main.js +++ b/src/renderer/src/main.js @@ -11,10 +11,9 @@ import 'virtual:windi.css' import request from "@/utils/request"; //v-md-editor -import VMdPreview from '@kangc/v-md-editor/lib/preview'; -import '@kangc/v-md-editor/lib/style/preview.css'; -// 引入你所使用的主题 此处以 github 主题为例 -import githubTheme from '@kangc/v-md-editor/lib/theme/github'; +import VMdEditor from '@kangc/v-md-editor'; +import '@kangc/v-md-editor/lib/style/base-editor.css'; +import githubTheme from '@kangc/v-md-editor/lib/theme/github.js'; import '@kangc/v-md-editor/lib/theme/style/github.css'; // highlightjs import hljs from 'highlight.js'; @@ -51,7 +50,7 @@ app.config.globalProperties.$requestGetJYW = (url,config)=>{ import Icon from '@/AixPPTist/src/plugins/icon' import Directive from '@/AixPPTist/src/plugins/directive' -VMdPreview.use(githubTheme, { +VMdEditor.use(githubTheme, { Hljs: hljs, }); @@ -63,7 +62,7 @@ app.use(router) .use(Icon) .use(Directive) .use(aiAudio) - .use(VMdPreview) + .use(VMdEditor) .mount('#app') const isStadium = (user) => { diff --git a/src/renderer/src/views/mindMap/index.vue b/src/renderer/src/views/mindMap/index.vue index 97f4bbd..d792f0e 100644 --- a/src/renderer/src/views/mindMap/index.vue +++ b/src/renderer/src/views/mindMap/index.vue @@ -1,17 +1,35 @@ \ No newline at end of file +} + diff --git a/src/renderer/src/views/model/index.vue b/src/renderer/src/views/model/index.vue index 41de465..eadeb32 100644 --- a/src/renderer/src/views/model/index.vue +++ b/src/renderer/src/views/model/index.vue @@ -68,6 +68,7 @@
{{item.name}}
+ AI思维导图 diff --git a/src/renderer/src/views/teachingDesign/container/center.vue b/src/renderer/src/views/teachingDesign/container/center.vue index f3efd8b..c9f8166 100644 --- a/src/renderer/src/views/teachingDesign/container/center.vue +++ b/src/renderer/src/views/teachingDesign/container/center.vue @@ -17,8 +17,8 @@
- - + +
From 4af2b5fc3acf18847b8a4745840d0ca034df783b Mon Sep 17 00:00:00 2001 From: zdg Date: Tue, 21 Jan 2025 12:24:24 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E4=BC=98=E5=8C=96pptx=E8=AF=86=E5=88=AB?= =?UTF-8?q?=20=E6=AF=8D=E7=89=88=E5=8A=A0=E8=BD=BD=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/AixPPTist/src/hooks/useImport.ts | 562 +- .../src/AixPPTist/src/types/slides.ts | 2 +- .../ElementStylePanel/ShapeStylePanel.vue | 10 +- .../element/ShapeElement/BaseShapeElement.vue | 2 + .../element/ShapeElement/GradientDefs.vue | 48 +- .../components/element/ShapeElement/index.vue | 2 + src/renderer/src/plugins/pptTojson/index.d.ts | 358 + src/renderer/src/plugins/pptTojson/index.js | 25494 ++++++++++++++++ src/renderer/src/utils/ppt/svgUtils.js | 138 + src/renderer/src/views/model/index.vue | 83 +- 10 files changed, 26237 insertions(+), 462 deletions(-) create mode 100644 src/renderer/src/plugins/pptTojson/index.d.ts create mode 100644 src/renderer/src/plugins/pptTojson/index.js create mode 100644 src/renderer/src/utils/ppt/svgUtils.js diff --git a/src/renderer/src/AixPPTist/src/hooks/useImport.ts b/src/renderer/src/AixPPTist/src/hooks/useImport.ts index b990686..5aa3c32 100644 --- a/src/renderer/src/AixPPTist/src/hooks/useImport.ts +++ b/src/renderer/src/AixPPTist/src/hooks/useImport.ts @@ -1,6 +1,8 @@ import { ref } from 'vue' import { storeToRefs } from 'pinia' -import { parse, type Shape, type Element, type ChartItem } from 'pptxtojson' +// import { parse, type Shape, type Element, type ChartItem } from 'pptxtojson' +// import { parse, utils, fill as fillUtil } from '@/plugins/pptTojson' +import { parse, fill as fillUtil, type Shape, type Element, type ChartItem } from '@/plugins/pptTojson' import { nanoid } from 'nanoid' import { useSlidesStore } from '../store' import { decrypt } from '../utils/crypto' @@ -9,6 +11,7 @@ import useAddSlidesOrElements from '../hooks/useAddSlidesOrElements' import useSlideHandler from '../hooks/useSlideHandler' import message from '../utils/message' import { getSvgPathRange } from '../utils/svgPathParser' +import { calculatePathDimensions } from '@/utils/ppt/svgUtils' import type { Slide, TableCellStyle, @@ -75,57 +78,18 @@ const parseLineElement = (el: Shape) => { return data } -export default () => { - const exporting = ref(false) - - // 导入pptist文件 - const importSpecificFile = (files: FileList, cover = false) => { - const file = files[0] - - const reader = new FileReader() - reader.addEventListener('load', () => { - try { - const slides = JSON.parse(decrypt(reader.result as string)) - if (cover) { - slidesStore.updateSlideIndex(0) - slidesStore.setSlides(slides) - } - else if (isEmptySlide.value) slidesStore.setSlides(slides) - else addSlidesFromData(slides) +// PPT json二次加工处理 +const parsePptJsonSlides = (slides: Slide[]|any, zip) => { + return new Promise(async (resolve, reject) => { + try { + const shapeList: ShapePoolItem[] = [] + for (const item of SHAPE_LIST) { + shapeList.push(...item.children) } - catch { - message.error('无法正确读取 / 解析该文件') - } - }) - reader.readAsText(file) - } - - - - // 导入PPTX文件 - const importPPTXFile = (files: FileList) => { - const file = files[0] - if (!file) return - - exporting.value = true - - const shapeList: ShapePoolItem[] = [] - for (const item of SHAPE_LIST) { - shapeList.push(...item.children) - } - - const reader = new FileReader() - reader.onload = async e => { - const json = await parse(e.target!.result as ArrayBuffer) - const ratio = 96 / 72 - const width = json.size.width - - slidesStore.setViewportSize(width * ratio) - - const slides: Slide[] = [] - for (const item of json.slides) { + const newSlides: Slide[] = [] + for (const item of slides) { const { type, value } = item.fill let background: SlideBackground if (type === 'image') { @@ -156,20 +120,20 @@ export default () => { color: value, } } - + const slide: Slide = { id: nanoid(10), elements: [], background, } - - const parseElements = (elements: Element[]) => { + + const parseElements = async (elements: Element[]) => { for (const el of elements) { const originWidth = el.width || 1 const originHeight = el.height || 1 const originLeft = el.left const originTop = el.top - + el.width = el.width * ratio el.height = el.height * ratio el.left = el.left * ratio @@ -219,6 +183,7 @@ export default () => { rotate: el.rotate, flipH: el.isFlipH, flipV: el.isFlipV, + zipPath: el.zipPath, }) } else if (el.type === 'audio') { @@ -235,6 +200,7 @@ export default () => { color: theme.value.themeColor, loop: false, autoplay: false, + zipPath: el.zipPath, }) } else if (el.type === 'video') { @@ -248,6 +214,7 @@ export default () => { top: el.top, rotate: 0, autoplay: false, + zipPath: el.zipPath, }) } else if (el.type === 'shape') { @@ -257,7 +224,7 @@ export default () => { } else { const shape = shapeList.find(item => item.pptxShapeType === el.shapType) - + const vAlignMap: { [key: string]: ShapeTextAlign } = { 'mid': 'middle', 'down': 'bottom', @@ -325,6 +292,34 @@ export default () => { element.viewBox = [maxX || originWidth, maxY || originHeight] } } + // auth: zdg + if (el.fill) { + const { type, ...opt } = el.fill + if (type === 'gradient') { // 线性渐变色 + element.gradient = { + type: 'linear', + colors: opt.colors.map(item => ({ + ...item, + pos: parseInt(item.pos), + })), + rotate: opt.rot, + } + } else if (type == 'image') { // 背景图填充 + const pathPos = calculatePathDimensions(element.path) + const url = opt.picBase64 || (!!zip ? await fillUtil.getPicFillBase64(opt.zipPath, zip) : '') + element.gradient = { + type: 'image', + image: { + src: url, + width: opt.w||el.width, + height: opt.h||el.height, + path_W: Math.round(pathPos.width), // 获取path 的宽高 + path_h: Math.round(pathPos.height), // 获取path 的宽高 + ...opt + } + } + } + } if (element.path) slide.elements.push(element) } @@ -342,17 +337,17 @@ export default () => { const rowCells: TableCell[] = [] for (let j = 0; j < col; j++) { const cellData = el.data[i][j] - + let textDiv: HTMLDivElement | null = document.createElement('div') textDiv.innerHTML = cellData.text const p = textDiv.querySelector('p') const align = p?.style.textAlign || 'left' - + const span = textDiv.querySelector('span') const fontsize = span?.style.fontSize ? (parseInt(span?.style.fontSize) * ratio).toFixed(1) + 'px' : '' const fontname = span?.style.fontFamily || '' const color = span?.style.color || cellData.fontColor - + rowCells.push({ id: nanoid(10), colspan: cellData.colSpan || 1, @@ -409,11 +404,11 @@ export default () => { legends = data.map(item => item.key) series = data.map(item => item.values.map(v => v.y)) } - + const options: ChartOptions = {} let chartType: ChartType = 'bar' - + switch (el.chartType) { case 'barChart': case 'bar3DChart': @@ -468,18 +463,91 @@ export default () => { }) } else if (el.type === 'group' || el.type === 'diagram') { - const elements = el.elements.map(_el => ({ - ..._el, - left: _el.left + originLeft, - top: _el.top + originTop, - })) - parseElements(elements) + const elements = el.elements.map(_el => { + const isGroup = el.type === 'group' // 子级是否为分组 + const isFlipH = !!(_el.isFlipH ^ el.isFlipH) // 水平翻转(分组有值进行异或运算) + const isFlipV = !!(_el.isFlipV ^ el.isFlipV) // 垂直翻转(分组有值进行异或运算) + const isPleft = el.isFlipH_def // 是否父级翻转-改变子元素坐标left + const isPtop = el.isFlipV_def // 是否父级翻转-改变子元素坐标top + const left = originLeft + (isPleft ? originWidth - _el.left - _el.width : _el.left) + const top = originTop + (isPtop ? originHeight - _el.top - _el.height : _el.top) + return { + ..._el, + left, + top, + isFlipH, + isFlipV, + isFlipH_def: _el.isFlipH, // 保留默认 + isFlipV_def: _el.isFlipV, // 保留默认 + } + }) + await parseElements(elements) } } } - parseElements(item.elements) - slides.push(slide) + // 设置默认翻转默认属性 + item.elements.forEach(o => { o.isFlipH_def = o.isFlipH; o.isFlipV_def = o.isFlipV }) + item.bgElements.forEach(o => { o.isFlipH_def = o.isFlipH; o.isFlipV_def = o.isFlipV }) + item.bgElements.length && await parseElements(item.bgElements) // 加载当前幻灯片对应的母版 + item.elements.length && await parseElements(item.elements) // 加载当前幻灯片 + newSlides.push(slide) } + resolve(newSlides) + } catch (error) { + reject(error) + } + }) +} + +export default () => { + + const exporting = ref(false) + + // 导入pptist文件 + const importSpecificFile = (files: FileList, cover = false) => { + const file = files[0] + + const reader = new FileReader() + reader.addEventListener('load', () => { + try { + const slides = JSON.parse(decrypt(reader.result as string)) + if (cover) { + slidesStore.updateSlideIndex(0) + slidesStore.setSlides(slides) + } + else if (isEmptySlide.value) slidesStore.setSlides(slides) + else addSlidesFromData(slides) + } + catch { + message.error('无法正确读取 / 解析该文件') + } + }) + reader.readAsText(file) + } + + + + // 导入PPTX文件 + const importPPTXFile = (files: FileList) => { + const file = files[0] + if (!file) return + + exporting.value = true + + const shapeList: ShapePoolItem[] = [] + for (const item of SHAPE_LIST) { + shapeList.push(...item.children) + } + + const reader = new FileReader() + reader.onload = async e => { + const json = await parse(e.target!.result as ArrayBuffer) + + const ratio = 96 / 72 + const width = json.size.width + slidesStore.setViewportSize(width * ratio) + // json数据二次加工 + const slides = await parsePptJsonSlides(json.slides, json.zip) slidesStore.updateSlideIndex(0) slidesStore.setSlides(slides) exporting.value = false @@ -537,365 +605,7 @@ export const PPTXFileToJson = (data: File|ArrayBuffer) => { resData.def = json // 保留原始数据 resData.width = width * ratio resData.ratio = slidesStore.viewportRatio - - const slides: Slide[] = [] - for (const item of json.slides) { - const { type, value } = item.fill - let background: SlideBackground - if (type === 'image') { - background = { - type: 'image', - image: { - src: value.picBase64, - size: 'cover', - }, - } - } - else if (type === 'gradient') { - background = { - type: 'gradient', - gradient: { - type: 'linear', - colors: value.colors.map(item => ({ - ...item, - pos: parseInt(item.pos), - })), - rotate: value.rot, - }, - } - } - else { - background = { - type: 'solid', - color: value, - } - } - - const slide: Slide = { - id: nanoid(10), - elements: [], - background, - } - - const parseElements = (elements: Element[]) => { - for (const el of elements) { - const originWidth = el.width || 1 - const originHeight = el.height || 1 - const originLeft = el.left - const originTop = el.top - - el.width = el.width * ratio - el.height = el.height * ratio - el.left = el.left * ratio - el.top = el.top * ratio - - if (el.type === 'text') { - const textEl: PPTTextElement = { - type: 'text', - id: nanoid(10), - width: el.width, - height: el.height, - left: el.left, - top: el.top, - rotate: el.rotate, - defaultFontName: theme.value.fontName, - defaultColor: theme.value.fontColor, - content: convertFontSizePtToPx(el.content, ratio), - lineHeight: 1, - outline: { - color: el.borderColor, - width: el.borderWidth, - style: el.borderType, - }, - fill: el.fillColor, - vertical: el.isVertical, - } - if (el.shadow) { - textEl.shadow = { - h: el.shadow.h * ratio, - v: el.shadow.v * ratio, - blur: el.shadow.blur * ratio, - color: el.shadow.color, - } - } - slide.elements.push(textEl) - } - else if (el.type === 'image') { - slide.elements.push({ - type: 'image', - id: nanoid(10), - src: el.src, - width: el.width, - height: el.height, - left: el.left, - top: el.top, - fixedRatio: true, - rotate: el.rotate, - flipH: el.isFlipH, - flipV: el.isFlipV, - }) - } - else if (el.type === 'audio') { - slide.elements.push({ - type: 'audio', - id: nanoid(10), - src: el.blob, - width: el.width, - height: el.height, - left: el.left, - top: el.top, - rotate: 0, - fixedRatio: false, - color: theme.value.themeColor, - loop: false, - autoplay: false, - }) - } - else if (el.type === 'video') { - slide.elements.push({ - type: 'video', - id: nanoid(10), - src: (el.blob || el.src)!, - width: el.width, - height: el.height, - left: el.left, - top: el.top, - rotate: 0, - autoplay: false, - }) - } - else if (el.type === 'shape') { - if (el.shapType === 'line' || /Connector/.test(el.shapType)) { - // 从返回对象中解构出 xx 函数并调用 - const lineElement = parseLineElement(el) - slide.elements.push(lineElement) - } - else { - const shape = shapeList.find(item => item.pptxShapeType === el.shapType) - - const vAlignMap: { [key: string]: ShapeTextAlign } = { - 'mid': 'middle', - 'down': 'bottom', - 'up': 'top', - } - - const element: PPTShapeElement = { - type: 'shape', - id: nanoid(10), - width: el.width, - height: el.height, - left: el.left, - top: el.top, - viewBox: [200, 200], - path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z', - fill: el.fillColor || 'none', - fixedRatio: false, - rotate: el.rotate, - outline: { - color: el.borderColor, - width: el.borderWidth, - style: el.borderType, - }, - text: { - content: convertFontSizePtToPx(el.content, ratio), - defaultFontName: theme.value.fontName, - defaultColor: theme.value.fontColor, - align: vAlignMap[el.vAlign] || 'middle', - }, - flipH: el.isFlipH, - flipV: el.isFlipV, - } - if (el.shadow) { - element.shadow = { - h: el.shadow.h * ratio, - v: el.shadow.v * ratio, - blur: el.shadow.blur * ratio, - color: el.shadow.color, - } - } - - if (shape) { - element.path = shape.path - element.viewBox = shape.viewBox - - if (shape.pathFormula) { - element.pathFormula = shape.pathFormula - element.viewBox = [el.width, el.height] - - const pathFormula = SHAPE_PATH_FORMULAS[shape.pathFormula] - if ('editable' in pathFormula && pathFormula.editable) { - element.path = pathFormula.formula(el.width, el.height, pathFormula.defaultValue) - element.keypoints = pathFormula.defaultValue - } - else element.path = pathFormula.formula(el.width, el.height) - } - } - if (el.shapType === 'custom') { - if (el.path!.indexOf('NaN') !== -1) element.path = '' - else { - element.special = true - element.path = el.path! - - const { maxX, maxY } = getSvgPathRange(element.path) - element.viewBox = [maxX || originWidth, maxY || originHeight] - } - } - - if (element.path) slide.elements.push(element) - } - } - else if (el.type === 'table') { - const row = el.data.length - const col = el.data[0].length - - const style: TableCellStyle = { - fontname: theme.value.fontName, - color: theme.value.fontColor, - } - const data: TableCell[][] = [] - for (let i = 0; i < row; i++) { - const rowCells: TableCell[] = [] - for (let j = 0; j < col; j++) { - const cellData = el.data[i][j] - - let textDiv: HTMLDivElement | null = document.createElement('div') - textDiv.innerHTML = cellData.text - const p = textDiv.querySelector('p') - const align = p?.style.textAlign || 'left' - - const span = textDiv.querySelector('span') - const fontsize = span?.style.fontSize ? (parseInt(span?.style.fontSize) * ratio).toFixed(1) + 'px' : '' - const fontname = span?.style.fontFamily || '' - const color = span?.style.color || cellData.fontColor - - rowCells.push({ - id: nanoid(10), - colspan: cellData.colSpan || 1, - rowspan: cellData.rowSpan || 1, - text: textDiv.innerText, - style: { - ...style, - align: ['left', 'right', 'center'].includes(align) ? (align as 'left' | 'right' | 'center') : 'left', - fontsize, - fontname, - color, - bold: cellData.fontBold, - backcolor: cellData.fillColor, - }, - }) - textDiv = null - } - data.push(rowCells) - } - - const colWidths: number[] = new Array(col).fill(1 / col) - - slide.elements.push({ - type: 'table', - id: nanoid(10), - width: el.width, - height: el.height, - left: el.left, - top: el.top, - colWidths, - rotate: 0, - data, - outline: { - width: el.borderWidth || 2, - style: el.borderType, - color: el.borderColor || '#eeece1', - }, - cellMinHeight: 36, - }) - } - else if (el.type === 'chart') { - let labels: string[] - let legends: string[] - let series: number[][] - - if (el.chartType === 'scatterChart' || el.chartType === 'bubbleChart') { - labels = el.data[0].map((item, index) => `坐标${index + 1}`) - legends = ['X', 'Y'] - series = el.data - } - else { - const data = el.data as ChartItem[] - labels = Object.values(data[0].xlabels) - legends = data.map(item => item.key) - series = data.map(item => item.values.map(v => v.y)) - } - - const options: ChartOptions = {} - - let chartType: ChartType = 'bar' - - switch (el.chartType) { - case 'barChart': - case 'bar3DChart': - chartType = 'bar' - if (el.barDir === 'bar') chartType = 'column' - if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true - break - case 'lineChart': - case 'line3DChart': - if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true - chartType = 'line' - break - case 'areaChart': - case 'area3DChart': - if (el.grouping === 'stacked' || el.grouping === 'percentStacked') options.stack = true - chartType = 'area' - break - case 'scatterChart': - case 'bubbleChart': - chartType = 'scatter' - break - case 'pieChart': - case 'pie3DChart': - chartType = 'pie' - break - case 'radarChart': - chartType = 'radar' - break - case 'doughnutChart': - chartType = 'ring' - break - default: - } - - slide.elements.push({ - type: 'chart', - id: nanoid(10), - chartType: chartType, - width: el.width, - height: el.height, - left: el.left, - top: el.top, - rotate: 0, - themeColors: [theme.value.themeColor], - textColor: theme.value.fontColor, - data: { - labels, - legends, - series, - }, - options, - }) - } - else if (el.type === 'group' || el.type === 'diagram') { - const elements = el.elements.map(_el => ({ - ..._el, - left: _el.left + originLeft, - top: _el.top + originTop, - })) - parseElements(elements) - } - } - } - parseElements(item.elements) - slides.push(slide) - } - resData.slides = slides + resData.slides = await parsePptJsonSlides(json.slides, json.zip) resolve(resData) }) } \ No newline at end of file diff --git a/src/renderer/src/AixPPTist/src/types/slides.ts b/src/renderer/src/AixPPTist/src/types/slides.ts index b936499..bf2151c 100644 --- a/src/renderer/src/AixPPTist/src/types/slides.ts +++ b/src/renderer/src/AixPPTist/src/types/slides.ts @@ -41,7 +41,7 @@ export const enum ElementTypes { * * rotate: 渐变角度(线性渐变) */ -export type GradientType = 'linear' | 'radial' +export type GradientType = 'linear' | 'radial' | 'image' export type GradientColor = { pos: number color: string diff --git a/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/ShapeStylePanel.vue b/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/ShapeStylePanel.vue index 7b83762..de5f5ec 100644 --- a/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/ShapeStylePanel.vue +++ b/src/renderer/src/AixPPTist/src/views/Editor/Toolbar/ElementStylePanel/ShapeStylePanel.vue @@ -46,11 +46,12 @@ :options="[ { label: '线性渐变', value: 'linear' }, { label: '径向渐变', value: 'radial' }, + { label: '背景图', value: 'image' }, ]" /> - @@ -114,8 +114,8 @@ const onBack = () =>{ margin-top: -3px; margin-right: 3px; } - + } - + } diff --git a/src/renderer/src/views/mindMap/index.vue b/src/renderer/src/views/mindMap/index.vue index d792f0e..098bfb0 100644 --- a/src/renderer/src/views/mindMap/index.vue +++ b/src/renderer/src/views/mindMap/index.vue @@ -10,43 +10,79 @@ 取消 保存 - 编辑 - + + - 导出图片 +
+ 导出清晰度:数字越大越清晰,但图片大小会越大 + 导出图片 +
-
-
-
+
+
+
- + + + +
-
+
+ +
diff --git a/src/renderer/src/views/model/index.vue b/src/renderer/src/views/model/index.vue index ce62cf8..e114504 100644 --- a/src/renderer/src/views/model/index.vue +++ b/src/renderer/src/views/model/index.vue @@ -68,7 +68,6 @@
{{item.name}}
- AI思维导图 @@ -162,6 +161,10 @@ const tools = reactive([{ name: '文生图片', path: '/model/aiKolors', img: 'aiimg' +},{ + name: 'AI思维导图', + path: '/model/mindmap', + img: 'aiswdt' },{ name: '文生连环画', path: '', From 8d8b8d223c5a18954e5968b08cefd35b3e694e6a Mon Sep 17 00:00:00 2001 From: zdg Date: Tue, 21 Jan 2025 14:15:15 +0800 Subject: [PATCH 08/10] =?UTF-8?q?ppt=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/src/views/prepare/index.vue | 93 +++++++++++++++--------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/src/renderer/src/views/prepare/index.vue b/src/renderer/src/views/prepare/index.vue index 6d69db6..8a9d5c6 100644 --- a/src/renderer/src/views/prepare/index.vue +++ b/src/renderer/src/views/prepare/index.vue @@ -289,7 +289,8 @@ export default { { color: '#5cb87a', percentage: 100 }, // 绿色 ] } - } + }, + pptMedia: {} // ppt媒体数据 } }, computed: { @@ -591,52 +592,76 @@ export default { this.createAIPPTByFile(file, this.currentNode.itemtitle + '.aippt') } }, + // 将图片|音频|视频 转换为线上地址 + getOnlineFileUrl(data, name){ + return new Promise(async (resolve, reject) => { + let file + if (data instanceof Blob) { // blob类型判断 + const fileName = Date.now() + `.${name||'png'}` + file = commUtils.blobToFile(data, fileName) + } else if (data instanceof File) { // file类型判断 + file = data + } else { // 其他类型 base64 + const blob = commUtils.base64ToBlob(data) + const fileName = Date.now() + `.${name||'png'}` + file = commUtils.blobToFile(blob, fileName) + } + const formData = new FormData() + formData.append('file', file) + const res = await Api_server.Other.uploadFile(formData) + if (res && res.code == 200){ + resolve(res?.url) + } else { // 失败 + reject(res?.msg||'上传失败') + } + }) + }, async toRousrceUrl(o) { if (!!o.src) { // 如果有src就转换 const isBase64 = /^data:image\/(\w+);base64,/.test(o.src) const isBlobUrl = /^blob:/.test(o.src) - // console.log('isBase64', o, isBase64) - if (isBase64) { - const bolb = commUtils.base64ToBlob(o.src) - const fileName = Date.now() + '.png' - const file = commUtils.blobToFile(bolb, fileName) - // o.src = fileName - // console.log('file', file) - const formData = new FormData() - formData.append('file', file) - const res = await Api_server.Other.uploadFile(formData) - if (res && res.code == 200){ - const url = res?.url - url &&(o.src = url) - } - } else if (isBlobUrl) { // 视频和音频 - const res = await fetch(o.src) - const blob = await res.blob() - const fileName = o.type=='video'? Date.now() + '.mp4':Date.now() + '.mp3' - const file = commUtils.blobToFile(blob, fileName) - // o.src = fileName - // console.log('file', file) - const formData = new FormData() - formData.append('file', file) - const ress = await Api_server.Other.uploadFile(formData) - if (ress && ress.code == 200){ - const url = ress?.url + let onLineUrl = '' // 线上地址 + if (!!o.zipPath) onLineUrl = this.pptMedia[o.zipPath] || '' // 是否已上传过 + if (onLineUrl) o.src = onLineUrl // 已存在线上地址直接赋值 + else { // 不存在重新上传 + if (isBase64) { // 相同资源处理 + const url = await this.getOnlineFileUrl(o.src) + url && o.zipPath && (this.pptMedia[o.zipPath] = url) // 缓存 + } else if (isBlobUrl) { // 视频和音频 + const res = await fetch(o.src) + const blob = await res.blob() + const url = await this.getOnlineFileUrl(blob, o.type=='video'?'mp4':'mp3') + URL.revokeObjectURL(o.src) // 释放内存 url &&(o.src = url) + url && o.zipPath && (this.pptMedia[o.zipPath] = url) // 缓存 } } } - + // 处理元素为shape 可能存在背景图等 + const isBg = o?.gradient?.type == 'image' && !!o?.gradient?.image + if (isBg) { + const {src, zipPath} = o.gradient.image || {} + let onLineUrl = '' // 线上地址 + if (!!zipPath) onLineUrl = this.pptMedia[zipPath] || '' // 是否已上传过 + if (onLineUrl) o.gradient.image.src = onLineUrl // 已存在线上地址直接赋值 + else { // 重新上传 + const url = await this.getOnlineFileUrl(src) + o.gradient.image.src = url + url && zipPath && (this.pptMedia[zipPath] = url) // 缓存 + } + } + if (o?.background?.image) await this.toRousrceUrl(o.background.image) - // if (o?.elements) o.elements.forEach(async o => {await this.toRousrceUrl(o)}) - if(o?.elements){ - for (let element of o.elements) { - await this.toRousrceUrl(element); - } - } + if(o?.elements){ + for (let element of o.elements) { + await this.toRousrceUrl(element); + } + } }, async createAIPPTByFile(file,fileShowName) { this.pgDialog.visible = true this.pgDialog.pg.percentage = 0 + this.pptMedia = {} // 清空媒体数据 const resPptJson = await PPTXFileToJson(file).catch(() => { ElMessageBox.alert('PPT文件转换失败!请点击素材右侧...下载文件后打开另存为PPTX文件格式再进行导入!') this.pgDialog.visible = false From 3c0e7777f8a86de117edffa0e2c4bed074cba93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=9D=A8?= <666> Date: Tue, 21 Jan 2025 14:22:56 +0800 Subject: [PATCH 09/10] =?UTF-8?q?fix=EF=BC=9A=E9=BB=98=E8=AE=A4=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E9=97=AE=E9=A2=98=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/src/layout/components/Aside.vue | 7 ++++++- src/renderer/src/views/profile/userAvatar.vue | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/layout/components/Aside.vue b/src/renderer/src/layout/components/Aside.vue index d87208d..ecc7566 100644 --- a/src/renderer/src/layout/components/Aside.vue +++ b/src/renderer/src/layout/components/Aside.vue @@ -5,7 +5,11 @@ @@ -65,6 +69,7 @@ import {toLinkLeftWeb} from "@/utils/tool" const { ipcRenderer } = window.electron || {} const dev_api = ref(import.meta.env.VITE_APP_BASE_API) +const route_path = ref(import.meta.env.VITE_APP_BUILD_BASE_PATH) const userStore = useUserStore() const router = useRouter() const currentRoute = ref('') diff --git a/src/renderer/src/views/profile/userAvatar.vue b/src/renderer/src/views/profile/userAvatar.vue index 755a3f6..531f3c7 100644 --- a/src/renderer/src/views/profile/userAvatar.vue +++ b/src/renderer/src/views/profile/userAvatar.vue @@ -1,6 +1,11 @@