@gct-paas/word 0.1.23 → 0.1.25

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.
@@ -1,5 +1,6 @@
1
1
  import { WrText } from '../../../model/document';
2
2
  import { LayoutContext } from '../../LayoutContext';
3
+ import { TextRun } from '../../../view/runs/TextRun';
3
4
  import { TextWidget } from '../../../view/runs/TextWidget';
4
5
  import { HandlerContext } from '../../types';
5
6
  type ValueType = string | number | undefined | null;
@@ -50,6 +51,7 @@ export declare class FieldBaseHandler {
50
51
  ctx: HandlerContext;
51
52
  fontSize: number;
52
53
  label: string;
54
+ layoutStyle?: Partial<TextRun>;
53
55
  extra?: Partial<TextWidget>;
54
56
  }): void;
55
57
  /**
@@ -97,6 +97,12 @@ export declare class TextRun extends LayoutNode implements ITextRun {
97
97
  descent: number;
98
98
  style?: TextStyle;
99
99
  constructor(options: TextRunOptions);
100
+ /**
101
+ * 构建与 Konva 渲染一致的度量参数。
102
+ * 须与段落 layout 使用的 TextRun 样式一致;若只传 fontSize 会按默认字体度量,
103
+ * 加粗/自定义字体后 advance 偏窄,易出现字母叠画。
104
+ */
105
+ static measurePayload(text: string, layoutStyle?: Partial<TextRun>): Konva.TextConfig;
100
106
  /**
101
107
  * 计算文字大小 度量
102
108
  * 优化版本
@@ -2,7 +2,7 @@ import { default as Konva } from 'konva';
2
2
  export declare class TextUtil {
3
3
  /** 字体度量缓存限制 */
4
4
  private static readonly FONT_METRICS_CACHE_LIMIT;
5
- /** 布局大小缓存限制 */
5
+ /** 布局大小缓存限制(按「样式 + 单段文本」键,长文档可提高命中率) */
6
6
  private static readonly LAYOUT_SIZE_CACHE_LIMIT;
7
7
  /** 东亚常见全宽字符块 */
8
8
  private static readonly EAST_ASIAN_FULL_WIDTH_REGEX;
