@gct-paas/render 6.0.0-dev.9 → 6.0.3-dev.1

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,7 +1,8 @@
1
1
  /**组件依赖触发器 */
2
- export declare function insetDep({ expression, rowData, }: {
2
+ export declare function insetDep({ expression, rowData, immediate, }: {
3
3
  expression?: string;
4
4
  rowData?: IObject | null;
5
+ immediate?: boolean;
5
6
  }, callback: (value: unknown) => void): void;
6
7
  /**
7
8
  * 直接计算表达式结果,不进行依赖监控
@@ -5,7 +5,7 @@ import { computed } from "vue";
5
5
  import { watchDebounced } from "@vueuse/core";
6
6
  //#region src/Event/Dependency/controller.ts
7
7
  /**组件依赖触发器 */
8
- function insetDep({ expression = "", rowData = null }, callback) {
8
+ function insetDep({ expression = "", rowData = null, immediate = true }, callback) {
9
9
  if (!expression || expression === "true") {
10
10
  callback(true);
11
11
  return;
@@ -40,10 +40,12 @@ function insetDep({ expression = "", rowData = null }, callback) {
40
40
  const { formKey, itemKey } = formCacheMap[k];
41
41
  cache[k] = itemKey ? form[formKey]?.[itemKey] : form[formKey];
42
42
  });
43
- calculate(expression, cache).then(callback);
43
+ calculate(expression, cache).then(callback).catch((err) => {
44
+ console.error("[组件依赖] 表达式计算失败:", expression, cache, err);
45
+ });
44
46
  }
45
- /**当处于行内场景的时候需要立即执行 快速响应 */
46
- if (rowData) playValueByRule(cacheFormFileds.value);
47
+ /**当处于行内场景且需要立即执行时 */
48
+ if (rowData && immediate) playValueByRule(cacheFormFileds.value);
47
49
  /**
48
50
  * 监听收集的依赖
49
51
  */
@@ -51,7 +53,7 @@ function insetDep({ expression = "", rowData = null }, callback) {
51
53
  playValueByRule(cacheFormFileds.value);
52
54
  }, {
53
55
  debounce: 200,
54
- immediate: !rowData
56
+ immediate: immediate && !rowData
55
57
  });
56
58
  }
57
59
  /**
@@ -68,7 +68,9 @@ function playValueByRule(form, formCacheMap, displayRule, callback) {
68
68
  const { formKey, itemKey } = formCacheMap[k];
69
69
  cache[k] = form?.[formKey]?.[itemKey];
70
70
  });
71
- calculate(displayRule, cache).then(callback);
71
+ calculate(displayRule, cache).then(callback).catch((err) => {
72
+ console.error("[显隐规则] 表达式计算失败:", displayRule, cache, err);
73
+ });
72
74
  }
73
75
  /**
74
76
  * 需要做控制的数组组件
@@ -3,7 +3,7 @@ import "../../utils/index.mjs";
3
3
  import "../utils/processRovedInfo.mjs";
4
4
  import { insetDep } from "./controller.mjs";
5
5
  import { ASSIGNMENTSTRATEGY_ENUM, Dependency_ENUM } from "@gct-paas/core";
6
- import { computed, inject, toRef } from "vue";
6
+ import { computed, inject, ref, toRef } from "vue";
7
7
  import { watchDebounced } from "@vueuse/core";
8
8
  //#region src/Event/Dependency/useDependency.ts
9
9
  function useDependency(widget, formState = {}, isRow = false) {
@@ -14,6 +14,17 @@ function useDependency(widget, formState = {}, isRow = false) {
14
14
  if (useProcessFieldEvent) useProcessFieldEvent.useFieldWidget(widget);
15
15
  if (!useProcessFieldEvent && widget.formItem && formReadonly?.value !== void 0) widget.props.readonly = formReadonly?.value || widget.props.readonly;
16
16
  if (!useProcessFieldEvent && !formReadonly?.value) {
17
+ /**
18
+ * readonly 由只读/必填/禁用规则的最新结果共同决定,避免多个异步回调各自回写相互覆写:
19
+ * 只读规则结果覆盖基础 readonly;必填/禁用触发时强制可编辑(readonly=false)
20
+ */
21
+ const readonlyByRule = ref(null);
22
+ const requiredForceEditable = ref(false);
23
+ const disabledForceEditable = ref(false);
24
+ const applyReadonly = () => {
25
+ const base = readonlyByRule.value ?? readonly;
26
+ widget.props.readonly = base && !requiredForceEditable.value && !disabledForceEditable.value;
27
+ };
17
28
  const readonly_expression = configDependency[Dependency_ENUM.READONLY]?.expression;
18
29
  const readonly_field_value = configDependency[Dependency_ENUM.READONLY]?.fieldValue;
19
30
  const readonly_value = configDependency[Dependency_ENUM.READONLY]?.value;
@@ -21,7 +32,8 @@ function useDependency(widget, formState = {}, isRow = false) {
21
32
  expression: readonly_expression,
22
33
  rowData: isRow ? formState : null
23
34
  }, (res) => {
24
- widget.props.readonly = !!res;
35
+ readonlyByRule.value = !!res;
36
+ applyReadonly();
25
37
  });
