openyida 2026.10.23-1 → 2026.10.23

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
@@ -534,7 +534,7 @@ When preparing a PR that needs a personal Fork, or when you ask how to support t
534
534
  | Command | Description |
535
535
  |---------|-------------|
536
536
  | `openyida data <query\|get\|create\|update\|transfer> <resource> ... \| delete form <appType> <formUuid> --inst-id <id> --expect-form-name <name> --expect-form-type receipt --confirm [--json]` | Unified data management (form/process/task/subform) |
537
- | `openyida task-center <todo\|created\|done\|cc\|proxy\|capabilities\|link\|scaffold\|options\|inspect\|count> [--spec <file.json> --output <file.canvas.jsx>] [options]` | Global task center (todo/processed/cc etc.) |
537
+ | `openyida task-center <todo\|created\|done\|cc\|proxy\|capabilities\|scaffold\|options\|inspect\|count> [--spec <file.json> --output <file.canvas.jsx>] [options]` | Global task center (todo/processed/cc etc.) |
538
538
  | `openyida basic-info <overview\|commodity\|grant\|capacity\|quota\|abs-path\|dataflow\|i18n\|domain>` | Query organization basic info, capacity, quotas, and domain settings |
539
539
  | `openyida read-dingtalk-doc <docUrl> [--output <file>] [--json]` | Fetch Markdown content from a DingTalk document |
540
540
  | `openyida read-dingtalk-tingji <taskUuid> [--json]` | Fetch DingTalk Tingji details by task UUID |
@@ -10,16 +10,7 @@ const {
10
10
  findEmojiInValue,
11
11
  } = require('../../core/no-emoji-detector');
12
12
 
