openyida 2026.8.29 → 2026.8.30

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.
package/README.md CHANGED
@@ -199,6 +199,7 @@ openyida create-process APP_XXX "Purchase Request" .cache/openyida/process/field
199
199
  openyida configure-process APP_XXX FORM_XXX .cache/openyida/process/process.json
200
200
  openyida process preview APP_XXX PROC_INST_XXX --output .cache/openyida/process/process.html
201
201
  openyida data query form APP_XXX FORM_XXX --page 1 --size 20
202
+ openyida data query form APP_XXX FORM_XXX --dynamic-order '{"dateField_xxx":"-"}'
202
203
  openyida data create form APP_XXX FORM_XXX --data-file .cache/openyida/data-import/record.json
203
204
  openyida get-permission APP_XXX FORM_XXX
204
205
  ```
@@ -226,6 +227,7 @@ openyida get-permission APP_XXX FORM_XXX
226
227
  发布成功还必须精确回读同一 PUBLISHED `processId/processVersion` 的 `getProcessById` 平台视图,并验证可见节点的组件、名称、顺序和审批模式。只有输出 `verificationLevel: "PLATFORM_VIEW_VERIFIED"` 才表示平台 view 已验证;`PUBLISHED_UNVERIFIED` 表示发布可能已生效但回读不完整,不能宣称 `processJson` 已验证,也不能直接重放写请求。
227
228
 
228
229
  When creating or updating test data with `openyida data`, Yida date fields must use 13-digit millisecond timestamps, for example `"dateField_xxx": 1719705600000`. Do not submit `YYYY-MM-DD` strings for `DateField` or `CascadeDateField` values.
230
+ For deterministic query order, pass `--dynamic-order '{"fieldId":"+"}'` for ascending order or `--dynamic-order '{"fieldId":"-"}'` for descending order. Without `--dynamic-order`, `searchFormDatas` does not guarantee a stable result order; pagination, comparison, and pairing logic must not depend on the default order.
229
231
  Temporary JSON, CSV, and one-off import scripts should live under `.cache/openyida/` so generated run artifacts do not clutter the repository root.
230
232
 
231
233
  ### Real Environment E2E
@@ -24,7 +24,7 @@ const { readFormMode } = require('../app/services/form-mode-service');
24
24
  const USAGE = `openyida data - Unified Yida data CLI
25
25
 
26
26
  Usage:
27
- openyida data query form <appType> <formUuid> [--page N] [--size N] [--all] [--max-pages N] [--search-json JSON|--search-file .cache/openyida/search.json] [--resolve-aliases] [--inst-id ID] [--no-hydrate-subforms]
27
+ openyida data query form <appType> <formUuid> [--page N] [--size N] [--all] [--max-pages N] [--search-json JSON|--search-file .cache/openyida/search.json] [--dynamic-order '{"fieldId":"+"}'] [--resolve-aliases] [--inst-id ID] [--no-hydrate-subforms]
28
28
  openyida data get form <appType> --inst-id <formInstId> [--form-uuid <formUuid>] [--no-hydrate-subforms]
29
29
  openyida data create form <appType> <formUuid> (--data-json <JSON>|--data-file .cache/openyida/data.json) [--dept-id ID] [--resolve-aliases]
30
30
  说明:若目标表单为流程表单,会自动使用 /v1/process/startInstance.json 发起流程。
@@ -41,6 +41,8 @@ Usage:
41
41
  openyida data query tasks <appType> --type <todo|done|submitted|cc> [--page N] [--size N] [--keyword TEXT] [--process-codes JSON] [--instance-status STATUS]
42
42
 
43
43
  Add --resolve-aliases when JSON keys use Yida component aliases instead of field IDs.