26
38
  const required_expression = configDependency[Dependency_ENUM.REQUIRED]?.expression;
27
39
  const required_value = configDependency[Dependency_ENUM.REQUIRED]?.value;
@@ -30,8 +42,8 @@ function useDependency(widget, formState = {}, isRow = false) {
30
42
  rowData: isRow ? formState : null
31
43
  }, (res) => {
32
44
  widget.props.required = !!res;
33
- if (res) widget.props.readonly = false;
34
- else widget.props.readonly = readonly;
45
+ requiredForceEditable.value = !!res;
46
+ applyReadonly();
35
47
  });
36
48
  const disabled_expression = configDependency[Dependency_ENUM.DISABLED]?.expression;
37
49
  if (configDependency[Dependency_ENUM.DISABLED]?.value && disabled_expression) insetDep({
@@ -39,16 +51,23 @@ function useDependency(widget, formState = {}, isRow = false) {
39
51
  rowData: isRow ? formState : null
40
52
  }, (res) => {
41
53
  widget.props.disabled = !!res;
42
- if (res) widget.props.readonly = false;
43
- else widget.props.readonly = readonly;
54
+ disabledForceEditable.value = !!res;
55
+ applyReadonly();
44
56
  });
45
57
  }
46
58
  const assignment_expression = configDependency[Dependency_ENUM.ASSIGNMENT]?.expression;
47
59
  const strategy = configDependency[Dependency_ENUM.ASSIGNMENT]?.strategy;
48
- if (assignment_expression) insetDep({
60
+ const isUserModified = ref(false);
61
+ if (strategy === ASSIGNMENTSTRATEGY_ENUM.notCoveredForever) {
62
+ const currentValue = formState[field];
63
+ if (currentValue !== "" && currentValue !== null && currentValue !== void 0) isUserModified.value = true;
64
+ }
65
+ if (assignment_expression && !readonly) insetDep({
49
66
  expression: assignment_expression,
50
- rowData: isRow ? formState : null
67
+ rowData: isRow ? formState : null,
68
+ immediate: false
51
69
  }, (res) => {
70
+ if ((strategy === ASSIGNMENTSTRATEGY_ENUM.notCovered || strategy === ASSIGNMENTSTRATEGY_ENUM.notCoveredForever) && isUserModified.value) return;
52
71
  formState[field] = res;
53
72
  });
54
73
  const fieldKey = isFieldModel ? bindFieldLink?.join(".") : field;
@@ -64,6 +83,9 @@ function useDependency(widget, formState = {}, isRow = false) {
64
83
  },
65
84
  set(val) {
66
85
  if (!!assignment_expression && strategy === ASSIGNMENTSTRATEGY_ENUM.alwaysCover) return;
86
+ if (!!assignment_expression && strategy === ASSIGNMENTSTRATEGY_ENUM.notCovered) if (val === "" || val === null || val === void 0 || val === 0 || val === false) isUserModified.value = false;
87
+ else isUserModified.value = true;
88
+ if (!!assignment_expression && strategy === ASSIGNMENTSTRATEGY_ENUM.notCoveredForever) isUserModified.value = true;
67
89
  formRowData.value[fieldKey] = val;
68
90
  }
69
91
  });
@@ -68,22 +68,28 @@ async function dependencyToShowSync(widget, rowData) {
68
68
  }
69
69
  const { displayType, displayRule } = widget.props || {};
70
70
  const { value, expression } = (widget.props.componentDependency?.configDependency || {})[Dependency_ENUM.HIDDEN] || {};
71
+ /**计算失败时保持 hidden,避免异常导致组件错误显示 */
72
+ const safeCalcHidden = async (exp) => {
73
+ try {
74
+ return await calculateDepResult({
75
+ expression: exp,
76
+ rowData
77
+ });
78
+ } catch (err) {
79
+ console.error("[组件依赖] 显隐规则表达式计算失败:", exp, err);
80
+ return true;
81
+ }
82
+ };
71
83
  if (value && expression) {
72
84
  widget.props.hidden = true;
73
- widget.props.hidden = !!await calculateDepResult({
74
- expression,
75
- rowData
76
- });
85
+ widget.props.hidden = await !!safeCalcHidden(expression);
77
86
  } else if (value)
78
87
  /**开启组件依赖没有配置隐藏条件 */
79
88
  widget.props.hidden = true;
80
89
  else if (displayType === DisplayType.RULE && displayRule) {
81
90
  /**老数据显示隐藏配置兼容 */
82
91
  widget.props.hidden = true;
83
- widget.props.hidden = !!await calculateDepResult({
84
- expression: displayRule,
85
- rowData
86
- });
92
+ widget.props.hidden = await !!safeCalcHidden(displayRule);
87
93
  }
88
94
  return !widget.props.hidden;
89
95
  }
@@ -4,6 +4,7 @@ import "../utils/index.mjs";
4
4
  import { $httpBizService, $request, BizService } from "./bizServiceRequest.mjs";
