@gct-paas/word 0.1.34 → 0.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -79,6 +79,11 @@ export declare class DataManager {
79
79
  private get2DByCoords;
80
80
  private set2DByCoords;
81
81
  private cleanupCrossByAxisUid;
82
+ /**
83
+ * 复制轴行插入时:若 uid 与已有行重复,换新 uid 并复制关联交叉区行。
84
+ */
85
+ private duplicateCrossRowsByAxisUid;
86
+ private prepareAxisRowForInsert;
82
87
  /**
83
88
  * 将一个部分值对象合进整体
84
89
  */
@@ -19,6 +19,8 @@ export declare class Wp extends ModelGroup {
19
19
  constructor(options: ModelGroupOptions & {
20
20
  pPr?: WpPr;
21
21
  });
22
+ /** 将 pPr 挂到段落节点,供样式解析沿 parent 链查找 styles */
23
+ private bindPPrParent;
22
24
  get styleId(): string | undefined;
23
25
  /**
24
26
  * runs 属性别名,指向 children
@@ -46,6 +46,8 @@ export declare class Wtc extends ModelGroup {
46
46
  /** 是否是合并起始单元格 */
47
47
  get isMergeStart(): boolean;
48
48
  get contents(): Wp[];
49
+ /** 单元格垂直对齐(w:tcPr/w:vAlign) */
50
+ get vAlign(): 'top' | 'center' | 'bottom' | undefined;
49
51
  get firstContent(): Wp | undefined;
50
52
  get lastContent(): Wp | undefined;
51
53
  get cellMergeInfo(): {
@@ -18,6 +18,8 @@ export declare class Paragraph extends LayoutGroup<LayoutNode> {
18
18
  component: "paragraph";
19
19
  parent: ParagraphParent;
20
20
  baseline: number;
21
+ /** 上次 layout 应用的水平对齐偏移,避免重复 layout 时累加 */
22
+ private _alignmentOffset;
21
23
  splitFillEvent: EventUtil;
22
24
  constructor(options: ParagraphOptions);
23
25
  /**
package/dist/index.es.js CHANGED
@@ -22204,7 +22204,7 @@ class WpPr extends ModelNode {
22204
22204
  while (node.parent) {
22205
22205
  node = node.parent;
22206
22206
  }
22207
- this._cachedWStyles = node.styles;
22207
+ this._cachedWStyles = node.styles ?? node.root?.styles;
22208
22208
  return this._cachedWStyles;
22209
22209
  }
22210
22210
  /**
@@ -22433,6 +22433,13 @@ class Wp extends ModelGroup {
22433
22433
  constructor(options) {
22434
22434
  super(options);
22435
22435
  this.pPr = options.pPr;
22436
+ this.bindPPrParent();
22437
+ }
22438
+ /** 将 pPr 挂到段落节点,供样式解析沿 parent 链查找 styles */
22439
+ bindPPrParent() {
22440
+ if (this.pPr) {
22441
+ this.pPr.parent = this;
22442
+ }
22436
22443
  }
22437
22444
  get styleId() {
22438
22445
  return this.pPr?.styleId;
@@ -22492,6 +22499,7 @@ class Wp extends ModelGroup {
22492
22499
  wp.runs.forEach((run) => {
22493
22500
  run.parent = wp;
22494
22501
  });
22502
+ wp.bindPPrParent();
22495
22503
  return wp;
22496
22504
  }
22497
22505
  toXmlJson() {
@@ -22560,6 +22568,7 @@ class Wp extends ModelGroup {
22560
22568
  ensurePPr() {
22561
22569
  if (!this.pPr) {
22562
22570
  this.pPr = new WpPr({ elements: [] });
22571
+ this.bindPPrParent();
22563
22572
  }
22564
22573
  return this.pPr;
22565
22574
  }
@@ -22695,6 +22704,15 @@ class Wtc extends ModelGroup {
22695
22704
  get contents() {
22696
22705
  return this.children.filter((child) => child instanceof Wp);
22697
22706
  }
22707
+ /** 单元格垂直对齐(w:tcPr/w:vAlign) */
22708
+ get vAlign() {
22709
+ const elem = this.tcPrRaw ? pickElement(this.tcPrRaw, "w:vAlign") : void 0;
22710
+ const val = elem?.["@attrs"]?.["w:val"];
22711
+ if (val === "center" || val === "bottom" || val === "top") {
22712
+ return val;
22713
+ }
22714
+ return void 0;
22715
+ }
22698
22716
  get firstContent() {
22699
22717
  return this.contents[0];
22700
22718
  }
@@ -26036,6 +26054,101 @@ class Wdocument extends ModelGroup {
26036
26054
  return result;
26037
26055
  }
26038
26056
  }
26057
+ class ParagraphStyleResolver {
26058
+ /**
26059
+ * 计算段落的有效样式
26060
+ * 优先级:直接属性 > 样式定义 > 文档默认
26061
+ *
26062
+ * @param wpPr - 段落属性节点
26063
+ * @returns 计算后的有效样式
26064
+ *
26065
+ * @example
26066
+ * const style = ParagraphStyleResolver.resolve(wpPr);
26067
+ * console.log(style.alignment); // 可能来自直接属性或样式定义
26068
+ */
26069
+ static resolve(wpPr) {
26070
+ const directStyle = wpPr.directStyle;
26071
+ if (!wpPr.styleId || !wpPr.wStyles) {
26072
+ return directStyle;
26073
+ }
26074
+ const styleDefinition = wpPr.wStyles.resolveStyleDef(wpPr.styleId);
26075
+ if (!styleDefinition) {
26076
+ return directStyle;
26077
+ }
26078
+ const inheritedStyle = this.convertPPrToStyle(styleDefinition.pPr);
26079
+ return this.mergeStyles(inheritedStyle, directStyle);
26080
+ }
26081
+ /**
26082
+ * 从 WparagraphProperties 转换为 ParagraphStyle
26083
+ *
26084
+ * WparagraphProperties 是样式体系内部使用的数值化表示
26085
+ * ParagraphStyle 是对外的字符串化 API 表示
26086
+ *
26087
+ * @param pPr - 段落属性对象(可能为 undefined)
26088
+ * @returns 转换后的段落样式
26089
+ */
26090
+ static convertPPrToStyle(pPr) {
26091
+ if (!pPr) {
26092
+ return {};
26093
+ }
26094
+ const style = {};
26095
+ if (pPr.alignment) {
26096
+ style.alignment = pPr.alignment;
26097
+ }
26098
+ if (pPr.indent && Object.keys(pPr.indent).length > 0) {
26099
+ style.indent = {
26100
+ left: pPr.indent.left?.toString(),
26101
+ right: pPr.indent.right?.toString(),
26102
+ firstLine: pPr.indent.firstLine?.toString()
26103
+ };
26104
+ }
26105
+ if (pPr.spacing && Object.keys(pPr.spacing).length > 0) {
26106
+ style.spacing = {
26107
+ before: pPr.spacing.before?.toString(),
26108
+ after: pPr.spacing.after?.toString(),
26109
+ line: pPr.spacing.line?.toString(),
26110
+ beforeAutospacing: pPr.spacing.beforeAutospacing,
26111
+ afterAutospacing: pPr.spacing.afterAutospacing
26112
+ };
26113
+ }
26114
+ return style;
26115
+ }
26116
+ /**
26117
+ * 深度合并两个样式对象
26118
+ * 后来的值优先覆盖前面的值(override > base)
26119
+ *
26120
+ * @param base - 基础样式(如样式定义)
26121
+ * @param override - 覆盖样式(如直接属性)
26122
+ * @returns 合并后的样式
26123
+ */
26124
+ static mergeStyles(base, override) {
26125
+ const result = {};
26126
+ result.alignment = override.alignment ?? base.alignment;
26127
+ if (base.indent || override.indent) {
26128
+ result.indent = {
26129
+ left: override.indent?.left ?? base.indent?.left,
26130
+ right: override.indent?.right ?? base.indent?.right,
26131
+ firstLine: override.indent?.firstLine ?? base.indent?.firstLine
26132
+ };
26133
+ }
26134
+ if (base.spacing || override.spacing) {
26135
+ result.spacing = {
26136
+ before: override.spacing?.before ?? base.spacing?.before,
26137
+ after: override.spacing?.after ?? base.spacing?.after,
26138
+ line: override.spacing?.line ?? base.spacing?.line,
26139
+ beforeAutospacing: override.spacing?.beforeAutospacing ?? base.spacing?.beforeAutospacing,
26140
+ afterAutospacing: override.spacing?.afterAutospacing ?? base.spacing?.afterAutospacing
26141
+ };
26142
+ }
26143
+ if (base.lang || override.lang) {
26144
+ result.lang = {
26145
+ val: override.lang?.val ?? base.lang?.val,
26146
+ eastAsia: override.lang?.eastAsia ?? base.lang?.eastAsia
26147
+ };
26148
+ }
26149
+ return result;
26150
+ }
26151
+ }
26039
26152
  class TextStyleResolver {
26040
26153
  /**
26041
26154
  * 计算字符的有效样式
@@ -40251,6 +40364,8 @@ class Paragraph extends LayoutGroup {
40251
40364
  component = BuiltinComponentTypeConst.Paragraph;
40252
40365
  baseline = 0;
40253
40366
  // 基线位置,默认为 0,表示与段落顶部对齐
40367
+ /** 上次 layout 应用的水平对齐偏移,避免重复 layout 时累加 */
40368
+ _alignmentOffset = 0;
40254
40369
  splitFillEvent = new EventUtil();
40255
40370
  // backgroundColor?: string = 'rgba(0,0,0,0.06)';
40256
40371
  // borderTop?: Border = {
@@ -40317,15 +40432,19 @@ class Paragraph extends LayoutGroup {
40317
40432
  }
40318
40433
  applyStyle() {
40319
40434
  const wp = this.doc.layoutMapper.getModelNodeById(this.modelRef.id);
40320
- const style = wp?.pPr?.directStyle ?? {};
40435
+ const style = wp?.pPr ? ParagraphStyleResolver.resolve(wp.pPr) : {};
40321
40436
  let offset2 = 0;
40322
40437
  if (style.alignment === "center") {
40323
40438
  offset2 = (this.width - this.getContextWidth()) / 2;
40324
40439
  } else if (style.alignment === "right") {
40325
40440
  offset2 = this.width - this.getContextWidth();
40326
40441
  }
40442
+ offset2 = Math.max(offset2, 0);
40443
+ const delta = offset2 - this._alignmentOffset;
40444
+ this._alignmentOffset = offset2;
40445
+ if (delta === 0) return;
40327
40446
  this.forEachChild((child) => {
40328
- child.x += Math.max(offset2, 0);
40447
+ child.x += delta;
40329
40448
  });
40330
40449
  }
40331
40450
  /**
@@ -42310,8 +42429,18 @@ class TableCell extends LayoutGroup {
42310
42429
  layout(x2, y2) {
42311
42430
  this.layoutX = x2;
42312
42431
  this.layoutY = y2;
42432
+ const contentHeight = this.getContentHeight();
42433
+ const innerHeight = this.height - this.pt - this.pb;
42434
+ let contentOffsetY = 0;
42435
+ const wtc = this.modelRef ? this.doc.layoutMapper.getModelNodeById(this.modelRef.id) : void 0;
42436
+ const vAlign = wtc?.vAlign;
42437
+ if (vAlign === "center") {
42438
+ contentOffsetY = Math.max(0, (innerHeight - contentHeight) / 2);
42439
+ } else if (vAlign === "bottom") {
42440
+ contentOffsetY = Math.max(0, innerHeight - contentHeight);
42441
+ }
42313
42442
  this.forEachChild((child) => {
42314
- child.layout(this.layoutX + this.pl, this.layoutY + this.pt + child.y);
42443
+ child.layout(this.layoutX + this.pl, this.layoutY + this.pt + contentOffsetY + child.y);
42315
42444
  });
42316
42445
  }
42317
42446
  }
@@ -46160,6 +46289,39 @@ class DataManager {
46160
46289
  this.emit(path2, next, oldValue);
46161
46290
  });
46162
46291
  }
46292
+ /**
46293
+ * 复制轴行插入时:若 uid 与已有行重复,换新 uid 并复制关联交叉区行。
46294
+ */
46295
+ duplicateCrossRowsByAxisUid(crossKeys, axis, oldUid, newUid) {
46296
+ const uidField = axis === "y" ? "__gw_y_uid" : "__gw_x_uid";
46297
+ for (const crossKey of crossKeys) {
46298
+ const path2 = `$.${crossKey}`;
46299
+ const rows = this.getByPath(this.rawData, path2);
46300
+ if (!Array.isArray(rows)) continue;
46301
+ const matched = rows.filter((item) => item?.[uidField] === oldUid);
46302
+ if (matched.length === 0) continue;
46303
+ const oldValue = this.deepClone(rows);
46304
+ const copies = matched.map((item) => {
46305
+ const copy = this.deepClone(item);
46306
+ copy[uidField] = newUid;
46307
+ return copy;
46308
+ });
46309
+ const next = [...rows, ...copies];
46310
+ this.setByPath(this.rawData, path2, next);
46311
+ this.emit(path2, next, oldValue);
46312
+ }
46313
+ }
46314
+ prepareAxisRowForInsert(arr, axisInfo, value) {
46315
+ const cloned = this.deepClone(value);
46316
+ const uidField = axisInfo.axis === "y" ? "__gw_y_uid" : "__gw_x_uid";
46317
+ const oldUid = cloned?.[uidField];
46318
+ if (oldUid && arr.some((row) => row?.[uidField] === oldUid)) {
46319
+ const newUid = `${axisInfo.axis}_${uuid()}`;
46320
+ cloned[uidField] = newUid;
46321
+ this.duplicateCrossRowsByAxisUid(axisInfo.crossKeys, axisInfo.axis, oldUid, newUid);
46322
+ }
46323
+ return cloned;
46324
+ }
46163
46325
  /**
46164
46326
  * 将一个部分值对象合进整体
46165
46327
  */
@@ -46229,7 +46391,7 @@ class DataManager {
46229
46391
  const tableKey = path2.replace(/^\$\./, "").replace(/\[(n|\d+)\]/g, "");
46230
46392
  const axisInfo = this.resolveAxisByTableKey(tableKey);
46231
46393
  values.forEach((value) => {
46232
- const cloned = this.deepClone(value);
46394
+ const cloned = axisInfo ? this.prepareAxisRowForInsert(arr, axisInfo, value) : this.deepClone(value);
46233
46395
  arr.push(cloned);
46234
46396
  if (axisInfo) {
46235
46397
  const index2 = arr.length - 1;
@@ -46290,10 +46452,12 @@ class DataManager {
46290
46452
  throw new Error(`Index ${index2} out of bounds for array of length ${arr.length}`);
46291
46453
  }
46292
46454
  const oldValue = [...arr];
46293
- const clonedValues = values.map((value) => this.deepClone(value));
46294
- arr.splice(index2, 0, ...clonedValues);
46295
46455
  const tableKey = path2.replace(/^\$\./, "").replace(/\[(n|\d+)\]/g, "");
46296
46456
  const axisInfo = this.resolveAxisByTableKey(tableKey);
46457
+ const clonedValues = values.map(
46458
+ (value) => axisInfo ? this.prepareAxisRowForInsert(arr, axisInfo, value) : this.deepClone(value)
46459
+ );
46460
+ arr.splice(index2, 0, ...clonedValues);
46297
46461
  if (axisInfo) {
46298
46462
  for (let i = index2; i < arr.length; i++) {
46299
46463
  this.ensureAxisRowAndUid(tableKey, i, axisInfo.axis);
@@ -46344,6 +46508,7 @@ class DataManager {
46344
46508
  const data = this.deepClone(this.rawData);
46345
46509
  for (let path2 of this.defaults.keys()) {
46346
46510
  if (this.defaults.get(path2) === void 0) continue;
46511
+ if (path2.includes("[n")) continue;
46347
46512
  const value = this.getByPath(data, path2);
46348
46513
  if (value !== void 0) continue;
46349
46514
  this.setByPath(data, path2, this.getDefault(path2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gct-paas/word",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "GCT 在线 word",
5
5
  "keywords": [
6
6
  "vue",