44
+ Use --dynamic-order '{"fieldId":"+"}' for ascending order or '{"fieldId":"-"}' for descending order.
45
+ When --dynamic-order is omitted, the API does not guarantee a stable result order.
44
46
  `;
45
47
 
46
48
  function fail(message) {
package/lib/core/utils.js CHANGED
@@ -1237,6 +1237,26 @@ async function httpPostJson(baseUrl, requestPath, payload, optionsOrLegacyCookie
1237
1237
  });
1238
1238
  }
1239
1239
 
1240
+ function appendQueryParams(requestPath, queryParams) {
1241
+ if (!queryParams) {
1242
+ return requestPath;
1243
+ }
1244
+
1245
+ const querystring = require('querystring');
1246
+ const serialized = querystring.stringify(queryParams);
1247
+ if (!serialized) {
1248
+ return requestPath;
1249
+ }
1250
+
1251
+ if (!requestPath.includes('?')) {
1252
+ return `${requestPath}?${serialized}`;
1253
+ }
1254
+ if (requestPath.endsWith('?') || requestPath.endsWith('&')) {
1255
+ return `${requestPath}${serialized}`;
1256
+ }
1257
+ return `${requestPath}&${serialized}`;
1258
+ }
1259
+
1240
1260
  /**
1241
1261
  * 发送 HTTP GET 请求
1242
1262
  * @param {string} baseUrl
@@ -1248,7 +1268,6 @@ async function httpPostJson(baseUrl, requestPath, payload, optionsOrLegacyCookie
1248
1268
  async function httpGet(baseUrl, requestPath, queryParams, optionsOrLegacyCookies, maybeOptions) {
1249
1269
  const https = require('https');
1250
1270
  const http = require('http');
1251
- const querystring = require('querystring');
1252
1271
  const optionsOverride = resolveRequestOptions(optionsOrLegacyCookies, maybeOptions);
1253
1272
  const authHeaders = await resolveRequestAuthHeaders(optionsOverride);
1254
1273
 
@@ -1256,7 +1275,7 @@ async function httpGet(baseUrl, requestPath, queryParams, optionsOrLegacyCookies
1256
1275
  const parsedUrl = new URL(baseUrl);
1257
1276
  const isHttps = parsedUrl.protocol === 'https:';
1258
1277
  const requestModule = isHttps ? https : http;
1259
- const fullPath = queryParams ? `${requestPath}?${querystring.stringify(queryParams)}` : requestPath;
1278
+ const fullPath = appendQueryParams(requestPath, queryParams);
1260
1279
 
1261
1280
  const options = {
1262
1281
  hostname: parsedUrl.hostname,
@@ -241,6 +241,22 @@ function resolveNodeRefs(value, context) {
241
241
  });
242
242
  }
243
243
 
244
+ function resolveDesignerSourceRefs(value, context) {
245
+ if (typeof value !== 'string') {
246
+ return value;
247
+ }
248
+ return value.replace(/#\{([^}/]+)\/\/([^}]+)\}/g, (match, alias, fieldId) => {
249
+ const nodeId = context.aliasToNodeId.get(alias);
250
+ if (nodeId) {
251
+ return `#{${nodeId}//${fieldId}}`;
252
+ }
253
+ if (context.nodeIdToAlias && context.nodeIdToAlias.has(alias)) {
254
+ return match;
255
+ }
256
+ throw new Error(`Unknown integration spec node alias: ${alias}`);
257
+ });
258
+ }
259
+
244
260
  function normalizeToUsers(value, fallback = []) {
245
261
  const raw = value === undefined ? fallback : value;
246
262
  if (!Array.isArray(raw)) {
@@ -327,7 +343,7 @@ function normalizeAssignments(assignments, context) {
327
343
  valueType: assignment.valueType || 'literal',
328
344
  value: resolveNodeRefs(assignment.value, context),
329
345
  __display: assignment.__display,
330
- __source: assignment.__source ? resolveNodeRefs(assignment.__source, context) : undefined,
346
+ __source: assignment.__source,
331
347
  }));
332
348
  }
333
349
 
