diff --git a/wwwroot/api/office_read.dspy b/wwwroot/api/office_read.dspy index 2ec85a9..eba6587 100644 --- a/wwwroot/api/office_read.dspy +++ b/wwwroot/api/office_read.dspy @@ -39,22 +39,37 @@ if not real_full.startswith(real_ws + os.sep): if not os.path.isfile(full_path): return {"error": "文件不存在", "markdown": ""} -# 读 docx bytes -with open(full_path, 'rb') as f: - docx_bytes = f.read() +# 按扩展名分流:xlsx → IWorkbookData(表格),其余(docx)→ IDocumentData(文档) +# (2026-09-18 xlsx 在线查看/编辑接入) +_is_sheet = file_id.lower().endswith(('.xlsx', '.xlsm')) -# 调 univer-office 转 IDocumentData(格式保真,跳过 markdown) +# 读文件 bytes +with open(full_path, 'rb') as f: + file_bytes = f.read() + +# 调 univer-office 转换(格式保真,跳过 markdown) try: timeout = aiohttp.ClientTimeout(total=60) async with aiohttp.ClientSession(timeout=timeout) as session: - async with session.post( - 'http://127.0.0.1:19091/convert/docx-to-docdata', - data=docx_bytes, - headers={'Content-Type': 'application/octet-stream'}, - ) as resp: - if resp.status != 200: - return {"error": f"转换失败 HTTP {resp.status}", "documentData": None} - doc_data = await resp.json() - return {"documentData": doc_data} + if _is_sheet: + async with session.post( + 'http://127.0.0.1:19091/convert/xlsx-to-workbookdata', + data=file_bytes, + headers={'Content-Type': 'application/octet-stream'}, + ) as resp: + if resp.status != 200: + return {"error": "转换失败 HTTP " + str(resp.status), "workbookData": None} + workbook_data = await resp.json() + return {"workbookData": workbook_data} + else: + async with session.post( + 'http://127.0.0.1:19091/convert/docx-to-docdata', + data=file_bytes, + headers={'Content-Type': 'application/octet-stream'}, + ) as resp: + if resp.status != 200: + return {"error": "转换失败 HTTP " + str(resp.status), "documentData": None} + doc_data = await resp.json() + return {"documentData": doc_data} except Exception as e: - return {"error": str(e), "documentData": None} + return {"error": str(e), "documentData": None, "workbookData": None} diff --git a/wwwroot/api/office_save.dspy b/wwwroot/api/office_save.dspy index e34ae1f..cddb082 100644 --- a/wwwroot/api/office_save.dspy +++ b/wwwroot/api/office_save.dspy @@ -5,6 +5,8 @@ import aiohttp file_id = (params_kw or {}).get('file', '').strip() docdata_str = (params_kw or {}).get('documentData', '') or '' +# xlsx 保存:前端传 workbookData 字段(2026-09-18 xlsx 在线编辑接入) +workbookdata_str = (params_kw or {}).get('workbookData', '') or '' uid = await get_user() if not uid: @@ -30,13 +32,16 @@ async with DBPools().sqlorContext(dbname) as sor: if not file_id or not space_dir: return {"success": False, "error": "未指定文件或无工作空间"} -if not docdata_str: +# 按扩展名分流:xlsx 用 workbookData 字段,docx 用 documentData 字段 +_is_sheet = file_id.lower().endswith(('.xlsx', '.xlsm')) +_payload_str = workbookdata_str if _is_sheet else docdata_str +if not _payload_str: return {"success": False, "error": "缺少文档数据"} try: - documentData = _json.loads(docdata_str) + _payload = _json.loads(_payload_str) except Exception as e: - return {"success": False, "error": f"文档数据解析失败: {e}"} + return {"success": False, "error": "文档数据解析失败: " + str(e)} full_path = resolve_workspace_path(project_dir, space_dir, file_id) @@ -52,24 +57,28 @@ if not os.path.isfile(full_path): title = os.path.basename(full_path) -# 调 univer-office 生成 docx(IDocumentData -> docx,保留格式) +# 调 univer-office 生成 office 文件(按扩展名分流,保留格式) +# xlsx: IWorkbookData -> xlsx;docx: IDocumentData -> docx try: timeout = aiohttp.ClientTimeout(total=120) async with aiohttp.ClientSession(timeout=timeout) as session: - async with session.post( - 'http://127.0.0.1:19091/convert/doc-data-to-docx', - json={'documentData': documentData, 'title': title}, - ) as resp: + if _is_sheet: + _url = 'http://127.0.0.1:19091/convert/workbook-data-to-xlsx' + _req_json = {'workbookData': _payload} + else: + _url = 'http://127.0.0.1:19091/convert/doc-data-to-docx' + _req_json = {'documentData': _payload, 'title': title} + async with session.post(_url, json=_req_json) as resp: if resp.status != 200: - return {"success": False, "error": f"导出失败 HTTP {resp.status}"} - docx_bytes = await resp.read() + return {"success": False, "error": "导出失败 HTTP " + str(resp.status)} + out_bytes = await resp.read() except Exception as e: return {"success": False, "error": str(e)} # 写回工作空间 try: with open(full_path, 'wb') as f: - f.write(docx_bytes) - return {"success": True, "bytes": len(docx_bytes)} + f.write(out_bytes) + return {"success": True, "bytes": len(out_bytes)} except Exception as e: return {"success": False, "error": str(e)} diff --git a/wwwroot/api/workspace_open.dspy b/wwwroot/api/workspace_open.dspy index 1196606..58e32f8 100644 --- a/wwwroot/api/workspace_open.dspy +++ b/wwwroot/api/workspace_open.dspy @@ -54,7 +54,7 @@ text_exts = ['.md', '.py', '.js', '.html', '.css', '.json', '.txt', '.xml', '.ya video_exts = ['.mp4', '.m3u8', '.mpd', '.webm', '.mov', '.avi', '.mkv'] audio_exts = ['.mp3', '.wav', '.aac', '.flac', '.m4a', '.ogg'] image_exts = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.bmp', '.ico'] -office_exts = ['.docx'] # 在线编辑(univer-office)。xlsx/ppt 后续接入 Univer sheet/slide 插件 +office_exts = ['.docx', '.xlsx', '.xlsm'] # 在线查看/编辑(univer-office)。ppt 后续接入 Univer slide 插件 # 二级 URL 双透传 session_id + pipeline_id(2026-09-17 根因同 workspace_view/upload): # 通用助手(pipeline_id=_generic、无 session_id)下漏拼 pipeline_id 会让下游端点 diff --git a/wwwroot/api/workspace_view.dspy b/wwwroot/api/workspace_view.dspy index 126af09..381766f 100644 --- a/wwwroot/api/workspace_view.dspy +++ b/wwwroot/api/workspace_view.dspy @@ -139,9 +139,9 @@ if ext in image_exts: }] } -# office 文档(docx)→ univer-office 在线查看(mode=view 只读,不显示保存按钮)+ 下载按钮 -# (2026-09-18 用户要求:查看和编辑都在线,另提供下载按钮) -office_exts = ['.docx'] +# office 文档(docx/xlsx)→ univer-office 在线查看(mode=view 只读,不显示保存按钮)+ 下载按钮 +# (2026-09-18 用户要求:查看和编辑都在线,另提供下载按钮;同日 xlsx 接入) +office_exts = ['.docx', '.xlsx', '.xlsm'] if ext in office_exts: univer_url = entire_url("/univer-office/") + "?file=" + quote(file_id) + "&mode=view" + _fq_s download_url_v = file_url + "&download=1"