@cniot/mdd-editor 0.3.6 → 0.3.8
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 +5 -0
- package/build/index.cjs.js +22 -22
- package/build/index.es.js +494 -31
- package/build/style.css +1 -1
- package/package.json +1 -1
- package/src/ai/LocalAIDrawer.jsx +511 -22
- package/src/ai/bridgeClient.js +1 -0
package/src/ai/LocalAIDrawer.jsx
CHANGED
|
@@ -231,9 +231,388 @@ function PullDiffDetail({ diffSummary }) {
|
|
|
231
231
|
);
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
const L4_CODE_PATTERN = /L4:([a-zA-Z0-9_-]+)/g;
|
|
235
|
+
const DEFAULT_LINKED_PAGE_DETAIL_URL = '/l4/api/v1/console/get-by-code';
|
|
236
|
+
const LINKED_PAGE_LOG_PREFIX = '[MDD AI Bridge][L4]';
|
|
237
|
+
const LOCAL_CHANGE_SKIP_REASON = '本地已有未同步修改';
|
|
238
|
+
|
|
239
|
+
const normalizeText = (value) => {
|
|
240
|
+
if (typeof value === 'string') return value;
|
|
241
|
+
if (value == null) return '';
|
|
242
|
+
return JSON.stringify(value, null, 2);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const logLinkedPageDebug = (...args) => {
|
|
246
|
+
if (typeof console !== 'undefined' && typeof console.info === 'function') {
|
|
247
|
+
console.info(LINKED_PAGE_LOG_PREFIX, ...args);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const collectL4ReferencesFromText = (text = '', source = 'script', path = '$') => {
|
|
252
|
+
const references = [];
|
|
253
|
+
const pattern = new RegExp(L4_CODE_PATTERN);
|
|
254
|
+
let match = pattern.exec(String(text));
|
|
255
|
+
while (match) {
|
|
256
|
+
references.push({
|
|
257
|
+
code: match[1],
|
|
258
|
+
source,
|
|
259
|
+
paths: [path],
|
|
260
|
+
});
|
|
261
|
+
match = pattern.exec(String(text));
|
|
262
|
+
}
|
|
263
|
+
return references;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const collectL4ReferencesFromJSON = (value, path = '$', references = []) => {
|
|
267
|
+
if (typeof value === 'string') {
|
|
268
|
+
references.push(...collectL4ReferencesFromText(value, 'schema', path));
|
|
269
|
+
return references;
|
|
270
|
+
}
|
|
271
|
+
if (Array.isArray(value)) {
|
|
272
|
+
value.forEach((item, index) => collectL4ReferencesFromJSON(item, `${path}[${index}]`, references));
|
|
273
|
+
return references;
|
|
274
|
+
}
|
|
275
|
+
if (value && typeof value === 'object') {
|
|
276
|
+
Object.keys(value).forEach((key) => {
|
|
277
|
+
references.push(...collectL4ReferencesFromText(key, 'schema-key', `${path}.${key}`));
|
|
278
|
+
collectL4ReferencesFromJSON(value[key], `${path}.${key}`, references);
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
return references;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const mergeL4References = (references = [], rootCode) => {
|
|
285
|
+
const referenceMap = new Map();
|
|
286
|
+
references.forEach((reference) => {
|
|
287
|
+
if (!reference?.code || reference.code === rootCode) return;
|
|
288
|
+
const existing = referenceMap.get(reference.code) || {
|
|
289
|
+
code: reference.code,
|
|
290
|
+
source: reference.source,
|
|
291
|
+
paths: [],
|
|
292
|
+
};
|
|
293
|
+
existing.source = existing.source === reference.source ? existing.source : 'mixed';
|
|
294
|
+
existing.paths = [...new Set([...existing.paths, ...(reference.paths || [])])];
|
|
295
|
+
referenceMap.set(reference.code, existing);
|
|
296
|
+
});
|
|
297
|
+
return [...referenceMap.values()];
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const collectLinkedPageReferences = ({ schemaJson, scriptInfo, rootCode, extraSources = [] }) => {
|
|
301
|
+
const schemaReferences = collectL4ReferencesFromJSON(schemaJson);
|
|
302
|
+
const scriptReferences = collectL4ReferencesFromText(scriptInfo, 'script', 'mdd.script.jsx');
|
|
303
|
+
const extraReferences = extraSources.flatMap((source) =>
|
|
304
|
+
collectL4ReferencesFromText(source?.text || '', source?.source || 'extra', source?.path || '$'),
|
|
305
|
+
);
|
|
306
|
+
return mergeL4References([...schemaReferences, ...scriptReferences, ...extraReferences], rootCode);
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const createLinkedPageExtraSources = (data = {}, prefix = 'payload') => {
|
|
310
|
+
const sourceFields = [
|
|
311
|
+
'schemaInfo',
|
|
312
|
+
'schemaJson',
|
|
313
|
+
'schema',
|
|
314
|
+
'scriptInfo',
|
|
315
|
+
'script',
|
|
316
|
+
'style',
|
|
317
|
+
'compileFile',
|
|
318
|
+
'translateInfo',
|
|
319
|
+
'serverDataHandle',
|
|
320
|
+
'metaData',
|
|
321
|
+
'sqlData',
|
|
322
|
+
'sqlDataCount',
|
|
323
|
+
'swaggerInfo',
|
|
324
|
+
'dataPermissions',
|
|
325
|
+
'pageMeta',
|
|
326
|
+
'pageIR',
|
|
327
|
+
];
|
|
328
|
+
return sourceFields
|
|
329
|
+
.filter((fieldName) => data[fieldName] != null)
|
|
330
|
+
.map((fieldName) => ({
|
|
331
|
+
source: fieldName,
|
|
332
|
+
path: `${prefix}.${fieldName}`,
|
|
333
|
+
text: normalizeText(data[fieldName]),
|
|
334
|
+
}));
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const enqueueLinkedPageReferences = ({ queue, referenceMap, references, parentCode, depth, rootCode, visitedCodes }) => {
|
|
338
|
+
references.forEach((reference) => {
|
|
339
|
+
if (!reference?.code || reference.code === rootCode) return;
|
|
340
|
+
const nextReference = {
|
|
341
|
+
...reference,
|
|
342
|
+
parentCode,
|
|
343
|
+
depth,
|
|
344
|
+
};
|
|
345
|
+
const existing = referenceMap.get(reference.code);
|
|
346
|
+
if (existing) {
|
|
347
|
+
existing.source = existing.source === nextReference.source ? existing.source : 'mixed';
|
|
348
|
+
existing.paths = [...new Set([...(existing.paths || []), ...(nextReference.paths || [])])];
|
|
349
|
+
existing.parents = [...new Set([...(existing.parents || []), parentCode])];
|
|
350
|
+
existing.depth = Math.min(existing.depth || depth, depth);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
referenceMap.set(reference.code, {
|
|
354
|
+
...nextReference,
|
|
355
|
+
parents: [parentCode],
|
|
356
|
+
});
|
|
357
|
+
if (!visitedCodes.has(reference.code)) {
|
|
358
|
+
queue.push(nextReference);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const fetchLinkedPageByCode = async (code, config = {}) => {
|
|
364
|
+
const detailUrl = config.linkedPageDetailUrl || DEFAULT_LINKED_PAGE_DETAIL_URL;
|
|
365
|
+
const separator = detailUrl.includes('?') ? '&' : '?';
|
|
366
|
+
const url = `${detailUrl}${separator}code=${encodeURIComponent(code)}`;
|
|
367
|
+
logLinkedPageDebug('fetch page detail', { code, url });
|
|
368
|
+
const response = await fetch(url, {
|
|
369
|
+
credentials: 'include',
|
|
370
|
+
});
|
|
371
|
+
const text = await response.text();
|
|
372
|
+
let result = null;
|
|
373
|
+
try {
|
|
374
|
+
result = text ? JSON.parse(text) : null;
|
|
375
|
+
} catch (error) {
|
|
376
|
+
throw new Error(`关联 L4 页面详情接口返回非 JSON:${text || response.status}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (!response.ok) {
|
|
380
|
+
throw new Error(result?.errorMessage || result?.message || `关联 L4 页面详情接口请求失败:${response.status}`);
|
|
381
|
+
}
|
|
382
|
+
if (result?.success === false) {
|
|
383
|
+
throw new Error(result?.errorMessage || result?.message || result?.msg || '关联 L4 页面详情接口返回失败');
|
|
384
|
+
}
|
|
385
|
+
const pageData = result?.data || result;
|
|
386
|
+
logLinkedPageDebug('fetch page detail success', {
|
|
387
|
+
code,
|
|
388
|
+
name: pageData?.name || pageData?.pageMeta?.name,
|
|
389
|
+
hasSchemaInfo: Boolean(pageData?.schemaInfo),
|
|
390
|
+
hasScriptInfo: Boolean(pageData?.scriptInfo),
|
|
391
|
+
hasCompileFile: Boolean(pageData?.compileFile),
|
|
392
|
+
});
|
|
393
|
+
return pageData;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const getLinkedPageResolver = (config) => {
|
|
397
|
+
if (typeof config?.resolveLinkedPage === 'function') return config.resolveLinkedPage;
|
|
398
|
+
if (typeof window !== 'undefined' && typeof window.__MDD_AI_BRIDGE_RESOLVE_L4_PAGE === 'function') {
|
|
399
|
+
return window.__MDD_AI_BRIDGE_RESOLVE_L4_PAGE;
|
|
400
|
+
}
|
|
401
|
+
return (code) => fetchLinkedPageByCode(code, config);
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const normalizeLinkedPagePayload = (reference, pageData) => {
|
|
405
|
+
if (!pageData) return null;
|
|
406
|
+
const schemaJson = parseSchema(pageData.schemaInfo || pageData.schemaJson || pageData.schema || '{}') || {};
|
|
407
|
+
const rawPageMeta = pageData.pageMeta || pageData.meta || {};
|
|
408
|
+
const pageMeta = {
|
|
409
|
+
...rawPageMeta,
|
|
410
|
+
code: pageData.code || rawPageMeta.code || reference.code,
|
|
411
|
+
name: pageData.name ?? rawPageMeta.name,
|
|
412
|
+
region: pageData.region ?? rawPageMeta.region,
|
|
413
|
+
module: pageData.module ?? rawPageMeta.module,
|
|
414
|
+
moduleName: pageData.moduleName ?? rawPageMeta.moduleName,
|
|
415
|
+
status: pageData.status ?? rawPageMeta.status,
|
|
416
|
+
grade: pageData.grade ?? rawPageMeta.grade,
|
|
417
|
+
mddTemplateType: pageData.mddTemplateType ?? rawPageMeta.mddTemplateType,
|
|
418
|
+
buildType: pageData.buildType ?? rawPageMeta.buildType,
|
|
419
|
+
operateType: pageData.operateType ?? rawPageMeta.operateType,
|
|
420
|
+
gmtModified: pageData.gmtModified ?? rawPageMeta.gmtModified,
|
|
421
|
+
modifierAccount: pageData.modifierAccount ?? rawPageMeta.modifierAccount,
|
|
422
|
+
modifierName: pageData.modifierName ?? rawPageMeta.modifierName,
|
|
423
|
+
};
|
|
424
|
+
const script = pageData.scriptInfo?.script ?? pageData.scriptInfo ?? pageData.script ?? '';
|
|
425
|
+
const style = pageData.scriptInfo?.style ?? pageData.style ?? '';
|
|
426
|
+
const pageIR =
|
|
427
|
+
pageData.pageIR ||
|
|
428
|
+
buildPageIR({
|
|
429
|
+
schemaJson,
|
|
430
|
+
script,
|
|
431
|
+
style,
|
|
432
|
+
pageMeta,
|
|
433
|
+
});
|
|
434
|
+
return {
|
|
435
|
+
code: pageMeta.code,
|
|
436
|
+
pageMeta,
|
|
437
|
+
schemaInfo: normalizeText(pageData.schemaInfo || schemaJson),
|
|
438
|
+
scriptInfo: script,
|
|
439
|
+
style,
|
|
440
|
+
pageIR,
|
|
441
|
+
extraSources: createLinkedPageExtraSources(
|
|
442
|
+
{
|
|
443
|
+
...pageData,
|
|
444
|
+
pageMeta,
|
|
445
|
+
pageIR,
|
|
446
|
+
style,
|
|
447
|
+
},
|
|
448
|
+
`linkedPage.${pageMeta.code}`,
|
|
449
|
+
),
|
|
450
|
+
linkedReference: reference,
|
|
451
|
+
};
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const buildLinkedPagePayload = async (payload, config) => {
|
|
455
|
+
const rootSchemaJson = parseSchema(payload.schemaInfo) || {};
|
|
456
|
+
const resolver = getLinkedPageResolver(config);
|
|
457
|
+
let rootPageData = null;
|
|
458
|
+
try {
|
|
459
|
+
logLinkedPageDebug('fetch root page detail for raw scan', { code: payload.code });
|
|
460
|
+
rootPageData = await resolver(payload.code, {
|
|
461
|
+
code: payload.code,
|
|
462
|
+
source: 'root',
|
|
463
|
+
depth: 0,
|
|
464
|
+
paths: ['root.get-by-code'],
|
|
465
|
+
});
|
|
466
|
+
} catch (error) {
|
|
467
|
+
logLinkedPageDebug('fetch root page detail failed, fallback to editor payload only', {
|
|
468
|
+
code: payload.code,
|
|
469
|
+
reason: error?.message || String(error),
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
const directReferences = collectLinkedPageReferences({
|
|
473
|
+
schemaJson: rootSchemaJson,
|
|
474
|
+
scriptInfo: payload.scriptInfo,
|
|
475
|
+
rootCode: payload.code,
|
|
476
|
+
extraSources: [
|
|
477
|
+
...(payload.extraSources || []),
|
|
478
|
+
...createLinkedPageExtraSources(rootPageData || {}, `rootApi.${payload.code}`),
|
|
479
|
+
],
|
|
480
|
+
});
|
|
481
|
+
logLinkedPageDebug('scan root page', {
|
|
482
|
+
code: payload.code,
|
|
483
|
+
source: rootPageData ? 'editor payload + root get-by-code raw data' : 'editor payload only',
|
|
484
|
+
directReferences: directReferences.map((reference) => ({
|
|
485
|
+
code: reference.code,
|
|
486
|
+
source: reference.source,
|
|
487
|
+
paths: reference.paths,
|
|
488
|
+
})),
|
|
489
|
+
});
|
|
490
|
+
const queue = [];
|
|
491
|
+
const referenceMap = new Map();
|
|
492
|
+
const visitedCodes = new Set([payload.code]);
|
|
493
|
+
const linkedPages = [];
|
|
494
|
+
const failedLinkedPages = [];
|
|
495
|
+
|
|
496
|
+
enqueueLinkedPageReferences({
|
|
497
|
+
queue,
|
|
498
|
+
referenceMap,
|
|
499
|
+
references: directReferences,
|
|
500
|
+
parentCode: payload.code,
|
|
501
|
+
depth: 1,
|
|
502
|
+
rootCode: payload.code,
|
|
503
|
+
visitedCodes,
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
while (queue.length > 0) {
|
|
507
|
+
const currentReference = queue.shift();
|
|
508
|
+
if (!currentReference?.code || visitedCodes.has(currentReference.code)) continue;
|
|
509
|
+
visitedCodes.add(currentReference.code);
|
|
510
|
+
const latestReference = referenceMap.get(currentReference.code) || currentReference;
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
logLinkedPageDebug('resolve linked page', {
|
|
514
|
+
code: currentReference.code,
|
|
515
|
+
parentCode: latestReference.parentCode,
|
|
516
|
+
depth: latestReference.depth,
|
|
517
|
+
paths: latestReference.paths,
|
|
518
|
+
});
|
|
519
|
+
const pageData = await resolver(currentReference.code, latestReference);
|
|
520
|
+
const linkedPage = normalizeLinkedPagePayload(latestReference, pageData);
|
|
521
|
+
if (!linkedPage) {
|
|
522
|
+
failedLinkedPages.push({
|
|
523
|
+
...latestReference,
|
|
524
|
+
reason: '关联 L4 页面返回为空',
|
|
525
|
+
});
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
linkedPages.push(linkedPage);
|
|
530
|
+
const linkedPageSchemaJson = parseSchema(linkedPage.schemaInfo) || {};
|
|
531
|
+
const childReferences = collectLinkedPageReferences({
|
|
532
|
+
schemaJson: linkedPageSchemaJson,
|
|
533
|
+
scriptInfo: linkedPage.scriptInfo,
|
|
534
|
+
rootCode: payload.code,
|
|
535
|
+
extraSources: linkedPage.extraSources,
|
|
536
|
+
});
|
|
537
|
+
logLinkedPageDebug('scan child page', {
|
|
538
|
+
code: linkedPage.code,
|
|
539
|
+
depth: latestReference.depth,
|
|
540
|
+
childReferences: childReferences.map((reference) => ({
|
|
541
|
+
code: reference.code,
|
|
542
|
+
source: reference.source,
|
|
543
|
+
paths: reference.paths,
|
|
544
|
+
})),
|
|
545
|
+
});
|
|
546
|
+
enqueueLinkedPageReferences({
|
|
547
|
+
queue,
|
|
548
|
+
referenceMap,
|
|
549
|
+
references: childReferences,
|
|
550
|
+
parentCode: linkedPage.code,
|
|
551
|
+
depth: (latestReference.depth || 1) + 1,
|
|
552
|
+
rootCode: payload.code,
|
|
553
|
+
visitedCodes,
|
|
554
|
+
});
|
|
555
|
+
} catch (error) {
|
|
556
|
+
failedLinkedPages.push({
|
|
557
|
+
...latestReference,
|
|
558
|
+
reason: error?.message || '关联 L4 页面加载失败',
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const linkedPageReferences = [...referenceMap.values()];
|
|
564
|
+
const maxDepth = linkedPageReferences.reduce((depth, reference) => Math.max(depth, reference.depth || 1), 0);
|
|
565
|
+
const mergedLinkedPages = linkedPages.map((linkedPage) => ({
|
|
566
|
+
...linkedPage,
|
|
567
|
+
linkedReference: referenceMap.get(linkedPage.code) || linkedPage.linkedReference,
|
|
568
|
+
}));
|
|
569
|
+
logLinkedPageDebug('scan complete', {
|
|
570
|
+
code: payload.code,
|
|
571
|
+
directCount: directReferences.length,
|
|
572
|
+
totalCount: linkedPageReferences.length,
|
|
573
|
+
syncedCount: mergedLinkedPages.length,
|
|
574
|
+
failedCount: failedLinkedPages.length,
|
|
575
|
+
references: linkedPageReferences.map((reference) => ({
|
|
576
|
+
code: reference.code,
|
|
577
|
+
source: reference.source,
|
|
578
|
+
depth: reference.depth,
|
|
579
|
+
paths: reference.paths,
|
|
580
|
+
parents: reference.parents,
|
|
581
|
+
})),
|
|
582
|
+
failed: failedLinkedPages,
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
return {
|
|
586
|
+
...payload,
|
|
587
|
+
linkedPageReferences,
|
|
588
|
+
linkedPageScan: {
|
|
589
|
+
directCount: directReferences.length,
|
|
590
|
+
totalCount: linkedPageReferences.length,
|
|
591
|
+
maxDepth,
|
|
592
|
+
},
|
|
593
|
+
linkedPages: mergedLinkedPages,
|
|
594
|
+
failedLinkedPages,
|
|
595
|
+
};
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
const isLocalChangeSkippedPage = (page) => String(page?.reason || '').includes(LOCAL_CHANGE_SKIP_REASON);
|
|
599
|
+
|
|
600
|
+
const createLinkedPushSummary = (res, fallbackDir) => ({
|
|
601
|
+
type: 'push',
|
|
602
|
+
dir: res?.dir || fallbackDir,
|
|
603
|
+
linkedPages: res?.linkedPages || {},
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
const createLinkedPullSummary = (res) => {
|
|
607
|
+
const changedPages = (res?.linkedPageDiffs || []).filter((item) => item.changed);
|
|
608
|
+
if (!changedPages.length) return '';
|
|
609
|
+
const codes = changedPages.map((item) => item.code).join('、');
|
|
610
|
+
return `检测到关联 L4 页面也存在本地修改:${codes}。当前只同步了当前页面,请分别打开这些 L4 页面执行“同步本地修改”并保存。`;
|
|
611
|
+
};
|
|
612
|
+
|
|
234
613
|
export default function LocalAIDrawer(props) {
|
|
235
614
|
const { schema, scriptInfo, pageMeta = {}, bridgeConfig, onApply } = props;
|
|
236
|
-
const config = normalizeBridgeConfig(bridgeConfig);
|
|
615
|
+
const config = React.useMemo(() => normalizeBridgeConfig(bridgeConfig), [bridgeConfig]);
|
|
237
616
|
const [baseURL, setBaseURL] = React.useState(config.baseURL || DEFAULT_BRIDGE_URL);
|
|
238
617
|
const [status, setStatus] = React.useState('未连接');
|
|
239
618
|
const [workspacePath, setWorkspacePath] = React.useState('');
|
|
@@ -248,24 +627,33 @@ export default function LocalAIDrawer(props) {
|
|
|
248
627
|
|
|
249
628
|
const getPayload = React.useCallback(() => {
|
|
250
629
|
const nextSchemaJson = schema?.getAllJSON?.() || {};
|
|
630
|
+
const nextPageMeta = {
|
|
631
|
+
...pageMeta,
|
|
632
|
+
code: pageCode,
|
|
633
|
+
};
|
|
634
|
+
const nextScriptInfo = scriptInfo?.script || '';
|
|
635
|
+
const nextStyle = scriptInfo?.style || '';
|
|
636
|
+
const nextPageIR = buildPageIR({
|
|
637
|
+
schemaJson: nextSchemaJson,
|
|
638
|
+
script: nextScriptInfo,
|
|
639
|
+
style: nextStyle,
|
|
640
|
+
pageMeta: nextPageMeta,
|
|
641
|
+
});
|
|
251
642
|
return {
|
|
252
643
|
code: pageCode,
|
|
253
|
-
pageMeta:
|
|
254
|
-
...pageMeta,
|
|
255
|
-
code: pageCode,
|
|
256
|
-
},
|
|
644
|
+
pageMeta: nextPageMeta,
|
|
257
645
|
schemaInfo: JSON.stringify(nextSchemaJson, null, 2),
|
|
258
|
-
scriptInfo:
|
|
259
|
-
style:
|
|
260
|
-
pageIR:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
code: pageCode,
|
|
646
|
+
scriptInfo: nextScriptInfo,
|
|
647
|
+
style: nextStyle,
|
|
648
|
+
pageIR: nextPageIR,
|
|
649
|
+
extraSources: createLinkedPageExtraSources(
|
|
650
|
+
{
|
|
651
|
+
pageMeta: nextPageMeta,
|
|
652
|
+
pageIR: nextPageIR,
|
|
653
|
+
style: nextStyle,
|
|
267
654
|
},
|
|
268
|
-
|
|
655
|
+
'editorPayload',
|
|
656
|
+
),
|
|
269
657
|
};
|
|
270
658
|
}, [pageCode, pageMeta, schema, scriptInfo]);
|
|
271
659
|
|
|
@@ -331,15 +719,22 @@ export default function LocalAIDrawer(props) {
|
|
|
331
719
|
setPullError(null);
|
|
332
720
|
setLastDiffSummary(null);
|
|
333
721
|
try {
|
|
334
|
-
const
|
|
722
|
+
const payload = await buildLinkedPagePayload(getPayload(), config);
|
|
723
|
+
const res = await pushPage(baseURL, pageCode, payload);
|
|
335
724
|
setWorkspacePath(res?.dir || '');
|
|
336
725
|
setBridgeInfo((prev) => ({
|
|
337
726
|
...(prev || {}),
|
|
338
727
|
workspaceRoot: res?.context?.workspaceRoot || prev?.workspaceRoot,
|
|
339
728
|
}));
|
|
340
729
|
setStatus('已同步到本地');
|
|
341
|
-
setLastSummary(
|
|
342
|
-
|
|
730
|
+
setLastSummary(createLinkedPushSummary(res, pageCode));
|
|
731
|
+
if (res?.linkedPages?.totalReferences > 0) {
|
|
732
|
+
Message.success(
|
|
733
|
+
`已发送到本地 AI 工作区,扫描到直接子 L4 页面 ${res.linkedPages.directCount || 0} 个,递归共 ${res.linkedPages.totalReferences} 个`,
|
|
734
|
+
);
|
|
735
|
+
} else {
|
|
736
|
+
Message.success('已发送到本地 AI 工作区');
|
|
737
|
+
}
|
|
343
738
|
} catch (e) {
|
|
344
739
|
Message.error(e.message || '发送到本地 AI 工作区失败');
|
|
345
740
|
} finally {
|
|
@@ -364,9 +759,14 @@ export default function LocalAIDrawer(props) {
|
|
|
364
759
|
scriptInfo: nextScriptInfo,
|
|
365
760
|
raw: res,
|
|
366
761
|
});
|
|
367
|
-
|
|
762
|
+
const linkedPullSummary = createLinkedPullSummary(res);
|
|
763
|
+
setLastSummary([res?.summary || '已从本地工作区同步修改', linkedPullSummary].filter(Boolean).join(';'));
|
|
368
764
|
setLastDiffSummary(res?.diffSummary || null);
|
|
369
|
-
|
|
765
|
+
if (linkedPullSummary) {
|
|
766
|
+
Message.warning('已同步当前页面;关联子 L4 页面也有本地修改,请分别打开对应 L4 页面同步并保存');
|
|
767
|
+
} else {
|
|
768
|
+
Message.success('已同步当前页面的本地修改,请预览确认后点击页面保存');
|
|
769
|
+
}
|
|
370
770
|
} catch (e) {
|
|
371
771
|
const detail = getJsonErrorDetail(e);
|
|
372
772
|
setPullError(detail);
|
|
@@ -415,7 +815,15 @@ export default function LocalAIDrawer(props) {
|
|
|
415
815
|
const res = await getPageStatus(baseURL, pageCode);
|
|
416
816
|
setWorkspacePath(res?.dir || '');
|
|
417
817
|
setLastDiffSummary(null);
|
|
418
|
-
|
|
818
|
+
const linkedChangedCount = res?.linkedPages?.changedCount || 0;
|
|
819
|
+
const linkedStatusText = linkedChangedCount
|
|
820
|
+
? `;关联 L4 页面中有 ${linkedChangedCount} 个存在本地修改`
|
|
821
|
+
: res?.linkedPages?.totalReferences
|
|
822
|
+
? `;已记录关联 L4 页面 ${res.linkedPages.totalReferences} 个`
|
|
823
|
+
: '';
|
|
824
|
+
setLastSummary(
|
|
825
|
+
res?.exists ? `本地工作区已存在: ${res.dir}${linkedStatusText}` : '本地还没有这个页面的工作区',
|
|
826
|
+
);
|
|
419
827
|
Message.success(res?.exists ? '本地工作区已存在' : '本地工作区不存在');
|
|
420
828
|
} catch (e) {
|
|
421
829
|
Message.error(e.message || '查询本地工作区失败');
|
|
@@ -516,6 +924,9 @@ export default function LocalAIDrawer(props) {
|
|
|
516
924
|
回滚上次更改
|
|
517
925
|
</CnButton>
|
|
518
926
|
</div>
|
|
927
|
+
<div className="mdd-local-ai-action-notice">
|
|
928
|
+
“同步本地修改”只会同步当前页面的本地变更;如果关联子 L4 页面也在本地改过,请打开对应子页面的 L4 地址,分别执行“同步本地修改”并在页面上点击保存。
|
|
929
|
+
</div>
|
|
519
930
|
</div>
|
|
520
931
|
|
|
521
932
|
<div className="mdd-local-ai-section">
|
|
@@ -533,7 +944,85 @@ export default function LocalAIDrawer(props) {
|
|
|
533
944
|
</div>
|
|
534
945
|
</div>
|
|
535
946
|
|
|
536
|
-
{lastSummary ?
|
|
947
|
+
{lastSummary ? (
|
|
948
|
+
lastSummary.type === 'push' ? (
|
|
949
|
+
<div className="mdd-local-ai-push-result">
|
|
950
|
+
<div className="mdd-local-ai-push-result-head">
|
|
951
|
+
<div>
|
|
952
|
+
<div className="mdd-local-ai-push-result-title">已写入本地工作区</div>
|
|
953
|
+
<div className="mdd-local-ai-push-result-path">{lastSummary.dir}</div>
|
|
954
|
+
</div>
|
|
955
|
+
<span className="mdd-local-ai-push-result-badge">完成</span>
|
|
956
|
+
</div>
|
|
957
|
+
|
|
958
|
+
<div className="mdd-local-ai-push-stats">
|
|
959
|
+
<div className="mdd-local-ai-push-stat">
|
|
960
|
+
<span>直接子 L4</span>
|
|
961
|
+
<strong>{lastSummary.linkedPages?.directCount || 0}</strong>
|
|
962
|
+
</div>
|
|
963
|
+
<div className="mdd-local-ai-push-stat">
|
|
964
|
+
<span>递归关联</span>
|
|
965
|
+
<strong>{lastSummary.linkedPages?.totalReferences || 0}</strong>
|
|
966
|
+
</div>
|
|
967
|
+
<div className="mdd-local-ai-push-stat is-success">
|
|
968
|
+
<span>已同步</span>
|
|
969
|
+
<strong>{lastSummary.linkedPages?.syncedCount || 0}</strong>
|
|
970
|
+
</div>
|
|
971
|
+
<div className="mdd-local-ai-push-stat is-warning">
|
|
972
|
+
<span>需处理</span>
|
|
973
|
+
<strong>{lastSummary.linkedPages?.failedCount || 0}</strong>
|
|
974
|
+
</div>
|
|
975
|
+
</div>
|
|
976
|
+
|
|
977
|
+
{lastSummary.linkedPages?.pages?.length ? (
|
|
978
|
+
<div className="mdd-local-ai-linked-section">
|
|
979
|
+
<div className="mdd-local-ai-linked-section-title">已一起同步的关联页面</div>
|
|
980
|
+
<div className="mdd-local-ai-linked-tags">
|
|
981
|
+
{lastSummary.linkedPages.pages.map((page) => (
|
|
982
|
+
<span key={page.code} className="mdd-local-ai-linked-tag is-success has-name">
|
|
983
|
+
<span className="mdd-local-ai-linked-name">{page.name || '未命名页面'}</span>
|
|
984
|
+
<span className="mdd-local-ai-linked-code">{page.code}</span>
|
|
985
|
+
</span>
|
|
986
|
+
))}
|
|
987
|
+
</div>
|
|
988
|
+
</div>
|
|
989
|
+
) : null}
|
|
990
|
+
|
|
991
|
+
{lastSummary.linkedPages?.failed?.length ? (
|
|
992
|
+
<div className="mdd-local-ai-linked-section">
|
|
993
|
+
<div className="mdd-local-ai-linked-section-title">需要用户处理的关联页面</div>
|
|
994
|
+
{lastSummary.linkedPages.failed.map((page) => {
|
|
995
|
+
const isLocalChangeSkipped = isLocalChangeSkippedPage(page);
|
|
996
|
+
return (
|
|
997
|
+
<div key={page.code} className="mdd-local-ai-linked-warning">
|
|
998
|
+
<div className="mdd-local-ai-linked-warning-main">
|
|
999
|
+
<span className="mdd-local-ai-linked-tag is-warning has-name">
|
|
1000
|
+
<span className="mdd-local-ai-linked-name">{page.name || '未命名页面'}</span>
|
|
1001
|
+
<span className="mdd-local-ai-linked-code">{page.code}</span>
|
|
1002
|
+
</span>
|
|
1003
|
+
<span>{page.reason || '未能自动同步'}</span>
|
|
1004
|
+
</div>
|
|
1005
|
+
{isLocalChangeSkipped ? (
|
|
1006
|
+
<div className="mdd-local-ai-linked-actions-tip">
|
|
1007
|
+
<div>这个页面本地已有未同步修改,为避免覆盖,本次没有重新下发。</div>
|
|
1008
|
+
<div>你可以选择:1)打开该 L4 页面先“同步本地修改”并保存;2)确认放弃本地改动后删除本地目录,再从父页面重新下发。</div>
|
|
1009
|
+
{page.dir ? (
|
|
1010
|
+
<CnButton size="small" onClick={() => handleCopy(page.dir, '已复制关联页面本地目录')}>
|
|
1011
|
+
复制本地目录
|
|
1012
|
+
</CnButton>
|
|
1013
|
+
) : null}
|
|
1014
|
+
</div>
|
|
1015
|
+
) : null}
|
|
1016
|
+
</div>
|
|
1017
|
+
);
|
|
1018
|
+
})}
|
|
1019
|
+
</div>
|
|
1020
|
+
) : null}
|
|
1021
|
+
</div>
|
|
1022
|
+
) : (
|
|
1023
|
+
<div className="mdd-local-ai-summary">{lastSummary}</div>
|
|
1024
|
+
)
|
|
1025
|
+
) : null}
|
|
537
1026
|
|
|
538
1027
|
<PullDiffDetail diffSummary={lastDiffSummary} />
|
|
539
1028
|
|