mirror of
https://github.com/iOfficeAI/OfficeCLI
synced 2026-04-21 13:37:23 +00:00
fix(xlsx): propagate source numFmt to pivot cacheField
When building a pivot from a source range, resolve each source column's StyleIndex to its numFmtId and stamp it onto the cacheField. Without this, a date-formatted column (numFmtId 164, yyyy-mm-dd) rendered in the pivot as raw OADate serials (45306, 45337, ...) instead of the intended date format. Reuses ResolveColumnNumFmtIds already used for DataField.NumberFormatId.
This commit is contained in:
parent
56171db77a
commit
4a5b82de37
2 changed files with 30 additions and 10 deletions
|
|
@ -361,7 +361,8 @@ internal static partial class PivotTableHelper
|
|||
string sourceSheetName, string sourceRef,
|
||||
string[] headers, List<string[]> columnData,
|
||||
HashSet<int>? axisFieldIndices = null,
|
||||
List<DateGroupSpec>? dateGroups = null)
|
||||
List<DateGroupSpec>? dateGroups = null,
|
||||
uint?[]? columnNumFmtIds = null)
|
||||
{
|
||||
var recordCount = columnData.Count > 0 ? columnData[0].Length : 0;
|
||||
|
||||
|
|
@ -430,11 +431,20 @@ internal static partial class PivotTableHelper
|
|||
var fieldName = string.IsNullOrEmpty(headers[i]) ? $"Column{i + 1}" : headers[i];
|
||||
var values = i < columnData.Count ? columnData[i] : Array.Empty<string>();
|
||||
|
||||
// R19-1: per-column source numFmtId (date/currency/etc.) to stamp
|
||||
// on the cacheField so the pivot renders values with the same
|
||||
// formatting as the source column. Null means "General" and we
|
||||
// leave the default in place.
|
||||
uint? srcNumFmtId = (columnNumFmtIds != null && i < columnNumFmtIds.Length)
|
||||
? columnNumFmtIds[i] : null;
|
||||
|
||||
if (derivedByIdx.TryGetValue(i, out var spec))
|
||||
{
|
||||
// Derived date group field — synthesized, no records entries.
|
||||
cacheFields.AppendChild(BuildDateGroupDerivedCacheField(fieldName, spec,
|
||||
out fieldValueIndex[i]));
|
||||
var derived = BuildDateGroupDerivedCacheField(fieldName, spec,
|
||||
out fieldValueIndex[i]);
|
||||
if (srcNumFmtId.HasValue) derived.NumberFormatId = srcNumFmtId.Value;
|
||||
cacheFields.AppendChild(derived);
|
||||
fieldNumeric[i] = false; // records should skip this field
|
||||
continue;
|
||||
}
|
||||
|
|
@ -448,8 +458,12 @@ internal static partial class PivotTableHelper
|
|||
int parIdx = derivedByIdx
|
||||
.Where(kv => kv.Value.BaseFieldIdx == i)
|
||||
.Min(kv => kv.Key);
|
||||
cacheFields.AppendChild(BuildDateGroupBaseCacheField(fieldName, values, parIdx,
|
||||
out fieldValueIndex[i]));
|
||||
var baseField = BuildDateGroupBaseCacheField(fieldName, values, parIdx,
|
||||
out fieldValueIndex[i]);
|
||||
// Prefer the source column's numFmtId when present; else keep
|
||||
// the builder's 164u default (yyyy-mm-dd).
|
||||
if (srcNumFmtId.HasValue) baseField.NumberFormatId = srcNumFmtId.Value;
|
||||
cacheFields.AppendChild(baseField);
|
||||
fieldNumeric[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -458,8 +472,10 @@ internal static partial class PivotTableHelper
|
|||
// even when their values parse as numeric, so pivotField items
|
||||
// indices and cache record references stay in sync.
|
||||
bool forceStringIndexed = axisFieldIndices?.Contains(i) == true;
|
||||
cacheFields.AppendChild(BuildCacheField(
|
||||
fieldName, values, out fieldNumeric[i], out fieldValueIndex[i], forceStringIndexed));
|
||||
var plainField = BuildCacheField(
|
||||
fieldName, values, out fieldNumeric[i], out fieldValueIndex[i], forceStringIndexed);
|
||||
if (srcNumFmtId.HasValue) plainField.NumberFormatId = srcNumFmtId.Value;
|
||||
cacheFields.AppendChild(plainField);
|
||||
}
|
||||
cacheDef.AppendChild(cacheFields);
|
||||
|
||||
|
|
|
|||
|
|
@ -1049,8 +1049,13 @@ internal static partial class PivotTableHelper
|
|||
foreach (var r in rowFields) axisFieldSet.Add(r);
|
||||
foreach (var c in colFields) axisFieldSet.Add(c);
|
||||
foreach (var f in filterFields) axisFieldSet.Add(f);
|
||||
// R19-1: resolve numFmtIds BEFORE building the cache so date/number
|
||||
// formats on the source column propagate onto the cacheField's
|
||||
// numFmtId attribute. Without this, a column styled as "yyyy-mm-dd"
|
||||
// renders in the pivot as the raw OADate serial (45306, ...).
|
||||
var columnNumFmtIds = ResolveColumnNumFmtIds(workbookPart, columnStyleIds);
|
||||
var (cacheDef, fieldNumeric, fieldValueIndex) =
|
||||
BuildCacheDefinition(sourceSheetName, sourceRef, headers, columnData, axisFieldSet, dateGroups);
|
||||
BuildCacheDefinition(sourceSheetName, sourceRef, headers, columnData, axisFieldSet, dateGroups, columnNumFmtIds);
|
||||
cachePart.PivotCacheDefinition = cacheDef;
|
||||
cachePart.PivotCacheDefinition.Save();
|
||||
|
||||
|
|
@ -1129,13 +1134,12 @@ internal static partial class PivotTableHelper
|
|||
}
|
||||
var style = properties.GetValueOrDefault("style", "PivotStyleLight16");
|
||||
|
||||
// Resolve per-column numFmtId from the source StyleIndex so we can stamp
|
||||
// columnNumFmtIds was resolved above (R19-1) and reused here to stamp
|
||||
// it onto DataField elements below. Excel uses DataField.NumberFormatId
|
||||
// as the PRIMARY display driver for pivot values — the cell-level
|
||||
// StyleIndex alone is not enough; without this, Excel renders pivot
|
||||
// values as plain General-format numbers even though the rendered cells
|
||||
// carry the correct style.
|
||||
var columnNumFmtIds = ResolveColumnNumFmtIds(workbookPart, columnStyleIds);
|
||||
|
||||
// Page filters occupy rows ABOVE the pivot body. Ensure position leaves
|
||||
// enough headroom for filterCount filter rows + 1 blank separator row.
|
||||
|
|
|
|||
Loading…
Reference in a new issue