5
5
  import { globalLoading } from "./utils/globalLoading.mjs";
6
6
  import { ProcessAppRovedData } from "./utils/processRovedInfo.mjs";
7
+ import { ContextExtensionRegistry } from "./context-extension-registry.mjs";
7
8
  import { FormComponents, INNER_EVENT, t } from "@gct-paas/core";
8
9
  import dayjs from "dayjs";
9
10
  import { toRaw } from "vue";
@@ -12,6 +13,7 @@ import * as _ from "lodash-es";
12
13
  import { cloneDeep, differenceBy } from "lodash-es";
13
14
  import BigNumberJS from "bignumber.js";
14
15
  //#region src/Event/baseEvent.ts
16
+ var contextExtensionRegistry = new ContextExtensionRegistry();
15
17
  var Events = class {
16
18
  context;
17
19
  searchVNodes;
@@ -45,6 +47,7 @@ var Events = class {
45
47
  $ref: this.getComponent.bind(this),
46
48
  $asyncRef: this.getSyncComponent.bind(this)
47
49
  });
50
+ contextExtensionRegistry.apply(this.context);
48
51
  this.#createFunction(js);
49
52
  Globals.setContextByKey(this.context, pageKey);
50
53
  this.pageKey = pageKey;
@@ -0,0 +1,43 @@
1
+ import { Context } from './baseEvent';
2
+ /** 上下文扩展方法 */
3
+ export type ContextExtension = {
4
+ bivarianceHack(context: Context, ...args: unknown[]): unknown;
5
+ }['bivarianceHack'];
6
+ /** 上下文扩展方法映射 */
7
+ export type ContextExtensionMethods = Record<string, unknown>;
8
+ /**
9
+ * 上下文扩展注册中心。
10
+ *
11
+ * 注册数据由核心包统一维护,端包和应用层仅通过继承补充保留键策略。
12
+ */
13
+ export declare class ContextExtensionRegistry {
14
+ private static readonly extensions;
15
+ /**
16
+ * 注册套件提供的上下文扩展方法。
17
+ *
18
+ * @param suiteKey 套件标识
19
+ * @param methods 扩展方法映射
20
+ */
21
+ static register(suiteKey: string, methods: ContextExtensionMethods): void;
22
+ /**
23
+ * 将已注册扩展挂载到指定上下文。
24
+ *
25
+ * @param context 当前页面上下文
26
+ */
27
+ apply(context: Context): void;
28
+ /**
29
+ * 判断方法名是否为当前层级的内置上下文方法。
30
+ *
31
+ * @param context 当前页面上下文
32
+ * @param methodName 上下文方法名
33
+ * @returns 是否为保留方法
34
+ */
35
+ protected isReservedKey(context: Context, methodName: string): boolean;
36
+ }
37
+ /**
38
+ * 注册套件上下文扩展。
39
+ *
40
+ * @param suiteKey 套件标识
41
+ * @param methods 扩展方法映射
42
+ */
43
+ export declare function registerContextExtension(suiteKey: string, methods: ContextExtensionMethods): void;
@@ -0,0 +1,67 @@
1
+ //#region src/Event/context-extension-registry.ts
2
+ /**
3
+ * 上下文扩展注册中心。
4
+ *
5
+ * 注册数据由核心包统一维护,端包和应用层仅通过继承补充保留键策略。
6
+ */
7
+ var ContextExtensionRegistry = class ContextExtensionRegistry {
8
+ static extensions = /* @__PURE__ */ new Map();
9
+ /**
10
+ * 注册套件提供的上下文扩展方法。
11
+ *
12
+ * @param suiteKey 套件标识
13
+ * @param methods 扩展方法映射
14
+ */
15
+ static register(suiteKey, methods) {
16
+ Object.entries(methods).forEach(([methodName, method]) => {
17
+ if (typeof method !== "function") {
18
+ console.error(`套件 ${suiteKey} 注册上下文扩展 ${methodName} 失败:扩展值必须是函数`);
19
+ return;
20
+ }
21
+ const previous = this.extensions.get(methodName);
22
+ if (previous) {
23
+ const duplicateType = previous.suiteKey === suiteKey ? "同一套件重复" : "套件间";
24
+ console.warn(`${duplicateType}注册上下文扩展 ${methodName}:${previous.suiteKey} 将被 ${suiteKey} 覆盖`);
25
+ }
26
+ this.extensions.set(methodName, {
27
+ suiteKey,
28
+ method
29
+ });
30
+ });
31
+ }
32
+ /**
33
+ * 将已注册扩展挂载到指定上下文。
34
+ *
35
+ * @param context 当前页面上下文
36
+ */
37
+ apply(context) {
38
+ ContextExtensionRegistry.extensions.forEach(({ suiteKey, method }, methodName) => {
39
+ if (this.isReservedKey(context, methodName)) {
40
+ console.error(`套件 ${suiteKey} 的上下文扩展 ${methodName} 与内置上下文方法冲突,已跳过挂载`);
41
+ return;
42
+ }
43
+ context[methodName] = (...args) => method(context, ...args);
44
+ });
45
+ }
46
+ /**
47
+ * 判断方法名是否为当前层级的内置上下文方法。
48
+ *
49
+ * @param context 当前页面上下文
50
+ * @param methodName 上下文方法名
51
+ * @returns 是否为保留方法
52
+ */
53
+ isReservedKey(context, methodName) {
54
+ return methodName in context;
55
+ }
56
+ };
57
+ /**
58
+ * 注册套件上下文扩展。
59
+ *
60
+ * @param suiteKey 套件标识
61
+ * @param methods 扩展方法映射
62
+ */
63
+ function registerContextExtension(suiteKey, methods) {
64
+ ContextExtensionRegistry.register(suiteKey, methods);
65
+ }
66
+ //#endregion
67
+ export { ContextExtensionRegistry, registerContextExtension };
@@ -1,5 +1,6 @@
1
1
  export { Globals, pageGlobaVariables, globalVarCaches, formMap, setFormData, pageDataforJson, getPageTitle, getPremission, PageTypeEnum, } from './utils/runGlobalByPage';