@@ -353,6 +369,9 @@ function resolveInitiator(node, context) {
353
369
 
354
370
  function buildUpdateAssignments(assignments, context) {
355
371
  return normalizeAssignments(assignments, context).map((assignment) => {
372
+ const source = assignment.__source
373
+ ? resolveDesignerSourceRefs(assignment.__source, context)
374
+ : undefined;
356
375
  const result = {
357
376
  column: assignment.column,
358
377
  valueType: assignment.valueType,
@@ -362,8 +381,8 @@ function buildUpdateAssignments(assignments, context) {
362
381
  if (assignment.__display) {
363
382
  result.__display = assignment.__display;
364
383
  }
365
- if (assignment.__source) {
366
- result.__source = assignment.__source;
384
+ if (source) {
385
+ result.__source = source;
367
386
  }
368
387
  return result;
369
388
  });
@@ -153,37 +153,6 @@ function buildPieChartSettings() {
153
153
  };
154
154
  }
155
155
 
156
- function buildScatterChartSettings() {
157
- return {
158
- container: { height: 248 },
159
- style: {
160
- pointSize: 4, pointShape: 'circle',
161
- colorType: 'SCHEMA_COLOR', chartColorsMode: 'defaultColorsMode',
162
- customColor: '#5894FF,#394B76,#F7B900,#E55F24,#80D5F5,#9849B0,#3BC88A,#0E869D,#F4A49E,#80563C',
163
- },
164
- axisType: 'hz',
165
- xAxis: {
166
- showXAxis: true, showTitle: false,
167
- title: { type: 'i18n', zh_CN: '', en_US: '' },
168
- line: true, tickLine: true, grid: false, label: true,
169
- },
170
- yAxis: {
171
- showYAxis: true, showTitle: false,
172
- title: { type: 'i18n', zh_CN: '', en_US: '' },
173
- line: false, tickLine: false, grid: true, label: true,
174
- min: null, max: null, tickCount: 5,
175
- },
176
- legend: { showLegend: true, legendPosition: 'top-left', flipPage: true },
177
- tooltip: { showTooltip: true },
178
- };
179
- }
180
-
181
- function buildAreaChartSettings() {
182
- const s = buildLineChartSettings();
183
- s.style.showArea = true;
184
- return s;
185
- }
186
-
187
156
  function buildFunnelChartSettings() {
188
157
  return {
189
158
  container: { height: 248 },
@@ -197,20 +166,6 @@ function buildFunnelChartSettings() {
197
166
  };
198
167
  }
199
168
 
200
- function buildRadarChartSettings() {
201
- return {
202
- container: { height: 248 },
203
- style: {
204
- colorType: 'SCHEMA_COLOR', chartColorsMode: 'defaultColorsMode',
205
- customColor: '#5894FF,#394B76,#F7B900,#E55F24,#80D5F5,#9849B0,#3BC88A,#0E869D,#F4A49E,#80563C',
206
- showArea: true, smooth: false, pointSize: 4, lineWidth: 2,
207
- },
208
- legend: { showLegend: true, legendPosition: 'top-left', flipPage: true },
209
- label: { showLabel: false, fontSize: 12, color: '#000' },
210
- tooltip: { showTooltip: true },
211
- };
212
- }
213
-
214
169
  function buildGaugeChartSettings() {
215
170
  return {
216
171
  container: { height: 248 },
@@ -365,14 +320,6 @@ function buildPivotSettings() {
365
320
  };
366
321
  }
367
322
 
368
- function buildNumberChartSettings() {
369
- return {
370
- container: { height: 120 },
371
- style: { fontSize: 36, color: '#1a1a1a', unit: '', colorType: 'SCHEMA_COLOR' },
372
- tooltip: { showTooltip: false },
373
- };
374
- }
375
-
376
323
  /**
377
324
  * 根据图表类型获取 settings
378
325
  */
@@ -383,16 +330,12 @@ function getChartSettings(chartType) {
383
330
  case 'calendarheatmap': return buildCalendarHeatmapSettings();
384
331
  case 'map': return buildMapSettings();
385
332
  case 'pie': return buildPieChartSettings();
386
- case 'scatter': return buildScatterChartSettings();
387
- case 'area': return buildAreaChartSettings();
388
333
  case 'funnel': return buildFunnelChartSettings();
389
- case 'radar': return buildRadarChartSettings();
390
334
  case 'gauge': return buildGaugeChartSettings();
391
335
  case 'combo': return buildComboChartSettings();
392
336
  case 'table': return buildTableSettings();
393
337
  case 'indicator': return buildIndicatorSettings();
394
338
  case 'pivot': return buildPivotSettings();
395
- case 'number': return buildNumberChartSettings();
396
339
  default: {
397
340
  const error = new Error(`unsupported report chart type: ${String(chartType)}`);
398
341
  error.code = 'REPORT_CHART_TYPE_UNSUPPORTED';
@@ -410,9 +353,6 @@ function buildUserConfig(chartType) {
410
353
  if (chartType === 'funnel') {
411
354
  return { chartType: 'funnel', dataConfig: { xField: [], yField: [] } };
412
355
  }
413
- if (chartType === 'radar') {
414
- return { chartType: 'radar', dataConfig: { xField: [], yField: [], groupField: [] } };
415
- }
416
356
  if (chartType === 'gauge') {
417
357
  return { chartType: 'gauge', dataConfig: { valueField: [], assitValueField: [] } };
418
358
  }
@@ -456,10 +396,7 @@ function buildUserConfig(chartType) {
456
396
  if (chartType === 'pivot') {
457
397
  return { chartType: 'pivot', dataConfig: { columnList: [] } };
458
398
  }
459
- if (chartType === 'number') {
460
- return { chartType: 'number', dataConfig: { valueField: [] } };
461
- }
462
- // 默认:bar/line/scatter/area
399
+ // 默认:bar/line/calendarheatmap
463
400
  return { chartType: chartType || 'bar', dataConfig: { xField: [], yField: [], groupField: [], annotationField: [] } };
464
401
  }
465
402
 
@@ -498,8 +435,8 @@ function buildUserConfigWithFields(chartType) {
498
435
  }];
499
436
  }
500
437
 
501
- // 柱状图 / 折线图 / 面积图 / 散点图 / 雷达图 / 漏斗图
502
- if (['bar', 'line', 'calendarheatmap', 'area', 'scatter', 'radar', 'funnel'].includes(chartType)) {
438
+ // 柱状图 / 折线图 / 日历热力图 / 漏斗图
439
+ if (['bar', 'line', 'calendarheatmap', 'funnel'].includes(chartType)) {
503
440
  const items = [
504
441
  { setterName: 'ColumnFieldSetter', name: 'xField', title: '横轴',
505
442
  setterProps: { single: true, showFormatTab: true, showFormulaEditor: true, showFieldInfo: true, showAggregateTab: false, showDrillTab: true, showEditTab: true, showSortTab: true } },
@@ -608,7 +545,7 @@ function buildMockData(chartType) {
608
545
  },
609
546
  }];
610
547
  }
611
- if (chartType === 'line' || chartType === 'area') {
548
+ if (chartType === 'line') {
612
549
  return [{
613
550
  name: 'chartData',
614
551
  data: {
@@ -662,23 +599,6 @@ function buildMockData(chartType) {
662
599
  },
663
600
  }];
664
601
  }
665
- if (chartType === 'radar') {
666
- return [{
667
- name: 'chartData',
668
- data: {
669
- data: [
670
- { xField: '销售', yField: 80 }, { xField: '管理', yField: 65 },
671
- { xField: '技术', yField: 90 }, { xField: '客服', yField: 70 },
672
- { xField: '研发', yField: 85 },
673
- ],
674
- meta: [
675
- { aliasName: '维度', alias: 'xField', category: 'xField', dataType: 'STRING' },
676
- { aliasName: '数值', alias: 'yField', category: 'yField', dataType: 'NUMBER' },
677
- ],
678
- currentPage: 1, totalCount: 5,
679
- },
680
- }];
681
- }
682
602
  if (chartType === 'gauge') {
683
603
  return [{
684
604
  name: 'chartData',
@@ -1078,16 +998,12 @@ module.exports = {
1078
998
  buildBarChartSettings,
1079
999
  buildLineChartSettings,
1080
1000
  buildPieChartSettings,
1081
- buildScatterChartSettings,
1082
- buildAreaChartSettings,
1083
1001
  buildFunnelChartSettings,
1084
- buildRadarChartSettings,
1085
1002
  buildGaugeChartSettings,
1086
1003
  buildTableSettings,
1087
1004
  buildComboChartSettings,
1088
1005
  buildIndicatorSettings,
1089
1006
  buildPivotSettings,
1090
- buildNumberChartSettings,
1091
1007
  getChartSettings,
1092
1008
  buildUserConfig,
1093
1009
  buildUserConfigWithFields,
@@ -373,7 +373,7 @@ function buildDataSetModelMap(chart, cubeTenantId) {
373
373
  };
374
374
  }
375
375
 
376
- // ── 通用图表(bar/line/pie/funnel/scatter/area)──
376
+ // ── 通用图表(bar/line/pie/funnel/calendarheatmap)──
377
377
  const { model, allFields } = buildDataViewQueryModel(chart, cubeTenantId);
378
378
 
379
379
  const fieldListObjs = allFields.map((f) => buildDisplayFieldObj(cubeCode, f));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "2026.8.29",
3
+ "version": "2026.8.30",
4
4
  "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
6
  "openyida": "bin/yida.js",
@@ -342,7 +342,7 @@ this.utils.yida.getFormDataById({
342
342
  | createTo | String | 否 | 创建时间范围结束,格式 yyyy-MM-dd | `'2024-02-01'` |
343
343
  | modifiedFrom | String | 否 | 修改时间范围起始,格式 yyyy-MM-dd | `'2024-01-01'` |
344
344
  | modifiedTo | String | 否 | 修改时间范围结束,格式 yyyy-MM-dd | `'2024-02-01'` |
345
- | dynamicOrder | String | 否 | 指定排序字段 | `'{"numberField_1ac":"+"}'` |
345
+ | dynamicOrder | String | 否 | 指定排序字段;`+` 升序,`-` 降序。不传时返回顺序无稳定性保证 | `'{"numberField_1ac":"+"}'` |
346
346
 
347
347
  **searchFieldJson 示例**:
348
348
 
@@ -398,6 +398,8 @@ this.utils.yida.getFormDataById({
398
398
  > - **页面内部调用**(即在宜搭自定义页面的 JS 代码中调用 `this.utils.yida.searchFormDatas`):**不需要传 `appType`**,SDK 会自动从当前页面上下文中获取。
399
399
  > - **外部 HTTP 直接调用**(如通过 `openyida data query form` CLI 或服务端脚本):**必须传 `appType`**,即应用的唯一标识(可在宜搭应用 URL 中找到,格式如 `APP_XXXXXXXX`)。
400
400
 
401
+ > 📌 **排序说明**:需要稳定顺序的分页、比对或配对逻辑必须显式传入 `dynamicOrder`。不传时不得依赖接口当前返回顺序;CLI 对应参数为 `--dynamic-order '{"fieldId":"+"}'` 或 `--dynamic-order '{"fieldId":"-"}'`。
402
+
401
403
  **请求示例**:
402
404
 
403
405
  ```javascript
@@ -411,7 +413,7 @@ this.utils.yida.searchFormDatas({
411
413
  createTo: '2024-02-01',
412
414
  modifiedFrom: '2024-01-01',
413
415
  modifiedTo: '2024-02-01',
414
- dynamicOrder: '',
416
+ dynamicOrder: '{"dateField_xxx":"-"}',
415
417
  }).then((res) => {
416
418
  // 兼容两种返回结构
417
419
  var data = (res && res.data) || (res && res.content && res.content.data) || [];
@@ -106,7 +106,7 @@ description: 宜搭数据管理。表单实例/子表/流程实例/任务中心
106
106
  ### 表单实例
107
107
 
108
108
  ```bash
109
- openyida data query form <appType> <formUuid> [--page 1 --size 20] [--search-json '<json>'|--search-file .cache/openyida/<项目名或任务名>/data-import/search.json] [--resolve-aliases]
109
+ openyida data query form <appType> <formUuid> [--page 1 --size 20] [--search-json '<json>'|--search-file .cache/openyida/<项目名或任务名>/data-import/search.json] [--dynamic-order '{"fieldId":"+"}'] [--resolve-aliases]
110
110
  openyida data get form <appType> --inst-id <formInstId>
111
111
  openyida data create form <appType> <formUuid> --data-json '<json>' [--resolve-aliases]
112
112
  openyida data create form <appType> <formUuid> --data-file .cache/openyida/<项目名或任务名>/data-import/record.json [--resolve-aliases]
@@ -258,6 +258,7 @@ openyida data create form APP_xxx FORM-商机表 --data-json '{
258
258
 
259
259
  - `pageSize` 最大 100,QPS 限制约 40 次/秒
260
260
  - `searchFieldJson` 和 `dynamicOrder` 必须传字符串
261
+ - 需要稳定顺序的分页、比对或配对必须显式传 `--dynamic-order '{"fieldId":"+"}'`(升序)或 `--dynamic-order '{"fieldId":"-"}'`(降序);未传时不得依赖默认返回顺序
261
262
  - 字段 ID 通过 `openyida get-schema` 获取,不要手写猜测
262
263
  - 批量脚本可以用 Python `subprocess` 调用 `openyida data ...`,也可以用 JS 复用 Node 工具;脚本必须由结构化文件写入工具创建到 `<projectRoot>/.cache/openyida/<项目名或任务名>/scripts/`,导入数据放在 `<projectRoot>/.cache/openyida/<项目名或任务名>/data-import/`
263
264
 
@@ -302,6 +302,8 @@ openyida integration create APP_XXX FORM-XXX "获取自身后分支更新" \
302
302
  - `fieldId`:字段 ID(可通过 `yida-get-schema` 技能查询)
303
303
  - `ComponentType`:字段组件类型(如 `TextField`、`NumberField`、`SelectField` 等)
304
304
 
305
+ 在结构化 spec 的公式赋值中,`valueType: "column"` 的 `value` 使用 `${alias}.fieldId` 引用上游节点;如果同时提供设计器展示用的 `__source`,使用 `#{alias//fieldId}`。CLI 会把这两种别名引用都替换成真实节点 ID;不要只写 `value` 后假设设计器一定能恢复“值设置”展示。
306
+
305
307
  ## 输出结果
306
308
 
307
309
  命令执行成功后,向 stdout 输出 JSON: