Files
llm-in-text/src/services/univerBridge.js
ydy0615 d8b7832b14 refactor: improve codebase structure and Univer integration
- Add AGENTS.md knowledge base with project documentation
- Move UserPreferences model to separate models.py file
- Extract API_KEY to environment variable for security
- Enhance Univer Editor with PPTX support and improved UI
- Improve file system handling with binary file detection
- Add HF_ENDPOINT mirror for better China connectivity
- Clean up unused imports and code structure
2026-04-11 09:24:14 +08:00

303 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Univer 编辑器桥接服务
* 封装 Univer 的初始化、加载、导出等操作
*
* 支持三种文档格式:
* - DOCX: 使用 DocsCorePreset
* - XLSX: 使用 SheetsCorePreset
* - PPTX: 使用 DocsCorePreset + Slides 插件组合
*/
import { createUniver, LocaleType, merge } from '@univerjs/presets'
import { UniverDocsCorePreset } from '@univerjs/preset-docs-core'
import { UniverSheetsCorePreset } from '@univerjs/preset-sheets-core'
import { UniverSlidesPlugin } from '@univerjs/slides'
import { UniverSlidesUIPlugin } from '@univerjs/slides-ui'
// 导入样式
import '@univerjs/preset-docs-core/lib/index.css'
import '@univerjs/preset-sheets-core/lib/index.css'
import '@univerjs/slides-ui/lib/index.css'
// 导入语言包
import DocsCoreEnUS from '@univerjs/preset-docs-core/locales/en-US'
import SheetsCoreEnUS from '@univerjs/preset-sheets-core/locales/en-US'
import DocsCoreZhCN from '@univerjs/preset-docs-core/locales/zh-CN'
import SheetsCoreZhCN from '@univerjs/preset-sheets-core/locales/zh-CN'
// Slides 语言包(使用空对象作为 fallback因为 slides 语言包可能不存在)
const SlidesZhCN = {}
const SlidesEnUS = {}
export const OfficeFormat = {
DOCX: 'docx',
XLSX: 'xlsx',
PPTX: 'pptx'
}
export const OfficePresetType = {
DOCS: 'docs',
SHEETS: 'sheets',
SLIDES: 'slides'
}
/**
* 根据文件扩展名判断 Office 格式
*/
export function detectOfficeFormat(filename) {
const ext = filename?.toLowerCase().split('.').pop() || ''
if (ext === 'docx') return OfficeFormat.DOCX
if (ext === 'xlsx') return OfficeFormat.XLSX
if (ext === 'pptx') return OfficeFormat.PPTX
return null
}
/**
* 根据格式获取对应的 Preset 类型
*/
export function getPresetType(format) {
switch (format) {
case OfficeFormat.DOCX:
return OfficePresetType.DOCS
case OfficeFormat.XLSX:
return OfficePresetType.SHEETS
case OfficeFormat.PPTX:
return OfficePresetType.SLIDES
default:
return null
}
}
/**
* 创建 Univer 实例
*/
export async function createUniverInstance(container, options = {}) {
const {
format = OfficeFormat.DOCX,
locale = 'zh-CN',
theme = 'light'
} = options
const localeType = locale === 'zh-CN' ? LocaleType.ZH_CN : LocaleType.EN_US
// 合并所有语言包(包括 Slides
const locales = locale === 'zh-CN'
? { [LocaleType.ZH_CN]: merge({}, DocsCoreZhCN, SheetsCoreZhCN, SlidesZhCN) }
: { [LocaleType.EN_US]: merge({}, DocsCoreEnUS, SheetsCoreEnUS, SlidesEnUS) }
const presets = []
const extraPlugins = []
// 根据格式添加对应的 Preset 和插件
switch (format) {
case OfficeFormat.DOCX:
// DOCX 只需要 DocsCorePreset
presets.push(UniverDocsCorePreset({
container,
theme: theme === 'dark' ? 'dark' : 'default'
}))
break
case OfficeFormat.XLSX:
// XLSX 只需要 SheetsCorePreset
presets.push(UniverSheetsCorePreset({
container,
theme: theme === 'dark' ? 'dark' : 'default'
}))
break
case OfficeFormat.PPTX:
// PPTX 需要 DocsCorePreset + Slides 插件
// DocsCorePreset 提供基础文档渲染能力
presets.push(UniverDocsCorePreset({
container,
theme: theme === 'dark' ? 'dark' : 'default'
}))
// Slides 插件提供演示文稿功能
// 注意:必须作为 plugins 而非 presets 传入
extraPlugins.push([UniverSlidesPlugin])
extraPlugins.push([UniverSlidesUIPlugin])
break
default:
// 默认使用 Docs 作为兜底
presets.push(UniverDocsCorePreset({
container,
theme: theme === 'dark' ? 'dark' : 'default'
}))
}
try {
const { univer, univerAPI } = createUniver({
locale: localeType,
locales,
presets,
plugins: extraPlugins.length > 0 ? extraPlugins : undefined,
collaboration: false // 纯前端模式,不启用协作
})
console.log(`[Univer] 初始化成功,格式: ${format}, 预设数量: ${presets.length}, 插件数量: ${extraPlugins.length}`)
return { univer, univerAPI }
} catch (error) {
console.error('[Univer] 初始化失败:', error)
throw new Error(`Univer 初始化失败: ${error.message}`)
}
}
/**
* Univer 编辑器实例包装类
*/
export class UniverEditorInstance {
constructor() {
this.univer = null
this.univerAPI = null
this.container = null
this.currentFormat = null
}
/**
* 初始化编辑器
*/
async init(container, options = {}) {
if (this.univer) {
await this.destroy()
}
this.container = container
this.currentFormat = options.format || OfficeFormat.DOCX
const result = await createUniverInstance(container, {
format: this.currentFormat,
...options
})
this.univer = result.univer
this.univerAPI = result.univerAPI
// 创建初始文档
if (this.currentFormat === OfficeFormat.XLSX) {
this.univerAPI.createWorkbook({})
} else {
this.univerAPI.createUniverDoc({})
}
return this
}
/**
* 从字节数组加载文档
*/
async loadFromBytes(bytes, format) {
if (!this.univerAPI) {
throw new Error('Univer 实例未初始化')
}
// 注意纯前端模式下Univer 不支持直接从 DOCX/XLSX/PPTX 字节流加载
// 这里需要使用快照模式或后端服务来解析
// 当前实现为占位,实际需要配合快照格式
console.warn('纯前端模式暂不支持从 DOCX/XLSX/PPTX 字节流加载,请使用快照模式')
return false
}
/**
* 导出为快照数据
*/
async exportSnapshot() {
if (!this.univerAPI) {
throw new Error('Univer 实例未初始化')
}
const activeDoc = this.univerAPI.getActiveDocument()
const activeSheet = this.univerAPI.getActiveWorkbook()
if (activeSheet) {
return {
type: OfficePresetType.SHEETS,
format: OfficeFormat.XLSX,
data: activeSheet.getSnapshot()
}
}
if (activeDoc) {
return {
type: OfficePresetType.DOCS,
format: OfficeFormat.DOCX,
data: activeDoc.getSnapshot()
}
}
return null
}
/**
* 从快照数据导入
*/
async importSnapshot(snapshot) {
if (!this.univerAPI || !snapshot?.data) {
throw new Error('无效的快照数据')
}
// 快照数据可以直接用于恢复文档状态
// 具体实现取决于 Univer API
console.log('导入快照:', snapshot.type, snapshot.format)
return true
}
/**
* 监听文档变化
*/
onChange(callback) {
if (!this.univerAPI) return
// Univer API 的事件监听
this.univerAPI.addEvent(this.univerAPI.Event.CommandExecuted, (event) => {
callback({
type: 'command',
data: event
})
})
}
/**
* 销毁实例
*/
async destroy() {
if (this.univer) {
this.univer.dispose()
this.univer = null
this.univerAPI = null
this.container = null
this.currentFormat = null
}
}
/**
* 获取当前格式
*/
getFormat() {
return this.currentFormat
}
/**
* 检查是否已初始化
*/
isInitialized() {
return this.univer !== null && this.univerAPI !== null
}
}
/**
* 创建 Univer 编辑器实例
*/
export function createUniverEditor() {
return new UniverEditorInstance()
}
export default {
createUniverInstance,
createUniverEditor,
detectOfficeFormat,
getPresetType,
OfficeFormat,
OfficePresetType,
UniverEditorInstance
}