2
2
  export { Events, Context, GctComponent, type ModalInstance } from './baseEvent';
3
+ export { ContextExtensionRegistry, registerContextExtension, type ContextExtension, type ContextExtensionMethods, } from './context-extension-registry';
3
4
  export * from './eventType';
4
5
  export * from './Dependency/useDependencyToShow';
5
6
  export * from './Dependency/useDependency';
@@ -1,5 +1,6 @@
1
1
  import "./utils/appRedis.mjs";
2
2
  import "./utils/runGlobalByPage.mjs";
3
+ import "./context-extension-registry.mjs";
3
4
  import "./baseEvent.mjs";
4
5
  import "./eventType.mjs";
5
6
  import "./Dependency/controller.mjs";
@@ -128,6 +128,7 @@ export declare const pageDataforJson: import('vue').Ref<{
128
128
  unitType?: "px" | "%" | undefined;
129
129
  pageWidth?: number | undefined;
130
130
  independentDetailPage?: boolean | undefined;
131
+ detailPageCustom?: boolean | undefined;
131
132
  } | undefined;
132
133
  pageName?: string | undefined;
133
134
  }, {
@@ -244,6 +245,7 @@ export declare const pageDataforJson: import('vue').Ref<{
244
245
  unitType?: "px" | "%" | undefined;
245
246
  pageWidth?: number | undefined;
246
247
  independentDetailPage?: boolean | undefined;
248
+ detailPageCustom?: boolean | undefined;
247
249
  } | undefined;
248
250
  pageName?: string | undefined;
249
251
  } | {
package/es/index.mjs CHANGED
@@ -26,10 +26,11 @@ import { cacheAdapter } from "./utils/cacheAdapter.mjs";
26
26
  import { getRefInfoId } from "./utils/get-ref-data.mjs";
27
27
  import { useGetBodyBySearch } from "./utils/search/search-hooks.mjs";
28
28
  import { defaultValMap } from "./utils/search/search-default-value.mjs";
29
- import { getIExp, getIKeywordFieldKeys, getQueryDateByKeyWord, getQuerySort, useQueryfilter } from "./utils/search/listhook.mjs";
29
+ import { getIExp, getIKeywordFieldKeys, getQueryDateByKeyWord, getQuerySort, getSimilarFieldBySearchField, useQueryfilter } from "./utils/search/listhook.mjs";
30
30
  import { is, isArray, isBoolean, isDate, isDef, isElement, isEmpty, isEmptyStr, isFunction, isMap, isNull, isNullAndUnDef, isNullOrUnDef, isNumber, isObject, isPromise, isRegExp, isString, isUnDef, isUrl, isWindow } from "./utils/is.mjs";
31
31
  import { emptyValueDisplay } from "./utils/field-empty-value.mjs";
32
32
  import "./utils/index.mjs";
33
+ import { ContextExtensionRegistry, registerContextExtension } from "./Event/context-extension-registry.mjs";
33
34
  import { Context, Events, GctComponent } from "./Event/baseEvent.mjs";
34
35
  import { calculateDepResult, insetDep } from "./Event/Dependency/controller.mjs";
35
36
  import { getOptionsByDisplayRule, useDisplayRuleColumnByStyles, useDisplayRuleOptions, useVisibileByRuleHook } from "./Event/Dependency/displayRule.mjs";
@@ -53,4 +54,4 @@ function onInit() {
53
54
  }
54
55
  onInit();
55
56
  //#endregion
56
- export { Context, ControllerType, DESIGN_DATA_KEY_TAG, DESIGN_TYPE, DateFormat, DateRangeMap, DatepickerRanges, DesignItemAttribute, DesignNodeType, DesignRender, DesignRenderViewPrefix, Events, FieldSchema, GctComponent, GlobaAppInfo, Globals, HandwritingPad_default as HandwritingPad, PageTypeEnum, RenderDesignEditor, RenderNodeRegister, RenderNodeType, RenderPluginPgkUtil, RenderRegister, Vue3DndDraggable, Vue3DndDraggableItem, addDataByForm, cacheAdapter, calculate, calculateDepResult, containerNodeProps, defaultValMap, dependencyToShow, dependencyToShowSync, emptyValueDisplay, formMap, getDefaultDate, getDisabledDate, getIExp, getIKeywordFieldKeys, getMobileDateRange, getOptionsByDisplayRule, getPageTitle, getPremission, getQueryDateByKeyWord, getQuerySort, getRefInfoId, globalVarCaches, identify, initFieldWidgetRuntime, insetDep, is, isArray, isBoolean, isDate, isDef, isElement, isEmpty, isEmptyStr, isFunction, isMap, isNull, isNullAndUnDef, isNullOrUnDef, isNumber, isObject, isPromise, isRegExp, isString, isUnDef, isUrl, isWindow, nodeEditorProps, nodeProps, onInit, pageDataforJson, pageGlobaVariables, renderSetupApp, schemaToStyle, setDataByForm, setFormData, tableWidgetByDept, tableWidgetToShow, transSelectData, transformData, transformDataToDict, transformPropsField, transformSourceData, useAsyncFileAttrs, useDependency, useDependencyByRequired, useDependencyToShow, useDependencyToShowList, useDesignRenderController, useDisplayRuleColumnByStyles, useDisplayRuleOptions, useGctSelect, useGetBodyBySearch, useQueryfilter, useStyle, useVisibileByRuleHook, widthRenderDesignEditorInstall };
57
+ export { Context, ContextExtensionRegistry, ControllerType, DESIGN_DATA_KEY_TAG, DESIGN_TYPE, DateFormat, DateRangeMap, DatepickerRanges, DesignItemAttribute, DesignNodeType, DesignRender, DesignRenderViewPrefix, Events, FieldSchema, GctComponent, GlobaAppInfo, Globals, HandwritingPad_default as HandwritingPad, PageTypeEnum, RenderDesignEditor, RenderNodeRegister, RenderNodeType, RenderPluginPgkUtil, RenderRegister, Vue3DndDraggable, Vue3DndDraggableItem, addDataByForm, cacheAdapter, calculate, calculateDepResult, containerNodeProps, defaultValMap, dependencyToShow, dependencyToShowSync, emptyValueDisplay, formMap, getDefaultDate, getDisabledDate, getIExp, getIKeywordFieldKeys, getMobileDateRange, getOptionsByDisplayRule, getPageTitle, getPremission, getQueryDateByKeyWord, getQuerySort, getRefInfoId, getSimilarFieldBySearchField, globalVarCaches, identify, initFieldWidgetRuntime, insetDep, is, isArray, isBoolean, isDate, isDef, isElement, isEmpty, isEmptyStr, isFunction, isMap, isNull, isNullAndUnDef, isNullOrUnDef, isNumber, isObject, isPromise, isRegExp, isString, isUnDef, isUrl, isWindow, nodeEditorProps, nodeProps, onInit, pageDataforJson, pageGlobaVariables, registerContextExtension, renderSetupApp, schemaToStyle, setDataByForm, setFormData, tableWidgetByDept, tableWidgetToShow, transSelectData, transformData, transformDataToDict, transformPropsField, transformSourceData, useAsyncFileAttrs, useDependency, useDependencyByRequired, useDependencyToShow, useDependencyToShowList, useDesignRenderController, useDisplayRuleColumnByStyles, useDisplayRuleOptions, useGctSelect, useGetBodyBySearch, useQueryfilter, useStyle, useVisibileByRuleHook, widthRenderDesignEditorInstall };
@@ -45,7 +45,7 @@ export declare class RenderRegister {
45
45
  * @param {string} [prefix]
46
46
  * @return {*} {(Component | undefined)}
47
47
  */
48
- get(tag: string, prefix?: string): Component | undefined;
48
+ get(tag: string, prefix?: string, isSilent?: boolean): Component | undefined;
49
49
  /**
50
50
  * 组件标识清单
51
51
  *
@@ -56,10 +56,12 @@ var RenderRegister = class {
56
56
  * @param {string} [prefix]
57
57
  * @return {*} {(Component | undefined)}
58
58
  */
59
- get(tag, prefix) {
59
+ get(tag, prefix, isSilent = false) {
60
60
  const fullTag = prefix ? `${prefix}:${tag}` : tag;
61
61
  const component = this.map.get(fullTag);
62
- if (!component) console.warn(`未找到组件,标识: ${fullTag}`);
62
+ if (!component) {
63
+ if (!isSilent) console.warn(`未找到组件,标识: ${fullTag}`);
64
+ }
63
65
  return component;
64
66
  }
65
67
  /**
package/es/setup-app.mjs CHANGED
@@ -1,6 +1,12 @@
1
1
  import { registerRenderLocale } from "./locale/auto-register.mjs";
2
2
  //#region src/setup-app.ts
3
+ var renderSetupAppKey = Symbol.for("@gct-paas/render/setupApp");
3
4
  async function renderSetupApp(_app) {
5
+ if (Reflect.get(window, renderSetupAppKey)) {
6
+ console.error("@gct-paas/render renderSetupApp 已执行,请勿重复调用。");
7
+ return;
8
+ }
9
+ Reflect.set(window, renderSetupAppKey, true);
4
10
  registerRenderLocale();
5
11
  }
6
12
  //#endregion
@@ -1,4 +1,16 @@
1
1
  import { UseMemoizeReturn } from '@vueuse/core';
2
+ /**
3
+ * 运行时注册单个公式函数执行器(第三方/业务方在预览态/运行态调用)
4
+ * @param fnName 函数名,必须与公式编辑器注册时的 id 完全一致(大小写敏感);
5
+ * 与内置函数(methods.ts)重名时内置优先,扩展不会覆盖平台行为
6
+ * @param executor 执行体,支持同步/async 函数(引擎按 await 调用)
7
+ */
8
+ export declare function registerExpressionMethods(fnName: string, executor: (...args: any[]) => any): void;
9
+ /**
10
+ * 按函数名卸载已注册的执行器;不传参数则清空全部
11
+ * @param names 函数名列表
12
+ */
13
+ export declare function unregisterExpressionMethods(...names: string[]): void;
2
14
  export declare const identify: UseMemoizeReturn<string[], [expr: string, isAll?: boolean | undefined]>;
3
15
  export declare const calculate: UseMemoizeReturn<Promise<IObject | number>, [
4
16
  expr: string,
@@ -5,7 +5,30 @@ import * as esprima from "esprima-next";
5
5
  import estraverse from "estraverse";
6
6
  import escodegen from "escodegen";
7
7
  //#region src/utils/expression/index.ts
8
- window._METHOD_DEBUG_ = methods_exports;
8
+ /**
9
+ * 运行时注册的业务函数执行器。
10
+ * 公式编辑器注册的业务函数(options.functions)只随定义携带可选的 execute,
11
+ * 表达式持久化仅保存函数名;执行体必须在会执行表达式的应用启动时注册到这里。
12
+ */
13
+ var extensionMethods = {};
14
+ Object.defineProperty(window, "_METHOD_DEBUG_", {
15
+ configurable: true,
16
+ enumerable: true,
17
+ get: () => ({
18
+ ...extensionMethods,
19
+ ...methods_exports
20
+ })
21
+ });
22
+ /**
23
+ * 运行时注册单个公式函数执行器(第三方/业务方在预览态/运行态调用)
24
+ * @param fnName 函数名,必须与公式编辑器注册时的 id 完全一致(大小写敏感);
25
+ * 与内置函数(methods.ts)重名时内置优先,扩展不会覆盖平台行为
26
+ * @param executor 执行体,支持同步/async 函数(引擎按 await 调用)
27
+ */
28
+ function registerExpressionMethods(fnName, executor) {
29
+ if (typeof fnName === "string" && fnName && typeof executor === "function") extensionMethods[fnName] = executor;
30
+ else console.warn("[Expression] registerExpressionMethods 参数非法:fnName 需为非空字符串,executor 需为函数");
31
+ }
9
32
  var identify = useMemoize(_identify);
10
33
  var calculate = useMemoize(_calc);
11
34
  function _deepCalcName(node) {
@@ -75,12 +98,15 @@ async function _calc(expr, values) {
75
98
  });
76
99
  const calcFunc = new AsyncFunction("Methods", [
77
100
  ...defs,
78
- `return new Promise(async(resolve)=>{`,
101
+ `return (async () => {`,
79
102
  `const res = ${runjs};`,
80
- "resolve(res);",
81
- "})"
103
+ "return res;",
104
+ "})()"
82
105
  ].join("\n"));
83
- const result = await calcFunc(methods_exports);
106
+ const result = await calcFunc({
107
+ ...extensionMethods,
108
+ ...methods_exports
109
+ });
84
110
  console.info("【表达式】", expr);
85
111
  console.info("【参数】", values);
86
112
  console.info("【执行语句】", calcFunc.toString());
@@ -111,7 +137,9 @@ function _identify(expr, isAll = false) {
111
137
  }
112
138
  window.gct_expression = {
113
139
  execute: calculate,
114
- identify
140
+ identify,
141
+ /** 控制台快速注册执行体做临时验证:gct_expression.register('MY_FN', v => ...) */
142
+ register: registerExpressionMethods
115
143
  };
116
144
  //#endregion
117
145
  export { calculate, identify };
@@ -41,9 +41,12 @@ declare function SMALL(v: number[], n: number): number | undefined;
41
41
  declare function AVERAGE(...args: (number | '')[]): number | '';
42
42
  declare function ABS(v: number): number;
43
43
  declare function MAXABS(...args: number[]): number | '';
44
+ declare function BANKROUND(v: number | string, n: number): number | '';
44
45
  declare function MOD(v: number, n: number): number | string;
45
46
  declare function POWER(v: number, n: number): number;
46
47
  declare function SQRT(v: number): number;
48
+ declare function EXP(v: number): number;
49
+ declare function LN(v: number): number;
47
50
  declare function GET<T extends object | IObject[]>(v: T, p: number | keyof T): T[keyof T];
48
51
  declare function PUT<T extends object>(a: T, b: keyof T, c: unknown): T;
49
52
  declare function PUSH<T>(v: T[], n: T): T[];
@@ -51,6 +54,8 @@ declare function HEADPUSH<T>(v: T[], n: T): T[];
51
54
  declare function TIMESTAMP2DATE(v: number): string | undefined;
52
55
  declare function DATE2TIMESTAMP(v: string): number | undefined;
53
56
  declare function DATEFORMAT(v: Date, f: string): string | undefined;
57
+ declare function DateAdd(v: string, num: number, unit: dayjs.ManipulateType, format?: string): string | undefined;
58
+ declare function DateSubtract(v: string, num: number, unit: dayjs.ManipulateType, format?: string): string | undefined;
54
59
  declare function NOW(): string;
55
60
  declare function TODAY(): string;
56
61
  declare function YEAR(v: dayjs.ConfigType): number;
@@ -73,8 +78,8 @@ declare function SEQMAP<K extends string | number | symbol, V>(...args: (K | V)[
73
78
  declare function SUMSQ(...args: (string | '')[]): number | string;
74
79
  /**数组计数 */
75
80
  declare function COUNT(v: number[]): number | '';
76
- /**标准差函数 */
77
- declare function STDEV(...args: number[]): number | '';
81
+ /**标准差函数,支持任意个参数,参数可为数组或非数组,空值不参与计算 */
82
+ declare function STDEV(...args: unknown[]): number | '';
78
83
  /** RSQ函数 */
79
84
  declare function RSQ(arr1: number[], arr2: number[]): number | '';
80
- export { IF, ISEMPTY, ISNULL, ISUNDEFINED, AND, OR, EQ, NE, LE, LT, GE, GT, LEN, CONCAT, SUBSTRING, SUBSTR, UPPER, LOWER, TRIM, LTRIM, RTRIM, REPEAT, REPLACE, FINDSTR, SEARCHSTR, PARSENUMBER, SPLIT, SUM, ADD, REDUCE, MULTIPLICATION, DIVISION, FIXED, ROUND, ROUNDUP, MAX, LARGE, MIN, SMALL, AVERAGE, ABS, MAXABS, MOD, POWER, SQRT, GET, PUT, PUSH, HEADPUSH, TIMESTAMP2DATE, DATE2TIMESTAMP, DATEFORMAT, NOW, TODAY, YEAR, MONTH, DAY, HOUR, MINUTE, WEEKRANGE, LASTWEEKRANGE, MONTHRANGE, LASTMONTHRANGE, YEARRANGE, LASTYEARRANGE, QUARTER, LASTQUARTER, ISDATERANGE, ISTIMERANGE, TUPLE, SEQMAP, SUMSQ, COUNT, STDEV, RSQ, };
85
+ export { IF, ISEMPTY, ISNULL, ISUNDEFINED, AND, OR, EQ, NE, LE, LT, GE, GT, LEN, CONCAT, SUBSTRING, SUBSTR, UPPER, LOWER, TRIM, LTRIM, RTRIM, REPEAT, REPLACE, FINDSTR, SEARCHSTR, PARSENUMBER, SPLIT, SUM, ADD, REDUCE, MULTIPLICATION, DIVISION, FIXED, ROUND, ROUNDUP, MAX, LARGE, MIN, SMALL, AVERAGE, ABS, MAXABS, MOD, POWER, SQRT, EXP, LN, GET, PUT, PUSH, HEADPUSH, TIMESTAMP2DATE, DATE2TIMESTAMP, DATEFORMAT, DateAdd, DateSubtract, NOW, TODAY, YEAR, MONTH, DAY, HOUR, MINUTE, WEEKRANGE, LASTWEEKRANGE, MONTHRANGE, LASTMONTHRANGE, YEARRANGE, LASTYEARRANGE, QUARTER, LASTQUARTER, ISDATERANGE, ISTIMERANGE, TUPLE, SEQMAP, SUMSQ, COUNT, STDEV, RSQ, BANKROUND, };
@@ -13,13 +13,17 @@ var methods_exports = /* @__PURE__ */ __exportAll({
13
13
  ADD: () => ADD,
14
14
  AND: () => AND,
15
15
  AVERAGE: () => AVERAGE,
16
+ BANKROUND: () => BANKROUND,
16
17
  CONCAT: () => CONCAT,
17
18
  COUNT: () => COUNT,
18
19
  DATE2TIMESTAMP: () => DATE2TIMESTAMP,
19
20
  DATEFORMAT: () => DATEFORMAT,
20
21
  DAY: () => DAY,
21
22
  DIVISION: () => DIVISION,
23
+ DateAdd: () => DateAdd,
24
+ DateSubtract: () => DateSubtract,
22
25
  EQ: () => EQ,
26
+ EXP: () => EXP,
23
27
  FINDSTR: () => FINDSTR,
24
28
  FIXED: () => FIXED,
25
29
  GE: () => GE,
@@ -40,6 +44,7 @@ var methods_exports = /* @__PURE__ */ __exportAll({
40
44
  LASTYEARRANGE: () => LASTYEARRANGE,
41
45
  LE: () => LE,
42
46
  LEN: () => LEN,
47
+ LN: () => LN,
43
48
  LOWER: () => LOWER,
44
49
  LT: () => LT,
45
50
  LTRIM: () => LTRIM,
@@ -332,6 +337,12 @@ function MAXABS(...args) {
332
337
  const fn = () => v.reduce((prev, current) => Math.abs(Number(current)) > Math.abs(Number(prev)) ? current : prev);
333
338
  return fn();
334
339
  }
340
+ function BANKROUND(v, n) {
341
+ if (v === null || v === "" || v === void 0) return "";
342
+ if (n < 0) throw new Error("Decimal places should be non-negative.");
343
+ n = n ? parseInt(n + "") : 0;
344
+ return new BigNumberJS(v).decimalPlaces(n, BigNumberJS.ROUND_HALF_EVEN).toNumber();
345
+ }
335
346
  function MOD(v, n) {
336
347
  const fn = () => {
337
348
  if (v === 0) throw new Error("The second value should not be zero.");
@@ -373,6 +384,22 @@ function SQRT(v) {
373
384
  };
374
385
  return fn();
375
386
  }
387
+ function EXP(v) {
388
+ const fn = () => {
389
+ const n = toSafeNumber(v);
390
+ if (n === null) return NaN;
391
+ return Math.exp(n);
392
+ };
393
+ return fn();
394
+ }
395
+ function LN(v) {
396
+ const fn = () => {
397
+ const n = toSafeNumber(v);
398
+ if (n === null || n <= 0) throw new Error("The value must be greater than zero.");
399
+ return Math.log(n);
400
+ };
401
+ return fn();
402
+ }
376
403
  function GET(v, p) {
377
404
  const fn = () => {
378
405
  if (Array.isArray(v)) if (typeof p === "number") return v[p];
@@ -429,6 +456,35 @@ function DATEFORMAT(v, f) {
429
456
  const fn = () => dayjs(v).format(f);
430
457
  return fn();
431
458
  }
459
+ function _detectDateType(v) {
460
+ if (/^\d{2}:\d{2}(:\d{2})?$/.test(v.trim())) return "time";
461
+ if (/^\d{4}-\d{2}-\d{2}$/.test(v.trim())) return "date";
462
+ return "datetime";
463
+ }
464
+ function _getDefaultFormat(type) {
465
+ if (type === "date") return "YYYY-MM-DD";
466
+ if (type === "time") return "HH:mm:ss";
467
+ return "YYYY-MM-DD HH:mm:ss";
468
+ }
469
+ var TIME_BASE_DATE = "2000-01-01 ";
470
+ function DateAdd(v, num, unit, format) {
471
+ if (!v) return;
472
+ const fn = () => {
473
+ const type = _detectDateType(v);
474
+ const fmt = format ?? _getDefaultFormat(type);
475
+ return dayjs(type === "time" ? TIME_BASE_DATE + v.trim() : v).add(num, unit).format(fmt);
476
+ };
477
+ return fn();
478
+ }
479
+ function DateSubtract(v, num, unit, format) {
480
+ if (!v) return;
481
+ const fn = () => {
482
+ const type = _detectDateType(v);
483
+ const fmt = format ?? _getDefaultFormat(type);
484
+ return dayjs(type === "time" ? TIME_BASE_DATE + v.trim() : v).subtract(num, unit).format(fmt);
485
+ };
486
+ return fn();
487
+ }
432
488
  function NOW() {
433
489
  return dayjs().format("YYYY-MM-DD HH:MM:ss");
434
490
  }
@@ -561,9 +617,9 @@ function COUNT(v) {
561
617
  if (!Array.isArray(v)) return "";
562
618
  return v.length;
563
619
  }
564
- /**标准差函数 */
620
+ /**标准差函数,支持任意个参数,参数可为数组或非数组,空值不参与计算 */
565
621
  function STDEV(...args) {
566
- const data = args.flat().map((v) => Number(v)).filter((v) => Number.isFinite(v));
622
+ const data = args.flat().filter((i) => i !== "" && i !== null && i !== void 0).map((v) => Number(v)).filter((v) => Number.isFinite(v));
567
623
  if (data.length < 2) return "";
568
624
  const mean = data.reduce((sum, val) => sum + val, 0) / data.length;
569
625
  const variance = data.reduce((sum, val) => {
@@ -21,3 +21,11 @@ export declare const useQueryfilter: (datafilter: IObject[] | IObject) => {
21
21
  * @returns
22
22
  */
23
23
  export declare const getQuerySort: ({ collation, collationSort, collationField, }: IObject) => any;
24
+ /**
25
+ * 关键字搜索时,取配置的搜索字段的第一段作为 similarField 交给后端做匹配度排序
26
+ * 使用方:下拉/对象选择的关键字搜索、搜索选择组件(select-search)
27
+ * 注意:与「查询组件」的 similarField(取第一个有值字段)来源不同,勿混用
28
+ * @param searchField 配置的搜索字段
29
+ * @param keyword 搜索的关键字
30
+ */
31
+ export declare function getSimilarFieldBySearchField(searchField?: string[], keyword?: string): string | undefined;