Spaces:
Paused
Paused
| /** | |
| * HTML Generator Utility | |
| * 用于生成幻灯片的 HTML 内容 | |
| */ | |
| /** | |
| * 生成幻灯片的 HTML 内容 | |
| * @param {Object} slideData - 幻灯片数据 | |
| * @param {number} slideIndex - 幻灯片索引 | |
| * @param {Object} options - 渲染选项 | |
| * @returns {string} HTML 字符串 | |
| */ | |
| export function generateSlideHTML(slideData, slideIndex = 0, options = {}) { | |
| const { | |
| width = 1000, | |
| height = 562, | |
| backgroundColor = '#ffffff', | |
| scale = 1 | |
| } = options; | |
| if (!slideData || !slideData.slides || !slideData.slides[slideIndex]) { | |
| throw new Error(`Invalid slide data or slide index: ${slideIndex}`); | |
| } | |
| const slide = slideData.slides[slideIndex]; | |
| const slideBackground = slide.background || backgroundColor; | |
| // 生成元素的 HTML | |
| const elementsHTML = generateElementsHTML(slide.elements || [], { width, height, scale }); | |
| return ` | |
| <!DOCTYPE html> | |
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Slide ${slideIndex + 1}</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| background: #f0f0f0; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| .slide-container { | |
| width: ${width}px; | |
| height: ${height}px; | |
| background: ${slideBackground}; | |
| position: relative; | |
| overflow: hidden; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); | |
| transform: scale(${scale}); | |
| transform-origin: center; | |
| } | |
| .slide-element { | |
| position: absolute; | |
| user-select: none; | |
| } | |
| .text-element { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| word-wrap: break-word; | |
| overflow-wrap: break-word; | |
| } | |
| .image-element { | |
| background-size: contain; | |
| background-repeat: no-repeat; | |
| background-position: center; | |
| } | |
| .shape-element { | |
| border-radius: 4px; | |
| } | |
| .line-element { | |
| border: none; | |
| background: currentColor; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="slide-container"> | |
| ${elementsHTML} | |
| </div> | |
| </body> | |
| </html> | |
| `.trim(); | |
| } | |
| /** | |
| * 生成元素的 HTML | |
| * @param {Array} elements - 元素数组 | |
| * @param {Object} options - 渲染选项 | |
| * @returns {string} 元素 HTML 字符串 | |
| */ | |
| function generateElementsHTML(elements, options = {}) { | |
| if (!Array.isArray(elements)) { | |
| return ''; | |
| } | |
| return elements.map(element => { | |
| try { | |
| return generateElementHTML(element, options); | |
| } catch (error) { | |
| console.warn(`Failed to generate HTML for element:`, element, error); | |
| return ''; | |
| } | |
| }).join('\n'); | |
| } | |
| /** | |
| * 生成单个元素的 HTML | |
| * @param {Object} element - 元素数据 | |
| * @param {Object} options - 渲染选项 | |
| * @returns {string} 元素 HTML 字符串 | |
| */ | |
| function generateElementHTML(element, options = {}) { | |
| if (!element || typeof element !== 'object') { | |
| return ''; | |
| } | |
| const { | |
| type, | |
| left = 0, | |
| top = 0, | |
| width = 100, | |
| height = 100, | |
| rotate = 0, | |
| opacity = 1 | |
| } = element; | |
| const baseStyle = ` | |
| left: ${left}px; | |
| top: ${top}px; | |
| width: ${width}px; | |
| height: ${height}px; | |
| transform: rotate(${rotate}deg); | |
| opacity: ${opacity}; | |
| `; | |
| switch (type) { | |
| case 'text': | |
| return generateTextElementHTML(element, baseStyle); | |
| case 'image': | |
| return generateImageElementHTML(element, baseStyle); | |
| case 'shape': | |
| return generateShapeElementHTML(element, baseStyle); | |
| case 'line': | |
| return generateLineElementHTML(element, baseStyle); | |
| case 'chart': | |
| return generateChartElementHTML(element, baseStyle); | |
| case 'table': | |
| return generateTableElementHTML(element, baseStyle); | |
| default: | |
| console.warn(`Unknown element type: ${type}`); | |
| return ''; | |
| } | |
| } | |
| /** | |
| * 生成文本元素 HTML | |
| */ | |
| function generateTextElementHTML(element, baseStyle) { | |
| const { | |
| content = '', | |
| fontSize = 14, | |
| fontFamily = 'Arial', | |
| color = '#000000', | |
| fontWeight = 'normal', | |
| fontStyle = 'normal', | |
| textDecoration = 'none', | |
| textAlign = 'left', | |
| lineHeight = 1.2 | |
| } = element; | |
| const textStyle = ` | |
| font-size: ${fontSize}px; | |
| font-family: ${fontFamily}; | |
| color: ${color}; | |
| font-weight: ${fontWeight}; | |
| font-style: ${fontStyle}; | |
| text-decoration: ${textDecoration}; | |
| text-align: ${textAlign}; | |
| line-height: ${lineHeight}; | |
| `; | |
| return ` | |
| <div class="slide-element text-element" style="${baseStyle} ${textStyle}"> | |
| ${escapeHtml(content)} | |
| </div> | |
| `; | |
| } | |
| /** | |
| * 生成图片元素 HTML | |
| */ | |
| function generateImageElementHTML(element, baseStyle) { | |
| const { src = '', alt = '' } = element; | |
| if (!src) { | |
| return ` | |
| <div class="slide-element" style="${baseStyle} background: #f0f0f0; border: 2px dashed #ccc;"> | |
| <div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #999;"> | |
| 图片加载失败 | |
| </div> | |
| </div> | |
| `; | |
| } | |
| return ` | |
| <div class="slide-element image-element" style="${baseStyle} background-image: url('${escapeHtml(src)}');"> | |
| <img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" style="width: 100%; height: 100%; object-fit: contain; opacity: 0;" /> | |
| </div> | |
| `; | |
| } | |
| /** | |
| * 生成形状元素 HTML | |
| */ | |
| function generateShapeElementHTML(element, baseStyle) { | |
| const { | |
| fill = '#ffffff', | |
| stroke = '#000000', | |
| strokeWidth = 1, | |
| borderRadius = 0 | |
| } = element; | |
| const shapeStyle = ` | |
| background: ${fill}; | |
| border: ${strokeWidth}px solid ${stroke}; | |
| border-radius: ${borderRadius}px; | |
| `; | |
| return ` | |
| <div class="slide-element shape-element" style="${baseStyle} ${shapeStyle}"> | |
| </div> | |
| `; | |
| } | |
| /** | |
| * 生成线条元素 HTML | |
| */ | |
| function generateLineElementHTML(element, baseStyle) { | |
| const { | |
| stroke = '#000000', | |
| strokeWidth = 2 | |
| } = element; | |
| const lineStyle = ` | |
| background: ${stroke}; | |
| height: ${strokeWidth}px; | |
| color: ${stroke}; | |
| `; | |
| return ` | |
| <div class="slide-element line-element" style="${baseStyle} ${lineStyle}"> | |
| </div> | |
| `; | |
| } | |
| /** | |
| * 生成图表元素 HTML | |
| */ | |
| function generateChartElementHTML(element, baseStyle) { | |
| // 简化的图表渲染,实际项目中可能需要更复杂的图表库 | |
| return ` | |
| <div class="slide-element" style="${baseStyle} background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 4px;"> | |
| <div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #6c757d;"> | |
| 📊 图表 | |
| </div> | |
| </div> | |
| `; | |
| } | |
| /** | |
| * 生成表格元素 HTML | |
| */ | |
| function generateTableElementHTML(element, baseStyle) { | |
| const { data = [] } = element; | |
| if (!Array.isArray(data) || data.length === 0) { | |
| return ` | |
| <div class="slide-element" style="${baseStyle} background: #f8f9fa; border: 1px solid #dee2e6;"> | |
| <div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #6c757d;"> | |
| 📋 空表格 | |
| </div> | |
| </div> | |
| `; | |
| } | |
| const tableHTML = data.map(row => { | |
| if (!Array.isArray(row)) return ''; | |
| const cellsHTML = row.map(cell => `<td style="border: 1px solid #dee2e6; padding: 8px;">${escapeHtml(String(cell))}</td>`).join(''); | |
| return `<tr>${cellsHTML}</tr>`; | |
| }).join(''); | |
| return ` | |
| <div class="slide-element" style="${baseStyle} overflow: auto;"> | |
| <table style="width: 100%; border-collapse: collapse; font-size: 12px;"> | |
| ${tableHTML} | |
| </table> | |
| </div> | |
| `; | |
| } | |
| /** | |
| * HTML 转义函数 | |
| * @param {string} text - 需要转义的文本 | |
| * @returns {string} 转义后的文本 | |
| */ | |
| function escapeHtml(text) { | |
| if (typeof text !== 'string') { | |
| return String(text); | |
| } | |
| const map = { | |
| '&': '&', | |
| '<': '<', | |
| '>': '>', | |
| '"': '"', | |
| "'": ''' | |
| }; | |
| return text.replace(/[&<>"']/g, m => map[m]); | |
| } | |
| /** | |
| * 生成完整的 PPT HTML(包含所有幻灯片) | |
| * @param {Object} pptData - PPT 数据 | |
| * @param {Object} options - 渲染选项 | |
| * @returns {string} 完整的 HTML 字符串 | |
| */ | |
| export function generatePPTHTML(pptData, options = {}) { | |
| if (!pptData || !pptData.slides || !Array.isArray(pptData.slides)) { | |
| throw new Error('Invalid PPT data'); | |
| } | |
| const { width = 1000, height = 562 } = options; | |
| const slidesHTML = pptData.slides.map((slide, index) => { | |
| try { | |
| return generateSlideHTML(pptData, index, options); | |
| } catch (error) { | |
| console.warn(`Failed to generate HTML for slide ${index}:`, error); | |
| return ` | |
| <div class="slide-error" style="width: ${width}px; height: ${height}px; background: #f8d7da; border: 1px solid #f5c6cb; display: flex; align-items: center; justify-content: center; color: #721c24;"> | |
| 幻灯片 ${index + 1} 渲染失败 | |
| </div> | |
| `; | |
| } | |
| }).join('\n\n'); | |
| return ` | |
| <!DOCTYPE html> | |
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>PPT Preview</title> | |
| <style> | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| background: #f0f0f0; | |
| margin: 0; | |
| padding: 20px; | |
| } | |
| .ppt-container { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 20px; | |
| } | |
| .slide-wrapper { | |
| position: relative; | |
| } | |
| .slide-number { | |
| position: absolute; | |
| top: -30px; | |
| left: 0; | |
| background: #007bff; | |
| color: white; | |
| padding: 4px 8px; | |
| border-radius: 4px; | |
| font-size: 12px; | |
| font-weight: bold; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="ppt-container"> | |
| ${slidesHTML} | |
| </div> | |
| </body> | |
| </html> | |
| `.trim(); | |
| } | |
| export default { | |
| generateSlideHTML, | |
| generatePPTHTML | |
| }; |