13
- const { createPageNavigation } = require('../../navigation/page-navigation');
14
- const { createTaskCenterContract } = require('../../task-center/contract');
15
- const { createTaskCenterLoader } = require('../../task-center/loader');
16
- const { createTaskCenterNavigation } = require('../../task-center/navigation');
17
-
18
- const CANVAS_YIDA_API_BRIDGE_SOURCE = `${createPageNavigation.toString()}
19
- ${createTaskCenterNavigation.toString()}
20
- ${createTaskCenterContract.toString()}
21
- ${createTaskCenterLoader.toString()}
22
- var OPENYIDA_YIDA_FORM_API_METHODS = [
13
+ const CANVAS_YIDA_API_BRIDGE_SOURCE = `var OPENYIDA_YIDA_FORM_API_METHODS = [
23
14
  'saveFormData',
24
15
  'updateFormData',
25
16
  'searchFormDataIds',
@@ -248,15 +239,8 @@ function openyidaCreateRouterBridge(utils) {
248
239
 
249
240
  function openyidaCreateUtilsBridge(context, yidaBridge) {
250
241
  var utils = context && context.utils;
251
- var navigation = createPageNavigation(window);
252
- var taskNavigation = createTaskCenterNavigation();
253
- var taskCounts = createTaskCenterLoader(createTaskCenterContract(), function() { return yidaBridge; });
254
242
  var methodNames = openyidaCollectUtilsMethodNames(utils);
255
243
  function invoke(methodName) {
256
- if (methodName === 'openPage') { return navigation.open.apply(null, Array.prototype.slice.call(arguments, 1)); }
257
- if (['taskCenterCount', 'taskCenterLink', 'openTaskCenter'].indexOf(methodName) !== -1) {
258
- return bridge[methodName].apply(null, Array.prototype.slice.call(arguments, 1));
259
- }
260
244
  if (!utils || typeof utils[methodName] !== 'function') {
261
245
  throw new Error('this.utils.' + methodName + ' is not available');
262
246
  }
@@ -267,8 +251,6 @@ function openyidaCreateUtilsBridge(context, yidaBridge) {
267
251
  apiGroups: {
268
252
  utility: OPENYIDA_UTIL_API_METHODS.slice(),
269
253
  router: ['push'],
270
- navigation: ['openPage', 'taskCenterLink', 'openTaskCenter'],
271
- tasks: ['taskCenterCount'],
272
254
  yida: OPENYIDA_KNOWN_YIDA_API_METHODS.slice()
273
255
  },
274
256
  knownMethods: OPENYIDA_UTIL_API_METHODS.slice(),
@@ -277,33 +259,9 @@ function openyidaCreateUtilsBridge(context, yidaBridge) {
277
259
  return !utils || typeof utils[methodName] !== 'function';
278
260
  }),
279
261
  invoke: invoke,
280
- openPage: navigation.open,
281
- taskCenterLink: function(options) {
282
- return taskNavigation.target(Object.assign({ baseUrl: window.location.origin }, options));
283
- },
284
- taskCenterCount: async function(options) {
285
- if (!options || typeof options !== 'object' || Array.isArray(options)
286
- || Object.keys(options).some(function(key) { return ['scope', 'appType', 'kind', 'filters'].indexOf(key) === -1; })) {
287
- var error = new Error('TASK_FILTER_INVALID: options');
288
- error.code = 'TASK_FILTER_INVALID';
289
- throw error;
290
- }
291
- var target = taskNavigation.target({ scope: options.scope, appType: options.appType, kind: options.kind, baseUrl: window.location.origin });
292
- var filters = createTaskCenterContract().normalizeFilters(target.kind, options.filters === undefined ? {} : options.filters, 'app-query');
293
- return taskCounts.count(target.kind, Object.assign({}, filters, { transport: 'app-query' },
294
- target.scope === 'corp' ? { scope: 'corp' } : { appType: target.appType }));
295
- },
296
- openTaskCenter: function(options) {
297
- var target = taskNavigation.target(Object.assign({ baseUrl: window.location.origin }, options));
298
- return navigation.open(target.pageUrl, { mode: target.mode });
299
- },
300
262
  router: openyidaCreateRouterBridge(utils),
301
263
  yida: yidaBridge
302
264
  };
303
- ['openPage', 'taskCenterLink', 'openTaskCenter', 'taskCenterCount'].forEach(function(methodName) {
304
- if (bridge.availableMethods.indexOf(methodName) === -1) { bridge.availableMethods.push(methodName); }
305
- });
306
- bridge.missingKnownMethods = bridge.missingKnownMethods.filter(function(name) { return name !== 'openPage'; });
307
265
  methodNames.forEach(function(methodName) {
308
266
  if (!bridge[methodName]) {
309
267
  bridge[methodName] = function() {
@@ -683,7 +683,7 @@ const COMMAND_SIDE_EFFECTS = new Map([
683
683
  ['task-center', sideEffect('mixed', {
684
684
  mutates_yida: false,
685
685
  mutates_local: true,
686
- read_actions: ['todo', 'created', 'done', 'cc', 'proxy', 'capabilities', 'link', 'options', 'inspect', 'count'],
686
+ read_actions: ['todo', 'created', 'done', 'cc', 'proxy', 'capabilities', 'options', 'inspect', 'count'],
687
687
  mutating_actions: ['scaffold'],
688
688
  })],
689
689
  ]);
@@ -1821,11 +1821,10 @@ const COMMAND_GROUPS = [
1821
1821
  'openyida data transfer task APP_XXX --process-inst-id PINST_XXX --form-uuid FORM_XXX --to-user-id USER_ID --expect-form-name "Approval form" --expect-form-type process --json',
1822
1822
  ],
1823
1823
  }),
1824
- command('task-center', ['task-center'], 'task-center <todo|created|done|cc|proxy|capabilities|link|scaffold|options|inspect|count> [--spec <file.json> --output <file.canvas.jsx>] [options]', 'help.cmd_task_center', {
1824
+ command('task-center', ['task-center'], 'task-center <todo|created|done|cc|proxy|capabilities|scaffold|options|inspect|count> [--spec <file.json> --output <file.canvas.jsx>] [options]', 'help.cmd_task_center', {
1825
1825
  requiresLogin: false,
1826
1826
  managedRun: { scope: 'actions', actions: {
1827
1827
  capabilities: { scope: 'local', allowedFlags: ['--json'] },
1828
- link: { scope: 'local', allowedFlags: ['--scope', '--type', '--base-url', '--mode', '--json'] },
1829
1828
  scaffold: { scope: 'local' },
1830
1829
  options: { scope: 'app', appPosition: 1 },
1831
1830
  inspect: { scope: 'app', appPosition: 1 },
@@ -1834,16 +1833,13 @@ const COMMAND_GROUPS = [
1834
1833
  output: 'json',
1835
1834
  examples: [
1836
1835
  'openyida task-center capabilities --json',
1837
- 'openyida task-center link APP_XXX --scope app --type todo --base-url https://tenant.aliwork.com --json',
1838
- 'openyida task-center link --scope corp --type cc --base-url https://tenant.aliwork.com --json',
1839
- 'openyida task-center count --scope corp --type created --json',
1840
1836
  'openyida task-center options APP_XXX --type originators --keyword 张 --json',
1841
1837
  'openyida task-center inspect APP_XXX --process-inst-id INSTANCE_REPLACE --form-uuid FORM_REPLACE --json',
1842
1838
  'openyida task-center inspect APP_XXX --process-inst-id INSTANCE_REPLACE --history --task-id TASK_REPLACE --json',
1843
1839
  'openyida task-center count APP_XXX --type todo --transport app-query --filters-json \'{"keyword":"采购"}\' --json',
1844
1840
  'openyida task-center scaffold --spec workflow-spec.json --output project/pages/src/progress.canvas.jsx',
1845
1841
  ],
1846
- notes: ['link generates a native app or corporation task-center pageUrl and DingTalk AppLink without opening a browser or writing platform resources. --scope app|corp is required; app requires appType, corp rejects appType. --base-url allows offline URL generation; otherwise the active auth environment supplies the origin. --mode defaults to auto: browser pages stay in the browser and DingTalk pages use native openLink with ddtab=true; generating a link cannot detect a future page visitor environment. Explicit dingtalk mode adds ddtab=true. count --scope corp uses app-query with appKeys=[]; explicit --scope app defaults to app-query, while legacy count without --scope retains app-v1 compatibility. Scope must be clarified with the user when ambiguous. Published utilities expose openPage(url, {mode: auto|dingtalk|browser}), taskCenterLink and openTaskCenter. DingTalk mode never silently falls back to a browser. capabilities describes application task kinds, filters, lookup options, workflow read models and scaffold specs. inspect <appType> --process-inst-id ID [--history] [--task-id ID] reads authorized process progress and optional history; taskResult matches a task, without asserting visitor ownership. count <appType> --type KIND [--transport app-query] [--filters-json JSON] reads one list record and its server total; unknown totals stay null. Neither read command requires a custom-page choice. The tasks scaffold selects fields, filterFields and optional expandProcess; the progress view exports ProcessProgressSection for a real linked instance. options <appType> --type originators|processes|forms --keyword TEXT --size N --json reads current-visitor lookup candidates. scaffold creates a new local Canvas page only after an explicit custom-page request or affirmative ask_human choice, recorded as customPage.source and confirmed=true. A generic task-center request or Fast mode does not provide that choice. Pages use the current visitor and existing application theme; CLI credentials are never embedded. Global queries remain unavailable in application-bound managed runs.'],
1842
+ notes: ['capabilities describes application task kinds, filters, lookup options, workflow read models and scaffold specs. inspect <appType> --process-inst-id ID [--history] [--task-id ID] reads authorized process progress and optional history; taskResult matches a task, without asserting visitor ownership. count <appType> --type KIND [--transport app-query] [--filters-json JSON] reads one list record and its server total; unknown totals stay null. Neither read command requires a custom-page choice. The tasks scaffold selects fields, filterFields and optional expandProcess; the progress view exports ProcessProgressSection for a real linked instance. options <appType> --type originators|processes|forms --keyword TEXT --size N --json reads current-visitor lookup candidates. scaffold creates a new local Canvas page only after an explicit custom-page request or affirmative ask_human choice, recorded as customPage.source and confirmed=true. A generic task-center request or Fast mode does not provide that choice. Pages use the current visitor and existing application theme; CLI credentials are never embedded. Global queries remain unavailable in application-bound managed runs.'],
1847
1843
  }),
1848
1844
  command('basic-info', ['basic-info'], 'basic-info <overview|commodity|grant|capacity|quota|abs-path|dataflow|i18n|domain>', 'help.cmd_basic_info', { managedRun: { scope: 'denied', reason: 'scope_not_audited' },
1849
1845
  output: 'json',
@@ -50,13 +50,11 @@ Usage:
50
50
  --keyword TEXT 搜索关键词
51
51
  --no-detail 不返回详情(仅 created 类型支持)
52
52
 
53
- openyida task-center link [appType] --scope app|corp --type todo|created|done|cc [--base-url URL] [--mode dingtalk|auto|browser] --json
54
- openyida task-center count --scope corp --type todo|created|done|cc [--filters-json JSON] --json
55
53
  openyida task-center capabilities --json
56
54
  openyida task-center scaffold --spec <file.json> --output <file.canvas.jsx>
57
55
  openyida task-center options <appType> --type <originators|processes|forms> [--keyword TEXT] [--size N] --json
58
56
  openyida task-center inspect <appType> --process-inst-id <id> [--form-uuid <id>] [--history] [--task-id <id>] --json
59
- openyida task-center count <appType> [--scope app] --type <todo|created|done|cc> [--transport app-v1|app-query] [--filters-json <json>] --json
57
+ openyida task-center count <appType> --type <todo|created|done|cc> [--transport app-v1|app-query] [--filters-json <json>] --json
60
58
  `;
61
59
 
62
60
  // ── 工具函数 ──────────────────────────────────────────
@@ -219,26 +217,6 @@ async function run(args) {
219
217
  return result;
220
218
  }
221
219
 
222
- if (type === 'link') {
223
- const allowed = ['scope', 'type', 'base_url', 'mode', 'json'];
224
- const invalid = Object.keys(options).find(key => !allowed.includes(key));
225
- if (invalid || positionals.length > 2 || options.json !== undefined && options.json !== true) {
226
- fail(t('task_center.invalid_spec', invalid || 'arguments'));
227
- }
228
- if (!['app', 'corp'].includes(options.scope)) { fail(t('task_center.invalid_spec', 'scope')); }
229
- const baseUrl = options.base_url === undefined ? (await ensureSession()).baseUrl : options.base_url;
230
- const target = require('../task-center/navigation').target({
231
- scope: options.scope, kind: options.type, baseUrl, mode: options.mode,
232
- ...(positionals[1] !== undefined ? { appType: positionals[1] } : {}),
233
- });
234
- const nativeTarget = require('../task-center/navigation').target({ scope: options.scope, kind: options.type, baseUrl, mode: 'dingtalk',
235
- ...(positionals[1] !== undefined ? { appType: positionals[1] } : {}) });
236
- const dingtalkUrl = require('../dingtalk/dingtalk-link').buildDingTalkPageLink({ url: nativeTarget.pageUrl });
237
- const result = { ...target, dingtalkUrl, opened: false };
238
- console.log(JSON.stringify(result, null, 2));
239
- return result;
240
- }
241
-
242
220
  if (type === 'options') {
243
221
  const invalid = Object.keys(options).find(key => !['type', 'keyword', 'size', 'json'].includes(key));
244
222
  if (invalid || positionals.length !== 2) { fail(t('task_center.invalid_spec', invalid || 'arguments')); }
@@ -256,9 +234,9 @@ async function run(args) {
256
234
  }
257
235
 
258
236
  if (type === 'inspect' || type === 'count') {
259
- const allowed = type === 'inspect' ? ['process_inst_id', 'form_uuid', 'task_id', 'history', 'json'] : ['type', 'transport', 'filters_json', 'scope', 'json'];
237
+ const allowed = type === 'inspect' ? ['process_inst_id', 'form_uuid', 'task_id', 'history', 'json'] : ['type', 'transport', 'filters_json', 'json'];
260
238
  const invalid = Object.keys(options).find(key => !allowed.includes(key));
261
- if (invalid || (positionals.length !== 2 && !(type === 'count' && options.scope === 'corp' && positionals.length === 1)) || options.json !== undefined && options.json !== true || options.history !== undefined && options.history !== true) {
239
+ if (invalid || positionals.length !== 2 || options.json !== undefined && options.json !== true || options.history !== undefined && options.history !== true) {
262
240
  fail(t('task_center.invalid_spec', invalid || 'arguments'));
263
241
  }
264
242
  const tasks = require('../task-center/contract');
@@ -273,13 +251,7 @@ async function run(args) {
273
251
  if (options.filters_json !== undefined) {
274
252
  try { filters = JSON.parse(options.filters_json); } catch { fail(t('task_center.invalid_spec', 'filters-json')); }
275
253
  }
276
- if (options.scope !== undefined && !['app', 'corp'].includes(options.scope)) { fail(t('task_center.invalid_spec', 'scope')); }
277
- if (options.scope === 'corp' && positionals[1] !== undefined) { fail(t('task_center.invalid_spec', 'appType')); }
278
- const transport = options.transport || (options.scope !== undefined ? 'app-query' : undefined);
279
- input = { ...tasks.normalizeFilters(options.type, filters, transport),
280
- ...(options.scope === 'corp' ? { scope: 'corp' } : { appType: tasks.appOf(positionals[1]) }),
281
- transport: tasks.transportOf(transport) };
282
- if (input.scope === 'corp' && input.transport !== 'app-query') { fail(t('task_center.invalid_spec', 'transport')); }
254
+ input = { ...tasks.normalizeFilters(options.type, filters, options.transport), appType: tasks.appOf(positionals[1]), transport: tasks.transportOf(options.transport) };
283
255
  }
284
256
  const client = createYidaClient({ authRef: await ensureSession() });
285
257
  const request = descriptor => client.get(descriptor.path, { _api: 'nattyFetch', _mock: 'false', ...descriptor.params });
@@ -46,12 +46,8 @@ function navigateCanvasPage(entry, context = {}) {
46
46
  }
47
47
  const href = buildCanvasPageUrl(entry, context);
48
48
  const utils = context.utils || window.__OPENYIDA_UTILS__;
49
- if (entry.openMode === 'dingtalk' || entry.openMode === 'new-tab' || entry.targetType === 'url') {
50
- if (typeof utils?.openPage === 'function') return entry.openMode === 'dingtalk'
51
- ? utils.openPage(href, { mode: 'dingtalk' }) : utils.openPage(href);
52
- if (entry.openMode === 'dingtalk' || /dingtalk/i.test(window.navigator?.userAgent || '')) {
53
- throw new Error('DINGTALK_API_UNAVAILABLE');
54
- }
49
+ if (entry.openMode === 'new-tab' || entry.targetType === 'url') {
50
+ if (typeof utils?.openPage === 'function') return utils.openPage({ url: href });
55
51
  window.open(href, '_blank', 'noopener,noreferrer');
56
52
  return;
57
53
  }
@@ -119,31 +119,6 @@ function createTaskCenterContract() {
119
119
  return { kind: canonical, transport, method: def.method, endpoint: def.endpoint,
120
120
  path: '/dingtalk/web/' + params.appType + '/v1/' + def.endpoint + '.json', params };
121
121
  }
122
- function corpCountRequest(kind, options) {
123
- const canonical = kindOf(kind);
124
- if (!options || options.scope !== 'corp' || options.appType !== undefined || options.transport !== 'app-query') {
125
- fail('TASK_FILTER_INVALID', 'scope');
126
- }
127
- const filters = { ...options };
128
- delete filters.scope;
129
- delete filters.transport;
130
- const normalizedFilters = normalizeFilters(canonical, filters, 'app-query');
131
- return { kind: canonical, transport: 'app-query', path: '/query/task/' + queryDefinitions[canonical].endpoint + '.json',
132
- params: { _api: 'nattyFetch', _mock: 'false', ...(canonical === 'todo' ? { taskQueryType: 1 } : {}),
133
- query: JSON.stringify({ page: 1, limit: 1, ...normalizedFilters, appKeys: [] }) }, filters: normalizedFilters };
134
- }
135
- function corpCount(payload, kind, filters) {
136
- checkFailure(payload);
137
- const page = Object.prototype.hasOwnProperty.call(payload, 'content') ? payload.content : payload;
138
- checkFailure(page);
139
- if (!Array.isArray(page.values)) { fail('TASK_RESPONSE_INVALID', 'values'); }
140
- let total = null;
141
- if (page.totalCount !== undefined && page.totalCount !== null) {
142
- if (!/^\d+$/.test(String(page.totalCount)) || !Number.isSafeInteger(Number(page.totalCount))) { fail('TASK_RESPONSE_INVALID', 'totalCount'); }
143
- total = Number(page.totalCount);
144
- }
145
- return { appType: null, kind: kindOf(kind), total, available: total !== null, basis: 'server-total', scope: 'current-visitor-in-corp', filters };
146
- }
147
122
  function text(value) {
148
123
  let parsed = value;
149
124
  if (typeof value === 'string' && /^\s*\{/.test(value)) {
@@ -268,7 +243,7 @@ function createTaskCenterContract() {
268
243
  identity: 'current-visitor', detail: 'native-page', theme: 'existing-application',
269
244
  };
270
245
  }
271
- return { kindOf, appOf, transportOf, filterSchema, normalizeFilters, request, corpCountRequest, corpCount, normalize, capabilities, text, outcome, summary, checkFailure };
246
+ return { kindOf, appOf, transportOf, filterSchema, normalizeFilters, request, normalize, capabilities, text, outcome, summary, checkFailure };
272
247
  }
273
248
 
274
249
  module.exports = { createTaskCenterContract, ...createTaskCenterContract() };
@@ -50,11 +50,6 @@ function createTaskCenterLoader(contract, resolveBridge, requestJson) {
50
50
  error.code = 'TASK_FILTER_UNSUPPORTED';
51
51
  throw error;
52
52
  }
53
- if (options.scope === 'corp') {
54
- const request = contract.corpCountRequest(kind, options);
55
- const payload = await (requestJson || fetchPage)(request);
56
- return contract.corpCount(payload, kind, request.filters);
57
- }
58
53
  const canonical = contract.kindOf(kind);
59
54
  const { appType, transport, ...filters } = options;
60
55
  const normalizedFilters = contract.normalizeFilters(canonical, filters, transport);
@@ -19,12 +19,6 @@ function fail(code, field) {
19
19
  function capabilities() {
20
20
  return {
21
21
  ...contract.capabilities(),
22
- navigation: {
23
- command: 'task-center link [appType] --scope app|corp --type todo|created|done|cc [--base-url URL] [--mode dingtalk|auto|browser] --json',
24
- scopes: ['app', 'corp'], scopeRequired: true, defaultMode: 'auto',
25
- runtime: 'window.__OPENYIDA_UTILS__.openTaskCenter({ scope, appType (app only), kind, mode })',
26
- opensFromCli: false,
27
- },
28
22
  lookups: {
29
23
  command: 'task-center options <appType> --type <originators|processes|forms> [--keyword TEXT] [--size N] --json',
30
24
  kinds: ['originators', 'processes', 'forms'],
@@ -36,9 +30,6 @@ function capabilities() {
36
30
  count: 'task-center count <appType> --type todo|created|done|cc [--transport app-v1|app-query] [--filters-json JSON] --json',
37
31
  scope: 'current-visitor-visible-process', historyDefault: false,
38
32
  results: { personal: 'personalResult on done tasks', process: 'processResult', task: 'taskResult matched by taskId; not proof of current-visitor ownership' },
39
- runtimeCount: 'window.__OPENYIDA_UTILS__.taskCenterCount({ scope, appType (app only), kind, filters })',
40
- scopedCount: 'task-center count [appType] --scope app|corp --type todo|created|done|cc --json',
41
- corpCount: 'task-center count --scope corp --type todo|created|done|cc [--filters-json JSON] --json',
42
33
  countBasis: 'server-total', unknownCount: null, historyFailure: 'explicit-partial-error',
43
34
  },
44
35
  scaffold: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openyida",
3
- "version": "2026.10.23-1",
3
+ "version": "2026.10.23",
4
4
  "description": "OpenYida CLI - 宜搭低代码 AI 开发工具(安装即用,零配置)",
5
5
  "bin": {
6
6
  "openyida": "bin/yida.js",
@@ -1109,9 +1109,7 @@ export function didMount() {
1109
1109
 
1110
1110
  ### openPage
1111
1111
 
1112
- **描述**:打开新页面。原生 `this.utils.openPage(url)` 的参数为 URL 字符串。
1113
-
1114
- Canvas 页面使用 OpenYida 发布桥 `window.__OPENYIDA_UTILS__.openPage(url)`,默认跟随当前浏览器或钉钉环境;浏览器正常打开页面,钉钉内添加 `ddtab=true` 并调用端内接口。只有明确要求严格端内打开时才传 `{mode:'dingtalk'}`。`mode` 是发布桥的能力;调用原生同名函数时按其原生签名传参。待办数量与入口使用发布桥的 `taskCenterCount / taskCenterLink / openTaskCenter`,参见[个人任务能力](../skills/yida-canvas-data-binding/references/workflow-capabilities.md)。
1112
+ **描述**:打开新页面。在钉钉环境下会使用钉钉 API 打开,体验更友好。
1115
1113
 
1116
1114
  ### router.push
1117
1115
 
@@ -9,8 +9,6 @@
9
9
  - `isRenderNav=false` 表示导航展示配置,不证明当前页面位于 iframe。不要仅凭该参数修改 `window.top.location`;嵌入方式、跨域和宿主能力需分别验证。
10
10
  - 宜搭应用壳根据 `navUuid/formUuid` 和 `workbench` 路径维护选中态;同应用页面应进入 `/{appType}/workbench/{formUuid}` 或对应壳层路由。
11
11
  - 发布层会把根级工具注册到 `window.__OPENYIDA_UTILS__`;可用时优先通过 `window.__OPENYIDA_UTILS__.router.push` 做应用内跳转,通过 `window.__OPENYIDA_UTILS__.openPage` 打开外部链接或新窗口场景。
12
- - `openPage(url, { mode: 'auto' })` 接收 URL 字符串;发布桥兼容旧 `{url}` 参数,但新代码统一传字符串。`auto` 在钉钉内添加 `ddtab=true` 并调用原生接口,失败报错;浏览器环境沿用浏览器打开。`mode:'dingtalk'` 要求钉钉环境并禁止失败后回退系统浏览器,`mode:'browser'` 才显式打开浏览器。调用返回 Promise,点击事件必须捕获并展示错误。
13
- - 待办卡片使用 `utils.taskCenterCount / taskCenterLink / openTaskCenter`,范围与 CLI 对齐,详见[数量卡片与原生入口](../../yida-canvas-data-binding/references/workflow-capabilities.md)。范围不明先 `ask_human`;不要自行拼跨应用入口或扫描 DOM。
14
12
  - `openPage` / `window.open` 更适合外部链接、新标签、钉钉链接、文件预览等场景;同应用页面默认在当前应用壳内切换。
15
13
  - 页面隐藏应用导航后,页面内自绘导航壳接管跨视图切换;导航可见时使用平台导航承载同级页面切换。
16
14
  - 自绘应用导航按 PRD 安排数量、顺序、分组和任务入口,通常工作台在首位;当前用户的 `getAccessableNavs.json` 结果只用于过滤不可见入口。数据片段和接入规则见 [导航数据来源](../../yida-nav-shell/references/nav-shell-patterns.md#导航数据来源)。
@@ -7,7 +7,7 @@ description: 为 YidaCodeCanvas 自定义内容接入真实表单、个人流程
7
7
 
8
8
  ## 核心定位
9
9
 
10
- 先根据用户描述确定“待办”指什么:对象是个人流程任务还是业务记录,与当前访问者有什么关系,需要哪些信息、筛选、刷新及点击行为。页面、区块、卡片或“待办”名称都不能代替业务含义;只接入需求涉及的能力,不默认增加四类栏目、统计或审批按钮。关键语义无法从上下文确定时,只澄清影响数据来源或权限范围的问题。 个人任务的当前应用或本组织所有应用范围不明确时,必须通过 `ask_human` 确认;数量与原生入口使用同一范围。数量卡片及端内入口复用[流程任务能力](references/workflow-capabilities.md)中的 CLI 与发布桥,不手拼另一套请求或打开逻辑。
10
+ 先根据用户描述确定“待办”指什么:对象是个人流程任务还是业务记录,与当前访问者有什么关系,需要哪些信息、筛选、刷新及点击行为。页面、区块、卡片或“待办”名称都不能代替业务含义;只接入需求涉及的能力,不默认增加四类栏目、统计或审批按钮。关键语义无法从上下文确定时,只澄清影响数据来源或权限范围的问题。
11
11
 
12
12
  个人流程任务复用 CLI 的 `todo`(待我处理)、`created`(我创建的普通表单与流程)、`done`(我已处理)、`cc`(抄送我的)契约;先用 `openyida task-center capabilities --json` 查询条件,再按所需类型执行 `openyida data query tasks <appType> --type <类型> --normalized` 核查。需要加载实现时复用 CLI 装配产物中的任务加载与详情助手,保留当前展示布局;发布运行态读取实际访问者的任务,不把 CLI 登录人的结果或凭证嵌入页面。订单待发货、工单待处理等业务记录仍按真实表单字段和权限查询,不用流程任务接口代替。
13
13
 
@@ -2,34 +2,6 @@
2
2
 
3
3
  按业务含义选能力,再沿用当前布局。首页的本人待办使用个人任务查询;采购单详情的审批进度使用该业务记录关联的流程实例;订单待发货等业务状态仍查询业务表单。只读查询不要求选择自定义实现,也不创建页面。新增自定义展示仍须用户显式要求或经 ask_human 明确同意。
4
4
 
5
- ## 数量卡片与原生入口
6
-
7
- 范围不明确时先 `ask_human` 确认当前应用或本组织所有应用;数量和入口共用同一范围。只需卡片数量与原生入口时,复用下列能力,不必装配自定义任务列表。
8
-
9
- ```bash
10
- openyida task-center count APP_REPLACE --scope app --type todo --json
11
- openyida task-center count --scope corp --type todo --json
12
- openyida task-center link APP_REPLACE --scope app --type todo --base-url https://example.aliwork.com --json
13
- openyida task-center link --scope corp --type cc --base-url https://example.aliwork.com --json
14
- ```
15
-
16
- `link` 只生成可检查的原生地址,`opened=false`;不打开本机浏览器。默认 `mode=auto`,生成普通页面 URL,由页面运行时按访问环境选择打开方式;CLI 本身不推断目标访客环境。显式 `--mode dingtalk` 时 URL 含 `ddtab=true`。另返回 `dingtalkUrl`,仅在用户明确要求从浏览器唤起钉钉时绑定为跨端点击入口。传 `--base-url` 可离线生成;否则读取当前登录组织。受应用约束的 managed run 不允许组织范围查询或覆盖组织域名,不绕过该约束。
17
-
18
- 页面发布层提供以下共用接口(必须用包含此能力的 OpenYida 编译发布):
19
-
20
- ```js
21
- const utils = window.__OPENYIDA_UTILS__;
22
- const selection = { scope: 'app', appType: 'APP_REPLACE', kind: 'todo' };
23
- const count = await utils.taskCenterCount(selection);
24
- // count.available=false 时显示“未知”;请求报错显示失败,不能显示成 0。
25
- await utils.openTaskCenter(selection); // 默认跟随浏览器/钉钉环境;放在点击事件中并捕获错误
26
- // 组织范围:{ scope: 'corp', kind: 'todo' },不要传 appType。
27
- ```
28
-
29
- `taskCenterCount` 固定同源 `app-query`、实际访问者身份与服务端总数,只读一页一条;可选 `filters` 与 CLI app-query 支持的条件一致,不扫描全历史或嵌入 CLI 身份。`taskCenterLink(selection)` 可读取同一地址。应用范围固定 `appKeys=[appType]`,组织范围固定 `appKeys=[]`。旧版不带 scope 的 CLI count 保留原 transport 默认值;新卡片应显式指定 scope,使 CLI 核查与页面采用同一口径。
30
-
31
- `openTaskCenter` 默认 `mode:'auto'`:浏览器内直接打开浏览器新页签,钉钉内添加唯一 `ddtab=true` 并调用端内打开接口。浏览器默认打开不能因缺少钉钉环境而报错,也不应默认唤起钉钉。钉钉内接口缺失、调用失败或超时仍须报错,不静默跨到系统浏览器。显式 `mode:'dingtalk'` 是仅限端内的严格模式;跨端唤起用用户明确选择的 `dingtalkUrl` 入口。`mode:'browser'` 显式使用浏览器打开方式。不得用浏览器验证结果宣称钉钉新窗口或免登已验收。四类入口分别使用应用系统导航或平台任务中心原生页签,无需猜测 DOM。
32
-
33
5
  ## CLI 能力与读取
34
6
 
35
7
  先执行 `openyida task-center capabilities --json`。固定接口、范围校验和返回格式由 CLI 维护,不在页面重新实现请求。以下标识均为待替换占位符,实例必须来自真实业务关联,不能按标题猜测。
@@ -50,7 +22,7 @@ openyida task-center inspect APP_REPLACE --process-inst-id INSTANCE_REPLACE --hi
50
22
 
51
23
  - 个人任务仍为 `todo / created / done / cc`;四类条件和人员、流程、表单候选见[任务筛选示例](../../yida-task-center/references/task-filters.md)。不支持的条件直接报错,不静默删条件。
52
24
  - 如果业务需要“我发起且仍在审批中的报销”,而个人任务接口不支持该状态条件,先确认真实流程表单,再选择既有 `data query process` 的服务端状态和发起人条件,并明确实例查询口径。运行态使用实际访问者身份,不能把 CLI 登录人的 ID 固化到页面;不在加载器中自动换接口。
53
- - `count.total` 是所选范围、访问者及所传条件下的服务端总数;`available=false / total=null` 表示未知。不是当前页长度,也不是全历史审批数量。“我创建的”包含普通表单与流程,不能先排除普通表单再把原总数称作审批数量。
25
+ - `count.total` 是当前应用、访问者及所传条件下的服务端总数;`available=false / total=null` 表示未知。不是当前页长度,也不是全历史审批数量。“我创建的”包含普通表单与流程,不能先排除普通表单再把原总数称作审批数量。
54
26
  - `inspect` 读取当前访问者有权查看的指定实例,不要求该实例由本人发起。返回 `summary / currentNodes / submittedAt / processFinishedAt / processResult / detailTarget`。范围或实例身份不符直接失败;缺少节点、人员或时间保持未知,不用历史最后一条猜当前环节。
55
27
  - 页面运行时优先复用发布层可用的流程 JS-API 桥,仅在方法不可用时使用固定同源读取;桥调用报错不切换通道重试。缓存仅限已识别访问者、目标应用及实例,刷新和返回后失效,不缓存失败历史。
56
28
  - `history.state` 为 `not-requested / ready / error`。历史失败时可保留详情并展示明确错误,不能展示成“暂无历史”;实例和历史归属冲突、登录失败仍整体报错。
@@ -7,8 +7,6 @@ description: 仅在用户明确要求用自定义页面开发待办中心,或
7
7
 
8
8
  先核对用户已明确选择自定义实现及目标应用。仅提到待办中心、Fast 模式、已有 Canvas 页面或委托 AI 决定均不算选择;不要自行补确认记录。应用搭建中由 `yida-app` 先 ask_human,明确同意后才交接本技能;用户已显式要求时不重复问。
9
9
 
10
- 先确认数据与入口范围:当前应用(`scope=app`)还是当前用户在本组织所有应用(`scope=corp`)。用户只说“我的待办”且上下文不能确定范围时,必须通过 `ask_human` 确认,不能因正在编辑某个应用就默认应用范围。用户已明确范围时沿用,不重复询问。数量与点击入口必须使用相同范围;不把组织任务误称为所有组织。
11
-
12
10
  按用户描述确定所需个人流程任务及交互,不按整页或区块划分业务能力。仅需接入某项任务数据时走 `yida-canvas-data-binding`,不强制使用本装配;业务记录的待处理状态也由数据绑定处理。
13
11
 
14
12
  需要筛选时按需读取[个人任务筛选示例](references/task-filters.md),包含四类任务的组合条件、候选项查询及初始配置片段。
@@ -1,62 +0,0 @@
1
- 'use strict';
2
-
3
- // Portable: publish injects this exact implementation into the page utility bridge.
4
- function createPageNavigation(win) {
5
- function fail(code) {
6
- const error = new Error(code);
7
- error.code = code;
8
- throw error;
9
- }
10
- function normalize(input, options) {
11
- const value = input && typeof input === 'object' && !Array.isArray(input) ? input.url : input;
12
- if (typeof value !== 'string' || !value.trim()) { fail('PAGE_URL_INVALID'); }
13
- if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) { fail('PAGE_OPEN_OPTIONS_INVALID'); }
14
- const mode = (options && options.mode) || 'auto';
15
- if (!['auto', 'dingtalk', 'browser'].includes(mode)) { fail('PAGE_OPEN_MODE_INVALID'); }
16
- let url;
17
- try { url = new URL(value, win.location.href || win.location.origin); } catch { fail('PAGE_URL_INVALID'); }
18
- if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) { fail('PAGE_URL_INVALID'); }
19
- const inDingTalk = /dingtalk/i.test((win.navigator && win.navigator.userAgent) || '');
20
- const useDingTalk = mode === 'dingtalk' || (mode === 'auto' && inDingTalk);
21
- if (useDingTalk) { url.searchParams.set('ddtab', 'true'); }
22
- return { url: url.href, mode, inDingTalk, useDingTalk };
23
- }
24
- function invokeNative(invoke, url) {
25
- return new Promise((resolve, reject) => {
26
- function success(value) { clearTimeout(timer); resolve(value); }
27
- function rejectOpen(reason) {
28
- clearTimeout(timer);
29
- const error = new Error('DINGTALK_OPEN_FAILED');
30
- error.code = 'DINGTALK_OPEN_FAILED';
31
- error.cause = reason;
32
- reject(error);
33
- }
34
- const timer = setTimeout(() => rejectOpen('timeout'), 15000);
35
- try {
36
- const result = invoke({ url, onSuccess: success, onFail: rejectOpen });
37
- if (result && typeof result.then === 'function') { result.then(success, rejectOpen); }
38
- } catch (error) { rejectOpen(error); }
39
- });
40
- }
41
- async function open(input, options) {
42
- const target = normalize(input, options);
43
- if (!target.useDingTalk) {
44
- win.open(target.url, '_blank', 'noopener,noreferrer');
45
- return;
46
- }
47
- if (!target.inDingTalk) { fail('DINGTALK_ENV_REQUIRED'); }
48
- // Use the publishing host's bridge. Do not scan parent DOM or cross-origin windows.
49
- const dd = win.dd;
50
- if (dd && dd.env && dd.env.platform === 'notInDingTalk') { fail('DINGTALK_API_UNAVAILABLE'); }
51
- if (dd && dd.biz && dd.biz.util && typeof dd.biz.util.openLink === 'function') {
52
- return invokeNative(options => dd.biz.util.openLink(options), target.url);
53
- }
54
- if (win.Ali && typeof win.Ali.pushWindow === 'function') {
55
- return invokeNative(options => win.Ali.pushWindow(options), target.url);
56
- }
57
- fail('DINGTALK_API_UNAVAILABLE');
58
- }
59
- return { normalize, open };
60
- }
61
-
62
- module.exports = { createPageNavigation };
@@ -1,41 +0,0 @@
1
- 'use strict';
2
-
3
- // Shared by the CLI and the published page bridge; scope is always explicit.
4
- function createTaskCenterNavigation() {
5
- const kinds = {
6
- todo: { tab: 'running', nav: 'NAV-SYSTEM-RUNNING-UUID' },
7
- created: { tab: 'fromMe', nav: 'NAV-SYSTEM-FROM-ME-UUID' },
8
- done: { tab: 'done', nav: 'NAV-SYSTEM-DONE-UUID' },
9
- cc: { tab: 'notifyMe', nav: 'NAV-SYSTEM-NOTIFY-ME-UUID' },
10
- };
11
- function fail(field) {
12
- const error = new Error('TASK_LINK_INVALID: ' + field);
13
- error.code = 'TASK_LINK_INVALID';
14
- throw error;
15
- }
16
- function target(input) {
17
- if (!input || typeof input !== 'object' || Array.isArray(input)) { fail('options'); }
18
- for (const key of Object.keys(input)) {
19
- if (!['scope', 'appType', 'kind', 'baseUrl', 'mode'].includes(key)) { fail(key); }
20
- }
21
- const { scope, appType } = input;
22
- const kind = input.kind === 'submitted' ? 'created' : input.kind;
23
- if (!Object.prototype.hasOwnProperty.call(kinds, kind)) { fail('kind'); }
24
- if (!['app', 'corp'].includes(scope)) { fail('scope'); }
25
- if (scope === 'app' && (typeof appType !== 'string' || !/^APP_[A-Za-z0-9_-]+$/.test(appType))) { fail('appType'); }
26
- if (scope === 'corp' && appType !== undefined) { fail('appType'); }
27
- let origin;
28
- try { origin = new URL(input.baseUrl); } catch { fail('baseUrl'); }
29
- if (!['http:', 'https:'].includes(origin.protocol) || origin.username || origin.password) { fail('baseUrl'); }
30
- const mode = input.mode === undefined ? 'auto' : input.mode;
31
- if (!['auto', 'dingtalk', 'browser'].includes(mode)) { fail('mode'); }
32
- const definition = kinds[kind];
33
- const url = new URL(scope === 'app' ? '/' + appType + '/workbench/' + definition.nav : '/taskCenter.html', origin.origin);
34
- if (scope === 'app') { url.searchParams.set('showTodoTab', 'true'); } else { url.searchParams.set('tab', definition.tab); }
35
- if (mode === 'dingtalk') { url.searchParams.set('ddtab', 'true'); }
36
- return { scope, appType: scope === 'app' ? appType : null, kind, pageUrl: url.href, mode };
37
- }
38
- return { target };
39
- }
40
-
41
- module.exports = { createTaskCenterNavigation, ...createTaskCenterNavigation() };