@@ -18,6 +18,11 @@ export declare class TextUtil {
18
18
  * - kg·m²
19
19
  * 等符号贴太近的问题
20
20
  */
21
+ /**
22
+ * 中间点类:CoreText / DirectWrite 下 advance 常偏窄,仅靠较小 ratio 仍可能与字母叠画;
23
+ * 在 getVisualSpacing 内另有 em 下限(与平台无关)。
24
+ */
25
+ private static readonly MIDDLE_DOT_EXTRA_FLOOR_EM;
21
26
  private static readonly VISUAL_SPACING_MAP;
22
27
  private static fontMetricsCache;
23
28
  private static layoutSizeCache;
@@ -51,6 +56,8 @@ export declare class TextUtil {
51
56
  * @param fontSize
52
57
  * @returns
53
58
  */
59
+ /** 数学/单位中间的点号(不含列表圆点 •) */
60
+ private static isMiddleDotSpacingChar;
54
61
  static getVisualSpacing(char: string, fontSize: number): number;
55
62
  /**
56
63
  * 生成文本度量缓存键
package/dist/index.es.js CHANGED
@@ -26676,9 +26676,9 @@ const mainData2WordData = (data) => {
26676
26676
  };
26677
26677
  class TextUtil {
26678
26678
  /** 字体度量缓存限制 */
26679
- static FONT_METRICS_CACHE_LIMIT = 50;
26680
- /** 布局大小缓存限制 */
26681
- static LAYOUT_SIZE_CACHE_LIMIT = 100;
26679
+ static FONT_METRICS_CACHE_LIMIT = 80;
26680
+ /** 布局大小缓存限制(按「样式 + 单段文本」键,长文档可提高命中率) */
26681
+ static LAYOUT_SIZE_CACHE_LIMIT = 500;
26682
26682
  /** 东亚常见全宽字符块 */
26683
26683
  static EAST_ASIAN_FULL_WIDTH_REGEX = /[\u3000-\u303F\u3040-\u30FF\u31F0-\u31FF\uFF01-\uFF60\uFFE0-\uFFE6]/u;
26684
26684
  /** 表单/文档中常见的几何符号 */
@@ -26693,14 +26693,25 @@ class TextUtil {
26693
26693
  * - kg·m²
26694
26694
  * 等符号贴太近的问题
26695
26695
  */
26696
+ /**
26697
+ * 中间点类:CoreText / DirectWrite 下 advance 常偏窄,仅靠较小 ratio 仍可能与字母叠画;
26698
+ * 在 getVisualSpacing 内另有 em 下限(与平台无关)。
26699
+ */
26700
+ static MIDDLE_DOT_EXTRA_FLOOR_EM = 0.24;
26696
26701
  static VISUAL_SPACING_MAP = {
26697
26702
  "·": 0.24,
26698
26703
  "•": 0.24,
26699
- "/": 0.16,
26700
- "/": 0.16,
26701
- "|": 0.12,
26702
- ":": 0.06,
26703
- "": 0.08
26704
+ "": 0.24,
26705
+ // ⋅ DOT OPERATOR(常见于 N⋅m 等)
26706
+ "": 0.24,
26707
+ // ∙ BULLET OPERATOR
26708
+ "·": 0.24,
26709
+ // · GREEK ANO TELEIA,易与 · 混用
26710
+ "/": 0.2,
26711
+ "/": 0.2,
26712
+ "|": 0.16,
26713
+ ":": 0.12,
26714
+ ":": 0.12
26704
26715
  };
26705
26716
  static fontMetricsCache = /* @__PURE__ */ new Map();
26706
26717
  static layoutSizeCache = /* @__PURE__ */ new Map();
@@ -26741,12 +26752,23 @@ class TextUtil {
26741
26752
  * @param fontSize
26742
26753
  * @returns
26743
26754
  */
26755
+ /** 数学/单位中间的点号(不含列表圆点 •) */
26756
+ static isMiddleDotSpacingChar(char) {
26757
+ return char === "·" || // · MIDDLE DOT
26758
+ char === "⋅" || // ⋅ DOT OPERATOR
26759
+ char === "∙" || // ∙ BULLET OPERATOR
26760
+ char === "·";
26761
+ }
26744
26762
  static getVisualSpacing(char, fontSize2) {
26745
26763
  const ratio = this.VISUAL_SPACING_MAP[char];
26746
26764
  if (!ratio) {
26747
26765
  return 0;
26748
26766
  }
26749
- return fontSize2 * ratio;
26767
+ let space = fontSize2 * ratio;
26768
+ if (this.isMiddleDotSpacingChar(char)) {
26769
+ space = Math.max(space, fontSize2 * this.MIDDLE_DOT_EXTRA_FLOOR_EM);
26770
+ }
26771
+ return space;
26750
26772
  }
26751
26773
  /**
26752
26774
  * 生成文本度量缓存键
@@ -26935,6 +26957,37 @@ class TextRun extends LayoutNode {
26935
26957
  Object.assign(this, options.style);
26936
26958
  }
26937
26959
  }
26960
+ /**
26961
+ * 构建与 Konva 渲染一致的度量参数。
26962
+ * 须与段落 layout 使用的 TextRun 样式一致;若只传 fontSize 会按默认字体度量,
26963
+ * 加粗/自定义字体后 advance 偏窄,易出现字母叠画。
26964
+ */
26965
+ static measurePayload(text, layoutStyle) {
26966
+ const ls = layoutStyle ?? {};
26967
+ const fontSize2 = ls.fontSize ?? DEFAULT_FONT_SIZE;
26968
+ const fontFamily = ls.fontFamily ?? DEFAULT_FONT_FAMILY;
26969
+ let fontStyle;
26970
+ if (ls.bold && ls.italic) {
26971
+ fontStyle = "bold italic";
26972
+ } else if (ls.bold) {
26973
+ fontStyle = "bold";
26974
+ } else if (ls.italic) {
26975
+ fontStyle = "italic";
26976
+ }
26977
+ const payload = {
26978
+ text,
26979
+ fontSize: fontSize2,
26980
+ fontFamily,
26981
+ lineHeight: 1
26982
+ };
26983
+ if (fontStyle) {
26984
+ payload.fontStyle = fontStyle;
26985
+ }
26986
+ if (ls.letterSpacing !== void 0 && ls.letterSpacing !== null) {
26987
+ payload.letterSpacing = ls.letterSpacing;
26988
+ }
26989
+ return payload;
26990
+ }
26938
26991
  /**
26939
26992
  * 计算文字大小 度量
26940
26993
  * 优化版本
@@ -26973,10 +27026,7 @@ class TextRun extends LayoutNode {
26973
27026
  }
26974
27027
  static createEmptyRun(doc) {
26975
27028
  const fontSize2 = DEFAULT_FONT_SIZE;
26976
- const { height, ascent, descent } = TextRun.measureText({
26977
- text: "0",
26978
- fontSize: fontSize2
26979
- });
27029
+ const { height, ascent, descent } = TextRun.measureText(TextRun.measurePayload("0", { fontSize: fontSize2 }));
26980
27030
  const run = new TextRun({
26981
27031
  doc,
26982
27032
  width: 6,
@@ -40299,14 +40349,11 @@ class TextHandler {
40299
40349
  let text = wr.text;
40300
40350
  const textStyle = TextStyleResolver.resolve(wr.rPr, context.doc.model?.styles);
40301
40351
  const layoutStyle = TextRun.textStyle2LayoutStyle(textStyle);
40302
- const fontSize2 = layoutStyle.fontSize ?? DEFAULT_FONT_SIZE;
40303
40352
  const chars = text.split("");
40304
40353
  let doc = context.doc;
40305
40354
  let charMetrics = [];
40306
40355
  let remainingWidth = context.getParagraphRemainingSize();
40307
- const { ascent, descent } = TextUtil.getFontMetrics({
40308
- fontSize: fontSize2
40309
- });
40356
+ const { ascent, descent } = TextUtil.getFontMetrics(TextRun.measurePayload("M", layoutStyle));
40310
40357
  const complete = () => {
40311
40358
  if (!charMetrics.length) return;
40312
40359
  const width = charMetrics.reduce((total, item) => total + item.width, 0);
@@ -40333,10 +40380,7 @@ class TextHandler {
40333
40380
  context.addRun(run);
40334
40381
  };
40335
40382
  chars.forEach((char, charIndex) => {
40336
- const { width, height } = TextRun.measureText({
40337
- text: char,
40338
- fontSize: fontSize2
40339
- });
40383
+ const { width, height } = TextRun.measureText(TextRun.measurePayload(char, layoutStyle));
40340
40384
  if (width <= remainingWidth) {
40341
40385
  charMetrics.push({
40342
40386
  char,
@@ -40370,14 +40414,10 @@ class TextHandler {
40370
40414
  let text = wr.text;
40371
40415
  const textStyle = TextStyleResolver.resolve(wr.rPr, context.doc.model?.styles);
40372
40416
  const layoutStyle = TextRun.textStyle2LayoutStyle(textStyle);
40373
- const fontSize2 = layoutStyle.fontSize ?? DEFAULT_FONT_SIZE;
40374
40417
  const chars = text.split("");
40375
40418
  let doc = context.doc;
40376
40419
  chars.forEach((char, charIndex) => {
40377
- const { width, height, ascent, descent } = TextRun.measureText({
40378
- text: char,
40379
- fontSize: fontSize2
40380
- });
40420
+ const { width, height, ascent, descent } = TextRun.measureText(TextRun.measurePayload(char, layoutStyle));
40381
40421
  const run = new TextRun({
40382
40422
  doc,
40383
40423
  width,
@@ -40581,10 +40621,7 @@ class FieldBaseHandler {
40581
40621
  position: "left"
40582
40622
  });
40583
40623
  chars.forEach((char, charIndex) => {
40584
- const { width, height, ascent, descent } = TextRun.measureText({
40585
- text: char,
40586
- fontSize: fontSize2
40587
- });
40624
+ const { width, height, ascent, descent } = TextRun.measureText(TextRun.measurePayload(char, layoutStyle));
40588
40625
  const run = new TextWidget({
40589
40626
  doc,
40590
40627
  width,
@@ -40618,6 +40655,9 @@ class FieldBaseHandler {
40618
40655
  static layoutNoValueLabel(ctx) {
40619
40656
  const { context, wr } = ctx;
40620
40657
  let doc = context.doc;
40658
+ const textStyle = TextStyleResolver.resolve(wr.rPr, context.doc.model?.styles);
40659
+ const layoutStyle = TextRun.textStyle2LayoutStyle(textStyle);
40660
+ const measureStyle = { ...layoutStyle, fontSize: 16 };
40621
40661
  const { label, type: type4 } = this.getNoValueLabel(ctx);
40622
40662
  const chars = label.split("");
40623
40663
  this.addFieldMarker({
@@ -40626,10 +40666,7 @@ class FieldBaseHandler {
40626
40666
  position: "left"
40627
40667
  });
40628
40668
  chars.forEach((char, charIndex) => {
40629
- const { width, height, ascent, descent } = TextRun.measureText({
40630
- text: char,
40631
- fontSize: 16
40632
- });
40669
+ const { width, height, ascent, descent } = TextRun.measureText(TextRun.measurePayload(char, measureStyle));
40633
40670
  const run = new TextWidget({
40634
40671
  doc,
40635
40672
  width,
@@ -40698,13 +40735,12 @@ class FieldBaseHandler {
40698
40735
  ctx: { context, wr },
40699
40736
  fontSize: fontSize2,
40700
40737
  extra,
40701
- label
40738
+ label,
40739
+ layoutStyle
40702
40740
  } = payload;
40741
+ const style = { ...layoutStyle, fontSize: fontSize2 };
40703
40742
  label.split("").forEach((char, charIndex) => {
40704
- const { width, height, ascent, descent } = TextRun.measureText({
40705
- text: char,
40706
- fontSize: fontSize2
40707
- });
40743
+ const { width, height, ascent, descent } = TextRun.measureText(TextRun.measurePayload(char, style));
40708
40744
  const run = new TextWidget({
40709
40745
  doc: context.doc,
40710
40746
  width,
@@ -40773,16 +40809,16 @@ class FieldBaseHandler {
40773
40809
  if (context.doc.mode === DocModeTypeConst.Print) {
40774
40810
  return;
40775
40811
  }
40812
+ const textStyle = TextStyleResolver.resolve(wr.rPr, context.doc.model?.styles);
40813
+ const layoutStyle = TextRun.textStyle2LayoutStyle(textStyle);
40814
+ const measureStyle = { ...layoutStyle, fontSize: fontSize2 };
40776
40815
  const label = position === "left" ? "[" : "]";
40777
40816
  const options = position === "left" ? {
40778
40817
  widgetFieldLeftMarker: true
40779
40818
  } : {
40780
40819
  widgetFieldRightMarker: true
40781
40820
  };
40782
- const { width, height, ascent, descent } = TextRun.measureText({
40783
- text: label,
40784
- fontSize: fontSize2
40785
- });
40821
+ const { width, height, ascent, descent } = TextRun.measureText(TextRun.measurePayload(label, measureStyle));
40786
40822
  const _width = position === "left" ? width + 2 : width + 3;
40787
40823
  const run = new TextWidget({
40788
40824
  doc: context.doc,
@@ -40849,10 +40885,9 @@ class OptionHandler extends FieldBaseHandler {
40849
40885
  });
40850
40886
  }
40851
40887
  static layoutOptionItem(context, wr, option, optionIndex, layoutStyle) {
40852
- const { width, height, ascent, descent } = TextRun.measureText({
40853
- text: "0",
40854
- fontSize: 16
40855
- });
40888
+ const { width, height, ascent, descent } = TextRun.measureText(
40889
+ TextRun.measurePayload("0", { ...layoutStyle, fontSize: 16 })
40890
+ );
40856
40891
  const fontSize2 = layoutStyle.fontSize ?? DEFAULT_FONT_SIZE;
40857
40892
  const { iconLabelSpace, labelPosition, controlEnumSpace } = wr.widgetMeta?.props;
40858
40893
  const isVertical = this.isVerticalLayout(wr);
@@ -40873,6 +40908,7 @@ class OptionHandler extends FieldBaseHandler {
40873
40908
  ctx: { context, wr },
40874
40909
  fontSize: fontSize2,
40875
40910
  label: option.label,
40911
+ layoutStyle,
40876
40912
  extra: {
40877
40913
  widgetOption
40878
40914
  }
@@ -40984,10 +41020,12 @@ class SignatureHandler extends FieldBaseHandler {
40984
41020
  const format2 = signatureType === SignatureTypeConst.SIGNATURE_DATETIME ? "YYYY-MM-DD HH:mm" : "YYYY-MM-DD";
40985
41021
  const timeStr = dayjs(time).format(format2);
40986
41022
  let position = signDisplayStyle === SignDisplayTypeConst.VERTICAL ? "bottom" : "right";
40987
- const { width: textWidth, height: textHeight } = TextRun.measureText({
40988
- text: timeStr,
40989
- fontSize: DEFAULT_FONT_SIZE
40990
- });
41023
+ const textStyle = TextStyleResolver.resolve(wr.rPr, ctx.context.doc.model?.styles);
41024
+ const layoutStyle = TextRun.textStyle2LayoutStyle(textStyle);
41025
+ const fontSize2 = layoutStyle.fontSize ?? DEFAULT_FONT_SIZE;
41026
+ const { width: textWidth, height: textHeight } = TextRun.measureText(
41027
+ TextRun.measurePayload(timeStr, { ...layoutStyle, fontSize: fontSize2 })
41028
+ );
40991
41029
  return {
40992
41030
  render: render2,
40993
41031
  rectWidth: width + (position === "right" ? textWidth : 0),
@@ -40995,7 +41033,7 @@ class SignatureHandler extends FieldBaseHandler {
40995
41033
  decorations: [
40996
41034
  {
40997
41035
  text: timeStr,
40998
- fontSize: DEFAULT_FONT_SIZE,
41036
+ fontSize: fontSize2,
40999
41037
  x: position === "right" ? width : 0,
41000
41038
  y: position === "right" ? (height - textHeight) / 2 : height,
41001
41039
  position,
@@ -41079,10 +41117,7 @@ class InputHandler extends FieldBaseHandler {
41079
41117
  position: "left"
41080
41118
  });
41081
41119
  chars.forEach((char, charIndex) => {
41082
- const { width, height, ascent, descent } = TextRun.measureText({
41083
- text: char,
41084
- fontSize: fontSize2
41085
- });
41120
+ const { width, height, ascent, descent } = TextRun.measureText(TextRun.measurePayload(char, layoutStyle));
41086
41121
  const run = new TextWidget({
41087
41122
  doc,
41088
41123
  width,
@@ -41150,10 +41185,9 @@ class AttachmentHandler extends FieldBaseHandler {
41150
41185
  const { ctx, item, itemIndex, layoutStyle, itemIsLast } = payload;
41151
41186
  const name = getFileName(item);
41152
41187
  const fontSize2 = layoutStyle.fontSize ?? DEFAULT_FONT_SIZE;
41153
- const { width, height, ascent, descent } = TextRun.measureText({
41154
- text: "0",
41155
- fontSize: 16
41156
- });
41188
+ const { width, height, ascent, descent } = TextRun.measureText(
41189
+ TextRun.measurePayload("0", { ...layoutStyle, fontSize: 16 })
41190
+ );
41157
41191
  const widgetFileItem = {
41158
41192
  index: itemIndex,
41159
41193
  value: item
@@ -41176,6 +41210,7 @@ class AttachmentHandler extends FieldBaseHandler {
41176
41210
  ctx,
41177
41211
  fontSize: fontSize2,
41178
41212
  label: name,
41213
+ layoutStyle,
41179
41214
  extra: {
41180
41215
  style: layoutStyle,
41181
41216
  widgetFileItem
@@ -41270,10 +41305,9 @@ class SerialNumberHandler extends WidgetBaseHandler {
41270
41305
  const layoutStyle = this.getLayoutStyle();
41271
41306
  const label = (context.cell?.subRenderer?.dataIndex ?? 0) + 1 + "";
41272
41307
  label.split("").forEach((char, charIndex) => {
41273
- const { width, height, ascent, descent } = TextRun.measureText({
41274
- text: char,
41275
- fontSize: layoutStyle.fontSize
41276
- });
41308
+ const { width, height, ascent, descent } = TextRun.measureText(
41309
+ TextRun.measurePayload(char, layoutStyle)
41310
+ );
41277
41311
  const run = new TextWidget({
41278
41312
  doc: context.doc,
41279
41313
  width,
@@ -41309,10 +41343,9 @@ class DefaultHandler extends WidgetBaseHandler {
41309
41343
  const layoutStyle = this.getLayoutStyle();
41310
41344
  const label = wr.pageWidgetMeta?.type ?? "default";
41311
41345
  label.split("").forEach((char, charIndex) => {
41312
- const { width, height, ascent, descent } = TextRun.measureText({
41313
- text: char,
41314
- fontSize: layoutStyle.fontSize
41315
- });
41346
+ const { width, height, ascent, descent } = TextRun.measureText(
41347
+ TextRun.measurePayload(char, layoutStyle)
41348
+ );
41316
41349
  const run = new TextWidget({
41317
41350
  doc: context.doc,
41318
41351
  width,
@@ -41371,11 +41404,11 @@ class BarcodeHandler extends WidgetBaseHandler {
41371
41404
  const render2 = showValue;
41372
41405
  if (render2) {
41373
41406
  let codeLabel = label ?? "";
41374
- let fontSize2 = DEFAULT_FONT_SIZE;
41375
- const { width: textWidth, height: textHeight } = TextRun.measureText({
41376
- text: codeLabel,
41377
- fontSize: fontSize2
41378
- });
41407
+ const layoutStyle = this.getLayoutStyle();
41408
+ const fontSize2 = layoutStyle.fontSize ?? DEFAULT_FONT_SIZE;
41409
+ const { width: textWidth, height: textHeight } = TextRun.measureText(
41410
+ TextRun.measurePayload(codeLabel, layoutStyle)
41411
+ );
41379
41412
  return {
41380
41413
  render: render2,
41381
41414
  rectWidth: width,
@@ -41498,10 +41531,9 @@ class PaginationHandler extends WidgetBaseHandler {
41498
41531
  total: context.doc.pages.length
41499
41532
  });
41500
41533
  label.split("").forEach((char, charIndex) => {
41501
- const { width, height, ascent, descent } = TextRun.measureText({
41502
- text: char,
41503
- fontSize: layoutStyle.fontSize
41504
- });
41534
+ const { width, height, ascent, descent } = TextRun.measureText(
41535
+ TextRun.measurePayload(char, layoutStyle)
41536
+ );
41505
41537
  const run = new TextWidget({
41506
41538
  doc: context.doc,
41507
41539
  width,
@@ -51271,6 +51303,7 @@ async function initializeDocumentEngine(props, payload, result) {
51271
51303
  ...defaultDataMap,
51272
51304
  ...dataInitMap
51273
51305
  });
51306
+ doc.docRuntimeMeta.handleInfo.initRawDataSnapshot = cloneDeep(doc.dataManager.getRawData());
51274
51307
  const docInfo = snapshotDocInfo(doc);
51275
51308
  return {
51276
51309
  doc,
@@ -51330,6 +51363,7 @@ function useDocumentFactory(props, payload) {
51330
51363
  docInfo.value = snapshotDocInfo(ins);
51331
51364
  };
51332
51365
  docInfo.value = initialized.docInfo;
51366
+ payload.onDocumentLoadSuccess?.(result);
51333
51367
  }
51334
51368
  const hasData = computed(() => docIns.value !== null);
51335
51369
  function clear() {
@@ -56926,7 +56960,7 @@ function useDocOperations(docRef) {
56926
56960
  focus: ids.length ? `root::${firstPage.id}` : null
56927
56961
  });
56928
56962
  }
56929
- function getUnsavedChanges() {
56963
+ function getUnsavedChanges(options) {
56930
56964
  const currentCheckpoint = buildSaveCheckpoint();
56931
56965
  if (!currentCheckpoint) {
56932
56966
  return { dirty: false };
@@ -56935,10 +56969,26 @@ function useDocOperations(docRef) {
56935
56969
  if (!isEqual(currentCheckpoint.currentModel, currentCheckpoint.interfaceModel)) {
56936
56970
  changedScopes.push("model");
56937
56971
  }
56972
+ if (options?.includeRawData) {
56973
+ const doc = getDoc();
56974
+ const baselineRaw = doc?.docRuntimeMeta.handleInfo.initRawDataSnapshot;
56975
+ if (baselineRaw !== void 0 && doc) {
56976
+ const currentRaw = doc.dataManager.getRawData();
56977
+ if (!isEqual(currentRaw, baselineRaw)) {
56978
+ changedScopes.push("rawData");
56979
+ }
56980
+ }
56981
+ }
56938
56982
  return {
56939
56983
  dirty: changedScopes.length > 0
56940
56984
  };
56941
56985
  }
56986
+ function markAsSaved() {
56987
+ const doc = getDoc();
56988
+ if (!doc) return;
56989
+ doc.docRuntimeMeta.handleInfo.initDocModelJson = JSON.stringify(exportModel());
56990
+ doc.docRuntimeMeta.handleInfo.initRawDataSnapshot = cloneDeep(doc.dataManager.getRawData());
56991
+ }
56942
56992
  return {
56943
56993
  validate,
56944
56994
  exportModel,
@@ -56946,7 +56996,8 @@ function useDocOperations(docRef) {
56946
56996
  enterBaseline,
56947
56997
  computeBaselineChanges,
56948
56998
  setAnnotation,
56949
- getUnsavedChanges
56999
+ getUnsavedChanges,
57000
+ markAsSaved
56950
57001
  };
56951
57002
  }
56952
57003
  function useDocController(factory2, ops) {
@@ -56994,6 +57045,12 @@ function useDocController(factory2, ops) {
56994
57045
  rawData() {
56995
57046
  return doc.dataManager.getRawData();
56996
57047
  },
57048
+ setRawData(data, defaultDataMap) {
57049
+ doc.dataManager.setRawData(data, defaultDataMap);
57050
+ },
57051
+ setMultiple(updates) {
57052
+ return doc.dataManager.setMultiple(updates);
57053
+ },
56997
57054
  getSubTableInfoList() {
56998
57055
  return doc.model?.getSubTableInfoList() ?? [];
56999
57056
  },
@@ -57016,8 +57073,11 @@ function useDocController(factory2, ops) {
57016
57073
  computeBaselineChanges(ctx) {
57017
57074
  return ops.computeBaselineChanges(ctx);
57018
57075
  },
57019
- getUnsavedChanges() {
57020
- return ops.getUnsavedChanges();
57076
+ getUnsavedChanges(options) {
57077
+ return ops.getUnsavedChanges(options);
57078
+ },
57079
+ markAsSaved() {
57080
+ ops.markAsSaved();
57021
57081
  },
57022
57082
  setAnnotation(ids, list) {
57023
57083
  ops.setAnnotation(ids, list);
@@ -10,6 +10,11 @@ export interface DocUnsavedChanges {
10
10
  /** 是否存在未保存改动 */
11
11
  dirty: boolean;
12
12
  }
13
+ /** getUnsavedChanges 可选参数 */
14
+ export interface DocGetUnsavedChangesOptions {
15
+ /** 为 true 时除文档模型外,同时与初始化时的 rawData 快照比对 */
16
+ includeRawData?: boolean;
17
+ }
13
18
  export interface DocOperations {
14
19
  /** 校验所有字段,返回错误映射或 null(通过) */
15
20
  validate(): Promise<Record<string, string[]> | null>;
@@ -27,7 +32,9 @@ export interface DocOperations {
27
32
  /** 设置变更批注 */
28
33
  setAnnotation(ids: string[], list: any[]): void;
29
34
  /** 获取当前未保存改动状态,供外部在退出前做拦截 */
30
- getUnsavedChanges(): DocUnsavedChanges;
35
+ getUnsavedChanges(options?: DocGetUnsavedChangesOptions): DocUnsavedChanges;
36
+ /** 标记当前文档为已保存 */
37
+ markAsSaved(): void;
31
38
  }
32
39
  /**
33
40
  * 文档操作组合式函数
@@ -43,6 +43,11 @@ export interface IDocPayload extends IDocTemplatePayload, IDocInstancePayload {
43
43
  /** 是否是预览模式 */
44
44
  isPreview: boolean;
45
45
  ctx: DesignSuiteContext;
46
+ /**
47
+ * 文档接口请求成功且引擎挂载完成后的回调(每轮成功加载触发一次)。
48
+ * 不会在 requestId 为空、请求被丢弃、或初始化抛错时调用。
49
+ */
50
+ onDocumentLoadSuccess?: (result: Execute) => void;
46
51
  }
47
52
  /** 文档加载工厂 — 只负责响应式加载与生命周期,不包含业务操作 */
48
53
  export interface DocumentFactory {
@@ -4,7 +4,8 @@
4
4
  */
5
5
  export { DocModeTypeConst, PageSizeEnumConst, BuiltinComponentTypeConst } from '../../core';
6
6
  export type { DocModeType, CompleteComponentType, BuiltinComponentType } from '../../core';
7
- export type { DocController, DocQueryAPI, WordRuntime, DocInfo, DocRuntimeMeta, DocRuntimeMetaHandleInfo, DocUnsavedChanges, UseWordProps, UseWordOptions, FieldModelQuery, } from '../types';
7
+ export type { DocController, DocQueryAPI, WordRuntime, DocInfo, DocRuntimeMeta, DocRuntimeMetaHandleInfo, DocUnsavedChanges, DocGetUnsavedChangesOptions, UseWordProps, UseWordOptions, FieldModelQuery, } from '../types';
8
+ export type { Execute } from '../doc-runtime/factories/useDocumentFactory';
8
9
  export { useWord } from '../doc-runtime/useWord';
9
10
  export { useDocSuite } from '../doc-runtime/composables/useDocSuite';
10
11
  export { useDocumentFactory } from '../doc-runtime/factories/useDocumentFactory';
@@ -2,11 +2,12 @@ import { ComputedRef, Ref, ShallowRef } from 'vue';
2
2
  import { DocModeType } from '../../core/constants';
3
3
  import { DocModel } from '../../core/model';
4
4
  import { Page } from '../../core';
5
- import { DocBaselineContext, DocUnsavedChanges, DocOperations } from '../doc-runtime/composables/useDocOperations';
5
+ import { DocBaselineContext, DocUnsavedChanges, DocOperations, DocGetUnsavedChangesOptions } from '../doc-runtime/composables/useDocOperations';
6
6
  import { OnlineFormInstanceResponse, OnlineFormTmplResponse } from '@gct-paas/api/apaas';
7
7
  import { FieldChangeItem } from '../../runtime/interface/change-diff';
8
8
  import { FormTmplConfigController } from '../../suites/edhr/panel-schema/data-load/hooks/form-tmpl-config';
9
- export type { DocUnsavedChanges } from '../doc-runtime/composables/useDocOperations';
9
+ import { Execute } from '../doc-runtime/factories/useDocumentFactory';
10
+ export type { DocUnsavedChanges, DocGetUnsavedChangesOptions } from '../doc-runtime/composables/useDocOperations';
10
11
  export type { FieldModelQuery } from './field-model-query';
11
12
  /** 字段权限信息 */
12
13
  export interface FieldPermissionInfo {
@@ -54,6 +55,10 @@ export interface DocControllerMethods extends DocOperations {
54
55
  setModel(model: DocModel): void;
55
56
  /** 获取当前文档原始填报数据快照 */
56
57
  rawData(): Recordable<any>;
58
+ /** 设置当前文档整个原始数据 */
59
+ setRawData(data: Record<string, any>, defaultDataMap?: Record<string, any>): void;
60
+ /** 根据 jsonpath 批量设置文档数据 */
61
+ setMultiple(updates: Record<string, any>): boolean;
57
62
  /** 获取当前文档的所有子表信息 */
58
63
  getSubTableInfoList(): Array<{
59
64
  field: string;
@@ -74,8 +79,10 @@ export interface DocControllerMethods extends DocOperations {
74
79
  enterBaseline(uniqueId: string): DocBaselineContext | null;
75
80
  /** 与基线做差异计算,返回字段变更列表 */
76
81
  computeBaselineChanges(ctx: DocBaselineContext): Promise<FieldChangeItem[]>;
77
- /** 获取当前未保存改动状态,适合外部在退出前做校验 */
78
- getUnsavedChanges(): DocUnsavedChanges;
82
+ /** 获取当前未保存改动状态,适合外部在退出前做校验;特定场景可传入 includeRawData 同时比对填报数据 */
83
+ getUnsavedChanges(options?: DocGetUnsavedChangesOptions): DocUnsavedChanges;
84
+ /** 标记当前文档为已保存 */
85
+ markAsSaved(): void;
79
86
  /** 向指定字段/组件设置批注信息 */
80
87
  setAnnotation(ids: string[], list: any[]): void;
81
88
  }
@@ -119,6 +126,8 @@ export interface DocRuntimeMetaHandleInfo {
119
126
  bpmnFieldAuthMap?: Record<string, any>;
120
127
  /** 初始化的doc model 用来比对用 */
121
128
  initDocModelJson: string;
129
+ /** 初始化后的填报原始数据快照,供 getUnsavedChanges({ includeRawData: true }) 比对 */
130
+ initRawDataSnapshot?: Record<string, any>;
122
131
  }
123
132
  /** 文档运行时元信息(接口返回字段 + 业务拼接字段) */
124
133
  export interface DocRuntimeMeta extends OnlineFormInstanceResponse, OnlineFormTmplResponse {
@@ -174,6 +183,11 @@ export interface UseWordOptions {
174
183
  isDetailPage?: boolean | (() => boolean);
175
184
  /** 指定渲染模式 */
176
185
  renderModeType?: DocModeType;
186
+ /**
187
+ * 文档接口请求成功且引擎挂载完成后的回调(每轮成功加载触发一次)。
188
+ * 不会在 requestId 为空、请求被丢弃、或初始化抛错时调用。
189
+ */
190
+ onDocumentLoadSuccess?: (result: Execute) => void;
177
191
  /** 扩展字段 - 兼容其他未明确定义的负载参数 */
178
192
  [key: string]: any;
179
193
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gct-paas/word",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "GCT 在线 word",
5
5
  "keywords": [
6
6
  "vue",