From c555de5fe558e778cd6415d42f2bca766bd8f983 Mon Sep 17 00:00:00 2001 From: zdg Date: Wed, 27 Nov 2024 14:09:32 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dpptx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/src/AixPPTist/src/App.vue | 17 ++++++++++++----- src/renderer/src/utils/tool.js | 18 ++++++++++++------ src/renderer/src/views/model/index.vue | 13 +++++++------ 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/renderer/src/AixPPTist/src/App.vue b/src/renderer/src/AixPPTist/src/App.vue index 2d58652..1a05300 100644 --- a/src/renderer/src/AixPPTist/src/App.vue +++ b/src/renderer/src/AixPPTist/src/App.vue @@ -26,6 +26,7 @@ import Mobile from './views/Mobile/index.vue' import msgUtils from '@/plugins/modal' // 消息工具 import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api import { PPTApi } from './api' +import { sessionStore } from '@/utils/store' // electron-store 状态管理 const loading = ref(true) const _isPC = isPC() @@ -68,11 +69,17 @@ interface Result { } // 获取参数 const initLoad: Function = () => { - const urlSearch = location.href.split('?')[1] - const query = Object.fromEntries(new URLSearchParams(urlSearch)) - const id: String = query.id - // 如果存在就获取pptx幻灯片内容 - if (!!id) return PPTApi.getSlideList(id) + // const urlSearch = location.href.split('?')[1] + // const query = Object.fromEntries(new URLSearchParams(urlSearch)) + // const id: String = query.id + // // 如果存在就获取pptx幻灯片内容 + // if (!!id) return PPTApi.getSlideList(id) + // 缓存当前资源信息 + const resource = sessionStore.get('curr.resource') + if (!!resource) { // 有ppt 资源数据缓存 + slidesStore.setTitle(resource.title) + return PPTApi.getSlideList(resource.id) + } return Promise.resolve() } diff --git a/src/renderer/src/utils/tool.js b/src/renderer/src/utils/tool.js index 4afe082..a89be59 100644 --- a/src/renderer/src/utils/tool.js +++ b/src/renderer/src/utils/tool.js @@ -216,7 +216,11 @@ export const createWindow = async (type, data) => { win.maximize(); // win.setFullScreen(true) // 设置窗口为全屏 if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') win.webContents.openDevTools() // 打开调试工具 - eventHandles(type, win) // 事件监听处理 + let events = {} // 事件处理函数对象 + Object.keys(data) + .filter(k => typeof data[k] === 'function') + .forEach(k => events[k] = data[k]) + eventHandles(type, win, events) // 事件监听处理 break } default: @@ -286,17 +290,20 @@ export function toolWindow(type, {url, isConsole, isWeb=true, option={}}) { * 窗口创建-事件处理 * @param {*} type 事件类型 * @param {*} win 窗口对象 + * @param {*} events 事件对象 */ -const eventHandles = (type, win) => { +const eventHandles = (type, win, events) => { const toolState = useToolState() // 获取store状态 const winAll = Remote.BrowserWindow.getAllWindows() const mainWin = winAll.find(o => o.type == 'main') // 主窗口对象 // 公共方法 - const publicMethods = ({onClosed}={}) => { + const publicMethods = ({onClosed, closed, close}={}) => { // 监听主窗口-关闭事件 mainWin.once('close', () => {winPdf=null;win.destroy();}) win.on('closed', () => { - if(onClosed) onClosed() // 自定义关闭事件 + if(!!onClosed) onClosed() // 自定义关闭事件 + if(!!closed) closed() // 自定义关闭事件 + if(!!close) close() // 自定义关闭事件 win = null wins_tool = null winChild=null @@ -385,8 +392,7 @@ const eventHandles = (type, win) => { win&&win.destroy() }); const on = { - onClosed: () => { - } + ...events } publicMethods(on) // 加载公共方法 break diff --git a/src/renderer/src/views/model/index.vue b/src/renderer/src/views/model/index.vue index e4dc056..9a1111d 100644 --- a/src/renderer/src/views/model/index.vue +++ b/src/renderer/src/views/model/index.vue @@ -255,13 +255,14 @@ const handleAll = async(type, row) =>{ break; } case 'open': { // 打开资源-pptist - // console.log(row) if (row.filetype != 'aptist') return msgUtils.msgWarning('暂不支持该类型文件!') - sessionStore.set('curr.resource', row) // 缓存当前资源信息 - const query = { id: row.id } - const queryUrl = new URLSearchParams(query).toString() - console.log('打开资源 ', queryUrl) - createWindow('open-win', { url: `/pptist?${queryUrl}` }) + // 缓存当前资源信息 + sessionStore.set('curr.resource', row) + createWindow('open-win', { + url: '/pptist', + // 窗口关闭时,清除缓存 + close: () => {sessionStore.set('curr.resource', null)} + }) } } } From f2b1a097bbb81a36ee13fa00c84ad3c933769326 Mon Sep 17 00:00:00 2001 From: zdg Date: Wed, 27 Nov 2024 14:20:52 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0ppt-=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/src/AixPPTist/src/api/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/AixPPTist/src/api/index.ts b/src/renderer/src/AixPPTist/src/api/index.ts index 4ed3766..de8df59 100644 --- a/src/renderer/src/AixPPTist/src/api/index.ts +++ b/src/renderer/src/AixPPTist/src/api/index.ts @@ -51,7 +51,7 @@ export class PPTApi { // 获取所有幻灯片列表 static getSlideList(parentid: (Number | String)): Promise { return new Promise(async (resolve, reject) => { - const params: object = { parentid } + const params: object = { parentid, orderByColumn: 'fileidx', isAsc: 'asc' } const res: Result = await API_entpcoursefile.listEntpcoursefileNew(params) if (res.code === 200) { const slides = (res.rows || []).map(o => { @@ -111,7 +111,7 @@ export class PPTApi { static async updateSlides(newVal: object, oldVal: object) { const newData = toRaw(newVal) const oldData = toRaw(oldVal) - console.log('监听幻灯片数据变化', newData, oldData) + // console.log('监听幻灯片数据变化', newData, oldData) if (!(newData&&newData.length)) return // 新数据为空,不需要更新数据 else if (!oldData.length) return // 初始加载,旧数据空不需要更新数据 From a9443035c2466806fc054b8f7629311f31eee56e Mon Sep 17 00:00:00 2001 From: zdg Date: Wed, 27 Nov 2024 17:58:25 +0800 Subject: [PATCH 3/5] =?UTF-8?q?ppt=E6=96=87=E4=BB=B6=E8=BD=AC=E5=85=A5?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/AixPPTist/src/hooks/useImport.ts | 414 +++++++++++++++++- src/renderer/src/api/apiService.js | 6 + .../src/api/education/entpcoursefile.js | 8 + src/renderer/src/utils/comm.js | 23 + .../views/teachingDesign/container/right.vue | 142 +++++- 5 files changed, 584 insertions(+), 9 deletions(-) diff --git a/src/renderer/src/AixPPTist/src/hooks/useImport.ts b/src/renderer/src/AixPPTist/src/hooks/useImport.ts index e39c69d..d0e450f 100644 --- a/src/renderer/src/AixPPTist/src/hooks/useImport.ts +++ b/src/renderer/src/AixPPTist/src/hooks/useImport.ts @@ -28,12 +28,12 @@ const convertFontSizePtToPx = (html: string, ratio: number) => { }) } -export default () => { - const slidesStore = useSlidesStore() - const { theme } = storeToRefs(useSlidesStore()) +const slidesStore = useSlidesStore() +const { theme } = storeToRefs(useSlidesStore()) - const { addSlidesFromData } = useAddSlidesOrElements() - const { isEmptySlide } = useSlideHandler() +const { addSlidesFromData } = useAddSlidesOrElements() +const { isEmptySlide } = useSlideHandler() +export default () => { const exporting = ref(false) @@ -486,9 +486,413 @@ export default () => { reader.readAsArrayBuffer(file) } + + return { importSpecificFile, importPPTXFile, + PPTXFileToJson, exporting, } +} + + // 导入PPTX 返回 json +export const PPTXFileToJson = (data: File|ArrayBuffer) => { + return new Promise(async(resolve, reject) => { + if (!data) return + let fileArrayBuffer: ArrayBuffer = null + let resData = {} // 返回的数据 + + const shapeList: ShapePoolItem[] = [] + for (const item of SHAPE_LIST) { + shapeList.push(...item.children) + } + // 获取文件的 ArrayBuffer + const getArrayBuffer = () => { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => { + resolve(reader.result) + } + reader.onerror = reject + reader.readAsArrayBuffer(data) + }) + } + if (data instanceof File) { // 文件 + fileArrayBuffer = await getArrayBuffer() + } else if (data instanceof ArrayBuffer) { // ArrayBuffer + fileArrayBuffer = data + } else { + throw new Error('Invalid data type') + } + + // 开始解析 + const json = await parse(fileArrayBuffer) + + const ratio = 96 / 72 + const width = json.size.width + + 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)) { + 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 + resolve(resData) + }) } \ No newline at end of file diff --git a/src/renderer/src/api/apiService.js b/src/renderer/src/api/apiService.js index b030354..99deb41 100644 --- a/src/renderer/src/api/apiService.js +++ b/src/renderer/src/api/apiService.js @@ -50,4 +50,10 @@ export class school { // 获取学校管理审核 static checkSchool = data => ApiService.publicHttp(`/smarttalk/audit/checkSchool`,data,'post') +} + +export class Other { + static baseUrl = "/common/upload" + // 测试 + static uploadFile = data => ApiService.publicHttp(this.baseUrl, data, 'post', null, 'file') } \ No newline at end of file diff --git a/src/renderer/src/api/education/entpcoursefile.js b/src/renderer/src/api/education/entpcoursefile.js index 26e2cf5..8b2052f 100644 --- a/src/renderer/src/api/education/entpcoursefile.js +++ b/src/renderer/src/api/education/entpcoursefile.js @@ -93,6 +93,14 @@ export function batchUpdateNew(data) { data: data }) } +// zdg: 批量新增pptist - 新 +export function batchAddNew(data) { + return request({ + url: '/education/entpcoursefile/batch/add', + method: 'post', + data: data + }) +} // 修改entpcoursefile export function updateFile2Redis(data) { diff --git a/src/renderer/src/utils/comm.js b/src/renderer/src/utils/comm.js index 1cbc5db..5c26eaf 100644 --- a/src/renderer/src/utils/comm.js +++ b/src/renderer/src/utils/comm.js @@ -21,6 +21,29 @@ export function getFiles() { } return new Promise(cb) } +/** + * base64 转 blob + */ +export function base64ToBlob(base64Data) { + const contentType = base64Data?.match(/^data:([^;]+);base64,/)?.[1]||'image/png' + // 去除Base64编码数据中的前缀(如"data:image/png;base64,") + const byteCharacters = atob(base64Data.split(',')[1]); + const byteArrays = []; + for (let i = 0; i < byteCharacters.length; i++) { + byteArrays.push(byteCharacters.charCodeAt(i)); + } + return new Blob([new Uint8Array(byteArrays)], { type: contentType }); +} + +export function arrayBufferToBlob(arrayBuffer, contentType) { + return new Blob([new Uint8Array(arrayBuffer)], { type: contentType }); +} + +export function blobToFile(blob, fileName, contentType) { + fileName = fileName || 'file' + contentType = contentType || blob.type ||'image/png' + return new File([blob], fileName, { type: contentType }); +} // ============= 数学公式--相关 =================== /** diff --git a/src/renderer/src/views/teachingDesign/container/right.vue b/src/renderer/src/views/teachingDesign/container/right.vue index 2be6c0d..5089b15 100644 --- a/src/renderer/src/views/teachingDesign/container/right.vue +++ b/src/renderer/src/views/teachingDesign/container/right.vue @@ -59,11 +59,23 @@ import emitter from '@/utils/mitt' import EditDialog from './edit-dialog.vue' import AdjustDialog from './adjust-dialog.vue' import { completion, tempResult } from '@/api/mode/index.js' -import { dataSetJson } from '@/utils/comm.js' +// import { dataSetJson } from '@/utils/comm.js' +import * as commUtils from '@/utils/comm.js' import PptDialog from '@/views/prepare/container/pptist-dialog.vue' +import useUserStore from '@/store/modules/user' +import {PPTXFileToJson} from '@/AixPPTist/src/hooks/useImport' // ppt转json +import * as API_entpcourse from '@/api/education/entpcourse' // 相关api +import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api +import * as Api_server from '@/api/apiService' // 相关api + +const userStore = useUserStore() const pptDialog = ref(false) const resultList = ref([]) +const courseObj = reactive({ + node: null, // 选择的课程节点 +}) + emitter.on('changeMode', (item) => { console.log(item, 'item') resultList.value = item.child @@ -98,9 +110,39 @@ const params = reactive( } ) -const addAiPPT = (res) => { +const addAiPPT = async(res) => { + let node = courseObj.node + if (!node) return msgUtils.msgWarning('请选择章节?') //TODO res中有PPT地址 - console.log(res) + const params = { evalid: node.id, edituserid: userStore.id, pageSize: 1 } + const resEnpt = await HTTP_SERVER_API('getCourseList', params) + if (!(resEnpt?.rows?.[0] || null)) { // 创建 + const resid = await HTTP_SERVER_API('addEntpcourse') + courseObj.entp.id = resid + } else courseObj.entp = resEnpt?.rows?.[0] || null + // 下载PPT 并解析json转换到我们自己数据库 + fetch(res.url) + .then(res => res.arrayBuffer()) + .then(async buffer => { + const resPptJson = await PPTXFileToJson(buffer) + const { def, slides, ...content } = resPptJson + console.log(slides) + for( let o of slides ) { + await toRousrceUrl(o) + } + // return + // 生成ppt课件-父级 + const p_params = {parentContent: JSON.stringify(content)} + const parentid = await HTTP_SERVER_API('addEntpcoursefile', p_params) + if (!!parentid??null) { // 生成内容幻灯片 + if (slides.length > 0) { + const resSlides = slides.map(({id, ...slide}) => slide) + const params = {parentid, filetype: 'slide', title: '', slides: resSlides } + const res_3 = await HTTP_SERVER_API('batchAddNew', params) + console.log('xxxx', res_3) + } + } + }) } const conversation = async () => { for (let item of resultList.value) { @@ -155,15 +197,107 @@ emitter.on('changeResult', (item) => { resultList.value[curIndex.value].answer = item }) +// ======== zdg start ============ +// 统一HTTP处理 +const HTTP_SERVER_API = (type, params = {}) => { + switch (type) { + case 'addEntpcourse': { // 添加课程 + const node = courseObj.node || {} + if (!node) return msgUtils.msgWarning('请选择章节?') + const def = { // 默认参数 + entpid: userStore.user.deptId, // 部门id + level: 1, // 层级 + parentid: 0, // 父级id + dictid: 0, // 字典id + evalid: node.id, // 章节id + evalparentid: node.parentid, // 单元id(父级id) + edusubject: node.edusubject, // 学科 + edudegree: node.edudegree, // 年级 + edustage: node.edustage, // 阶段 + coursetype: '课标学科', // 课程类型 + coursetitle: node.itemtitle, // 课程名称 + coursedesc: '', // 课程描述 + status: '', // 状态 + dflag: 0, // 状态 + edituserid: userStore.id, // 编辑人id + createblankfile: 'no', // 创建空白文件 + } + courseObj.entp = def + return API_entpcourse.addEntpcourse(def) + } + case 'addEntpcoursefile': { // 添加课程文件 + params = getDefParams(params) + return API_entpcoursefile.addEntpcoursefileReturnId(params) + } + case 'batchAddNew': { // 批量添加课程文件 + params = getDefParams(params) + return API_entpcoursefile.batchAddNew(params) + } + case 'getCourseList': { // 获取课程列表 + return API_entpcourse.listEntpcourse(params) + } + case 'getCourseFileList':{ // 获取课程文件列表 + return API_entpcoursefile.listEntpcoursefileNew(params) + } + } +} +// 获取默认参数 +const getDefParams = (params) => { + const enpt = courseObj.entp + const def = { + parentid: 0, + entpid: userStore.user.deptId, + entpcourseid: enpt.id, + ppttype: 'file', + title: enpt.coursetitle, + fileurl: '', + filetype: 'aptist', + datacontent: '', + filekey: '', + filetag: '', + fileidx: 0, + dflag: 0, + status: '', + edituserid: userStore.id + } + return Object.assign(def, params) +} +// 图片|音频|视频 转换为在线地址 +const toRousrceUrl = async(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) { // 视频和音频 + } + } + if (o?.background?.image) await toRousrceUrl(o.background.image) + if (o?.elements) o.elements.forEach(async o => {await toRousrceUrl(o)}) +} +// ======== zdg end ============ const curNode = reactive({}) onMounted(() => { let data = sessionStore.get('subject.curNode') Object.assign(curNode, data); + courseObj.node = data let jsonKey = `课标-${data.edustage}-${data.edusubject}` - params.dataset_id = dataSetJson[jsonKey] + params.dataset_id = commUtils.dataSetJson[jsonKey] }) From d432d97507cbf9012a936967b77e07c4a7fb4b80 Mon Sep 17 00:00:00 2001 From: zdg Date: Thu, 28 Nov 2024 09:33:39 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 5867aac..5034c80 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,6 @@ "typescript": "~5.3.0", "vite": "^5.3.1", "vite-plugin-windicss": "^1.9.3", - "vue": "^3.4.30", "vue-tsc": "^1.8.25", "windicss": "^3.5.6" } From 482cab5cd3a574203f911889220189b7421ef2ba Mon Sep 17 00:00:00 2001 From: zdg Date: Thu, 28 Nov 2024 16:15:25 +0800 Subject: [PATCH 5/5] =?UTF-8?q?ppt=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/src/AixPPTist/src/App.vue | 18 +- src/renderer/src/AixPPTist/src/api/index.ts | 2 +- src/renderer/src/AixPPTist/src/api/store.ts | 1 - src/renderer/src/AixPPTist/src/api/watcher.ts | 31 ++ src/renderer/src/AixPPTist/src/main.ts | 2 +- .../src/views/Editor/EditorHeader/index.vue | 12 +- .../src/api/education/entpcoursefile.js | 8 + src/renderer/src/plugins/index.js | 8 +- src/renderer/src/plugins/vue3-menus.js | 437 ++++++++++++++++++ src/renderer/src/views/model/index.vue | 64 ++- .../views/teachingDesign/container/right.vue | 11 +- 11 files changed, 559 insertions(+), 35 deletions(-) create mode 100644 src/renderer/src/AixPPTist/src/api/watcher.ts create mode 100644 src/renderer/src/plugins/vue3-menus.js diff --git a/src/renderer/src/AixPPTist/src/App.vue b/src/renderer/src/AixPPTist/src/App.vue index 1a05300..62045fc 100644 --- a/src/renderer/src/AixPPTist/src/App.vue +++ b/src/renderer/src/AixPPTist/src/App.vue @@ -27,6 +27,7 @@ import msgUtils from '@/plugins/modal' // 消息工具 import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api import { PPTApi } from './api' import { sessionStore } from '@/utils/store' // electron-store 状态管理 +import './api/watcher' // 监听 const loading = ref(true) const _isPC = isPC() @@ -69,25 +70,20 @@ interface Result { } // 获取参数 const initLoad: Function = () => { - // const urlSearch = location.href.split('?')[1] - // const query = Object.fromEntries(new URLSearchParams(urlSearch)) - // const id: String = query.id - // // 如果存在就获取pptx幻灯片内容 - // if (!!id) return PPTApi.getSlideList(id) - // 缓存当前资源信息 + // 获取缓存的ppt 资源数据 const resource = sessionStore.get('curr.resource') if (!!resource) { // 有ppt 资源数据缓存 slidesStore.setTitle(resource.title) + if (!!resource.parentContent) { // 有全局配置项 + const opt = JSON.parse(resource.parentContent) + !!(opt.width??null) && slidesStore.setViewportSize(opt.width) // 有宽度配置项 + !!(opt.ratio??null) && slidesStore.setViewportRatio(opt.ratio)// 有比例配置项 + } return PPTApi.getSlideList(resource.id) } return Promise.resolve() } -// 监听幻灯片内容变化 -watch(() => slidesStore.slides, (newVal, oldVal) => { - // 更新幻灯片内容 - PPTApi.updateSlides(newVal, oldVal) -},{ deep: true })