# XLSX 受控 editor

`fsys.openXml.xlsx.editor` 用于对既有普通 `.xlsx` 工作簿进行固定坐标的受控内容编辑。它只依赖公共 ZIP/关系层和系统 MSXML，不启动 Excel/WPS，不依赖 .NET，也不计算公式。

## 最小流程

```aardio
import fsys.openXml.xlsx.editor;

var ed,err = fsys.openXml.xlsx.editor.open("/template.xlsx");
if(!ed) return null,err;

var cell,ce = ed.getCell("数据","B3");
var count,we,report = ed.setCell("数据","B3",{value="已完成"});
if(count===null) { ed.close(); return null,we; }

var output,se = ed.save("/filled.xlsx",{overwrite=true});
ed.close();
if(!output) return null,se;
return output,report;
```

源文件以只读会话打开，成功保存必须使用另一个 `.xlsx` 路径。默认不覆盖已有目标；即使指定 `overwrite=true` 也不能覆盖源文件或可识别的硬链接别名。

## 读取会话视图

```aardio
var cell,err = ed.getCell("数据","B3");
var area,areaErr = ed.getRange("数据","A1:D10",{includeRows=true});
```

`getCell` 返回 `ref/row/column/exists/hasValue/value/valueType/raw/styleIndex`，以及 `hasFormula/formula/formulaType/formulaRef/hasCachedValue` 和 `merge` 等信息。公式的 `value` 只是文件保存的缓存；缓存失效或不存在时为 `null`，不伪造 `0`。

`getRange` 总是返回明确的 `startRow/startColumn/endRow/endColumn/rowCount/columnCount` 与稀疏 `cells`。`includeRows=true` 时附加 `values/rows` 矩阵，矩阵受 `maxMatrixCells`（默认 1000000）限制。合并区域不向非主格复制值，隐藏行列不被删除。

工作表选择器可以是 1 起序号、精确名称或 `listSheets()` 返回的描述对象。chartsheet/dialogsheet 可以列出，但不能作为单元格目标。地址只支持单格或矩形 A1，支持 `$` 和小写，不支持整行、整列、多区域、工作表前缀或外部引用。

## 写入语义

### 单元格

```aardio
ed.setCell("数据","B3",{value="文本"});
ed.setCell("数据","C3",{value=0});
ed.setCell("数据","D3",{value=false});
ed.setCell("数据","E3",{formula="SUM(C3,D3)"});
ed.setCell("数据","F3",{clear=true});
```

描述必须在 `value/formula/clear` 中选择一项：

- 空字符串、`0`、`false` 都是有效保存值；`null` 不是清除命令。
- 普通字符串（包括 `=开头`）按内联文本保存，不自动变成公式。
- 公式可以有一个前导 `=`，不写入调用者缓存；editor 不计算公式。
- `clear=true` 删除值、公式和缓存，但保留 `<c>`、坐标、样式及工作表结构。
- `kind="date"/"datetime"` 接受无时区 ISO 文本或 `time` 对象并写成 Excel 序列值；不自动创建或推断样式。`kind="error"` 只接受七种传统错误码。
- 未知字段、非法 UTF-8/XML 字符、超长文本、非有限数值和不支持的日期/错误值都会失败。

普通值或新公式覆盖已有公式默认拒绝：

```aardio
var count,err = ed.setCell("数据","C3",{value=10},{allowFormulaOverwrite=true});
```

相同内容写入返回 0，不触发缓存失效。失败返回 `null,err`，此前已经成功的会话修改不回滚，但本次写入不提交。

### 矩形

```aardio
var count,err,report = ed.setRange("数据","B3:D4",[
    [1,2,{formula="SUM(B3:C3)"}];
    [null,"",{clear=true}]
]);
```

`values` 必须是与 A1 矩形完全一致的二维数组；尾部空位使用显式 `length` 表达。`null` 跳过该位置，不改变内容和格式。任何位置越界、合并冲突、保护冲突、特殊公式或公式覆盖失败都会使整个 `setRange` 不提交。

合并区域只允许左上角主单元格，不能自动拆分或复制值。工作表坐标遵守 Excel 1 起边界：最大行 1048576、最大列 16384。

## 公式缓存失效

一次写操作真正改变单元格内容后，editor 对全工作簿执行保守失效：

1. 保留所有公式 `<f>` 原文及其属性；
2. 删除 `<v>`、非法公式缓存 `<is>` 和缓存类型属性，不填入 `0/false/空串`；
3. 删除 `calcChain.xml`、工作簿关系中的 `calcChain` 关系以及 `[Content_Types].xml` 对应声明；
4. 创建或更新 `calcPr`：`calcMode="auto"`、`fullCalcOnLoad="1"`、`forceFullCalc="1"`。

报告中的 `formulaCount/clearedFormulaCacheCount/removedCalcChain/recalcRequested` 只描述保存层操作。它不代表公式已经计算，也不代表图表、透视缓存、表对象计算列或外部链接结果已同步；需要最新结果请使用 Excel/WPS COM 打开、重算并保存。

## 拒绝范围

首版拒绝宏启用/旧格式/加密/签名包、工作簿保护、受保护目标工作表、共享/数组/数据表公式目标、表对象结构目标、插入删除行列、样式编辑以及复杂对象结构编辑。未知部件会保留，但涉及图表、透视、表对象或外链时报告 `dependent-cache-not-updated` 警告。

## 报告与原子性

`setCell/setRange` 的第一个返回值是实际发生内容变化的单元格数；报告还包含 `changedCellCount/changedSheets/changedParts/formulaCount/clearedFormulaCacheCount/removedCalcChain/recalcRequested/warnings/sourcePath`。`save` 报告另外包含 `outputPath`。

每个写操作先在独立 DOM 快照中完成全部检查，再通过公共 `applyParts` 一次提交；失败不会留下部分内存修改。保存使用公共 writer 的同目录临时 ZIP、完整关闭/CRC/限额检查和原子提交；失败不留下半成品，也不破坏已有目标。`close()` 直接丢弃尚未保存的内存修改。
