openyida 2026.7.7 → 2026.7.9
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 +4 -2
- package/bin/yida.js +4 -4
- package/lib/app/canvas-compile.js +303 -0
- package/lib/app/publish.js +190 -39
- package/lib/auth/auth.js +11 -3
- package/lib/auth/login.js +34 -15
- package/lib/core/command-manifest.js +1 -1
- package/lib/core/env.js +19 -4
- package/lib/core/locales/ar.js +6 -2
- package/lib/core/locales/de.js +6 -2
- package/lib/core/locales/en.js +7 -3
- package/lib/core/locales/es.js +6 -2
- package/lib/core/locales/fr.js +6 -2
- package/lib/core/locales/hi.js +6 -2
- package/lib/core/locales/ja.js +7 -3
- package/lib/core/locales/ko.js +6 -2
- package/lib/core/locales/pt.js +6 -2
- package/lib/core/locales/vi.js +6 -2
- package/lib/core/locales/zh-HK.js +7 -3
- package/lib/core/locales/zh.js +7 -3
- package/lib/core/utils.js +195 -15
- package/lib/samples/yida-custom-page/custom-page-template.js +6 -6
- package/lib/samples/yida-custom-page/design-tokens.js +14 -12
- package/package.json +1 -1
- package/scripts/e2e-real/skill-coverage.js +4 -0
- package/scripts/validate-ci.sh +6 -0
- package/scripts/validate-package-size.js +12 -1
- package/yida-skills/SKILL.md +21 -6
- package/yida-skills/references/development-rules.md +13 -9
- package/yida-skills/references/field-and-url-reference.md +14 -1
- package/yida-skills/skills/large-file-write/SKILL.md +12 -10
- package/yida-skills/skills/large-file-write/references/write-patterns.md +13 -15
- package/yida-skills/skills/large-file-write/scripts/write.js +10 -10
- package/yida-skills/skills/yida-app/SKILL.md +15 -8
- package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +115 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-authoring-examples.md +163 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/canvas-design-system.md +148 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +115 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/dependencies-and-cdn.md +45 -0
- package/yida-skills/skills/yida-canvas-custom-page/references/employeefield-verification.md +68 -0
- package/yida-skills/skills/yida-canvas-upgrade/SKILL.md +99 -0
- package/yida-skills/skills/yida-canvas-upgrade/references/migration-examples.md +69 -0
- package/yida-skills/skills/yida-chart/SKILL.md +2 -2
- package/yida-skills/skills/yida-chart/references/echarts-bindding-guide.md +5 -6
- package/yida-skills/skills/yida-chart/references/examples.md +2 -2
- package/yida-skills/skills/yida-connector/SKILL.md +4 -0
- package/yida-skills/skills/yida-connector-safe-actions/SKILL.md +7 -4
- package/yida-skills/skills/yida-create-form-page/SKILL.md +11 -8
- package/yida-skills/skills/yida-create-process/SKILL.md +10 -4
- package/yida-skills/skills/yida-custom-page/SKILL.md +17 -6
- package/yida-skills/skills/yida-custom-page/examples/attachment-upload.js +1 -1
- package/yida-skills/skills/yida-custom-page/references/coding-guide.md +3 -3
- package/yida-skills/skills/yida-custom-page/references/component-jsx-guide.md +6 -3
- package/yida-skills/skills/yida-custom-page/references/design-system.md +30 -13
- package/yida-skills/skills/yida-data-management/SKILL.md +14 -11
- package/yida-skills/skills/yida-data-source-connectors/SKILL.md +3 -1
- package/yida-skills/skills/yida-flash-note-to-prd/references/examples.md +3 -2
- package/yida-skills/skills/yida-formula/references/examples.md +4 -4
- package/yida-skills/skills/yida-formula-evaluate/SKILL.md +4 -2
- package/yida-skills/skills/yida-get-schema/SKILL.md +14 -5
- package/yida-skills/skills/yida-i18n/SKILL.md +4 -2
- package/yida-skills/skills/yida-integration/SKILL.md +4 -1
- package/yida-skills/skills/yida-integration/references/examples.md +2 -2
- package/yida-skills/skills/yida-nav-shell/SKILL.md +119 -0
- package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +302 -0
- package/yida-skills/skills/yida-openyida-publish-guard/SKILL.md +2 -2
- package/yida-skills/skills/yida-page-config/SKILL.md +1 -0
- package/yida-skills/skills/yida-page-uiux/SKILL.md +68 -0
- package/yida-skills/skills/yida-page-uiux/references/scenes/dashboard.md +128 -0
- package/yida-skills/skills/yida-page-uiux/references/scenes/detail.md +79 -0
- package/yida-skills/skills/yida-page-uiux/references/scenes/landing.md +101 -0
- package/yida-skills/skills/yida-page-uiux/references/scenes/list.md +78 -0
- package/yida-skills/skills/yida-page-uiux/references/scenes/workbench.md +74 -0
- package/yida-skills/skills/yida-page-uiux/references/visual-decision-engine.md +151 -0
- package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +36 -0
- package/yida-skills/skills/yida-page-uiux/workflow/step-0-nav-shape.md +39 -0
- package/yida-skills/skills/yida-page-uiux/workflow/step-1-page-type.md +25 -0
- package/yida-skills/skills/yida-page-uiux/workflow/step-2-intent-decode.md +25 -0
- package/yida-skills/skills/yida-page-uiux/workflow/step-3-scene-routing.md +29 -0
- package/yida-skills/skills/yida-page-uiux/workflow/step-4-visual-decision.md +32 -0
- package/yida-skills/skills/yida-page-uiux/workflow/step-5-icon-and-assets.md +36 -0
- package/yida-skills/skills/yida-page-uiux/workflow/step-6-deai-selfcheck.md +37 -0
- package/yida-skills/skills/yida-process-rule/SKILL.md +4 -1
- package/yida-skills/skills/yida-process-rule/references/examples.md +12 -12
- package/yida-skills/skills/yida-publish-page/SKILL.md +7 -3
- package/yida-skills/skills/yida-report/SKILL.md +5 -2
- package/yida-skills/skills/yida-report/references/examples.md +4 -4
- package/yida-skills/skills/yida-table-form/references/examples.md +2 -2
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Code Canvas 编写示例 / 脚手架
|
|
2
|
+
|
|
3
|
+
本文件是从零写 Code Canvas 页面的 vetted 模板集。所有示例都遵守运行时事实:`YidaComp` 是普通 React18 函数组件,必须**导出或返回** `YidaComp` / `YidaComp.default`;只 `import` 依赖白名单内的包;副作用在 `useEffect` 里注册并返回 cleanup。
|
|
4
|
+
|
|
5
|
+
> 白名单、windowAlias、CDN 与 antd/dayjs 陷阱见 [dependencies-and-cdn.md](dependencies-and-cdn.md);读写宜搭数据见 [data-bridge-guide.md](data-bridge-guide.md)。
|
|
6
|
+
|
|
7
|
+
## 1. 最小可运行组件(入口 + 本地状态)
|
|
8
|
+
|
|
9
|
+
最小骨架:明确入口导出、用 `useState` 管本地状态,不写任何普通页面契约(无 `renderJsx` / `didMount` / `this.*`)。
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
import React, { useState } from 'react';
|
|
13
|
+
|
|
14
|
+
function YidaComp(props) {
|
|
15
|
+
var s = React.useState(0);
|
|
16
|
+
var count = s[0];
|
|
17
|
+
var setCount = s[1];
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div style={{ padding: 16 }}>
|
|
21
|
+
<p>当前计数:{count}</p>
|
|
22
|
+
<button onClick={function () { setCount(count + 1); }}>加一</button>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default YidaComp;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 2. 带副作用清理(定时器 / 事件监听)
|
|
31
|
+
|
|
32
|
+
`useEffect` 注册的定时器、事件、图表实例,**必须**在返回的 cleanup 里销毁,否则重渲染 / 卸载会泄漏。
|
|
33
|
+
|
|
34
|
+
```jsx
|
|
35
|
+
import React, { useEffect, useState } from 'react';
|
|
36
|
+
|
|
37
|
+
function YidaComp(props) {
|
|
38
|
+
var t = React.useState(0);
|
|
39
|
+
var seconds = t[0];
|
|
40
|
+
var setSeconds = t[1];
|
|
41
|
+
|
|
42
|
+
React.useEffect(function () {
|
|
43
|
+
var timer = setInterval(function () {
|
|
44
|
+
setSeconds(function (prev) { return prev + 1; });
|
|
45
|
+
}, 1000);
|
|
46
|
+
function onResize() { /* 响应窗口变化 */ }
|
|
47
|
+
window.addEventListener('resize', onResize);
|
|
48
|
+
|
|
49
|
+
return function cleanup() {
|
|
50
|
+
clearInterval(timer);
|
|
51
|
+
window.removeEventListener('resize', onResize);
|
|
52
|
+
};
|
|
53
|
+
}, []);
|
|
54
|
+
|
|
55
|
+
return <div style={{ padding: 16 }}>已运行 {seconds}s</div>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default YidaComp;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 3. 可视化:recharts 图表
|
|
62
|
+
|
|
63
|
+
`recharts` 在白名单内(windowAlias `Recharts`)。标准 `import` 即可,CLI 本地编译会把它计入 `importedModules`。图表容器给定高度,避免 0 高度不渲染。图表颜色是 JS 传给库的字符串,**不能硬编码蓝**——用 `readBrandColor` 读平台品牌色,让线条跟随 App 主题(见 [canvas-design-system.md](canvas-design-system.md))。
|
|
64
|
+
|
|
65
|
+
```jsx
|
|
66
|
+
import React from 'react';
|
|
67
|
+
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
|
|
68
|
+
|
|
69
|
+
// 读平台品牌色(跑在真 window,getComputedStyle 可直接解析),缺失时退默认蓝
|
|
70
|
+
function readBrandColor(level, fallback) {
|
|
71
|
+
try {
|
|
72
|
+
var v = getComputedStyle(document.documentElement)
|
|
73
|
+
.getPropertyValue('--color-brand1-' + (level || 6)).trim();
|
|
74
|
+
return v || fallback;
|
|
75
|
+
} catch (e) { return fallback; }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function YidaComp(props) {
|
|
79
|
+
var brand = readBrandColor(6, '#1677ff');
|
|
80
|
+
var data = [
|
|
81
|
+
{ name: '1月', value: 120 },
|
|
82
|
+
{ name: '2月', value: 200 },
|
|
83
|
+
{ name: '3月', value: 150 },
|
|
84
|
+
{ name: '4月', value: 320 },
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div style={{ width: '100%', height: 300, padding: 16 }}>
|
|
89
|
+
<ResponsiveContainer width="100%" height="100%">
|
|
90
|
+
<LineChart data={data}>
|
|
91
|
+
<XAxis dataKey="name" />
|
|
92
|
+
<YAxis />
|
|
93
|
+
<Tooltip />
|
|
94
|
+
<Line type="monotone" dataKey="value" stroke={brand} />
|
|
95
|
+
</LineChart>
|
|
96
|
+
</ResponsiveContainer>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default YidaComp;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 4. 数据拉取组件(接数据桥)
|
|
105
|
+
|
|
106
|
+
结合 [data-bridge-guide.md](data-bridge-guide.md) 的 `useYidaFetch`:同源 `fetch` + `credentials: 'include'` + AbortController 清理。此处只演示消费侧结构。
|
|
107
|
+
|
|
108
|
+
```jsx
|
|
109
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
110
|
+
|
|
111
|
+
function YidaComp(props) {
|
|
112
|
+
var st = React.useState({ loading: true, rows: [], error: null });
|
|
113
|
+
var state = st[0];
|
|
114
|
+
var setState = st[1];
|
|
115
|
+
var abortRef = React.useRef(null);
|
|
116
|
+
|
|
117
|
+
React.useEffect(function () {
|
|
118
|
+
var controller = new AbortController();
|
|
119
|
+
abortRef.current = controller;
|
|
120
|
+
|
|
121
|
+
fetch('/your-connector-proxy/searchFormDatas', {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
credentials: 'include',
|
|
124
|
+
headers: { 'Content-Type': 'application/json' },
|
|
125
|
+
body: JSON.stringify({ appType: props.appType, formUuid: props.formUuid, pageSize: 20 }),
|
|
126
|
+
signal: controller.signal,
|
|
127
|
+
})
|
|
128
|
+
.then(function (r) { if (!r.ok) { throw new Error('HTTP ' + r.status); } return r.json(); })
|
|
129
|
+
.then(function (json) {
|
|
130
|
+
var rows = (json && json.result && json.result.data) || [];
|
|
131
|
+
setState({ loading: false, rows: rows, error: null });
|
|
132
|
+
})
|
|
133
|
+
.catch(function (e) {
|
|
134
|
+
if (e.name === 'AbortError') { return; }
|
|
135
|
+
setState({ loading: false, rows: [], error: e.message });
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return function () { controller.abort(); };
|
|
139
|
+
}, [props.appType, props.formUuid]);
|
|
140
|
+
|
|
141
|
+
if (state.loading) { return <div>加载中…</div>; }
|
|
142
|
+
if (state.error) { return <div style={{ color: 'red' }}>加载失败:{state.error}</div>; }
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<ul style={{ padding: 16 }}>
|
|
146
|
+
{state.rows.map(function (row) {
|
|
147
|
+
return <li key={row.formInstanceId}>{row.title}</li>;
|
|
148
|
+
})}
|
|
149
|
+
</ul>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export default YidaComp;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## 通用自查清单
|
|
157
|
+
|
|
158
|
+
- 入口:源码有 `export default YidaComp`(或返回组件函数),不是只定义局部组件。
|
|
159
|
+
- 依赖:所有 `import` 都在白名单内,能出现在编译结果 `dependencies` 里。
|
|
160
|
+
- 副作用:每个 `useEffect` 的定时器 / 监听 / 图表实例都有 cleanup。
|
|
161
|
+
- 数据:读写走同源 `fetch` + `credentials: 'include'`,无硬编码 Cookie / CSRF / appSecret。
|
|
162
|
+
- 主色:antd 走 `ConfigProvider.colorPrimary`、Tailwind 走 `var(--color-brand1-*)`、图表走 `readBrandColor`,无散落的 `#1677ff` / `bg-blue-500`(见 [canvas-design-system.md](canvas-design-system.md))。
|
|
163
|
+
- 原生字段组件(`EmployeeField` 等):先按 [employeefield-verification.md](employeefield-verification.md) 最小验证,缺证据就降级。
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Code Canvas 主色对齐与视觉落地
|
|
2
|
+
|
|
3
|
+
本文件是 Code Canvas 页面的**实现层**引导:怎么让首次生成的页面跟随宿主 App 的品牌主题色,而不是永远一片 antd 默认蓝。视觉方向怎么定(页面类型、差异化、去 AI 味)是**栈无关**的,走共用的决策层技能 `yida-page-uiux`,本文件只讲 Canvas(React18 + antd + Tailwind)这套栈怎么把主色落地。
|
|
4
|
+
|
|
5
|
+
> 决策层:读 `yida-page-uiux/SKILL.md` 定视觉方向(先做 Step 0 导航形态判定,再定工作台/仪表盘/列表/详情、5 维差异化、去 AI 味、禁 emoji)。
|
|
6
|
+
> 实现层:本文件负责把「主色跟随 App 品牌」落到 antd token / Tailwind / 图表。
|
|
7
|
+
|
|
8
|
+
> **前提是导航可见**:跟随品牌主色是为了跟应用框架融合。若页面隐藏了应用导航(`isRenderNav=false`,沉浸/独立/门户/大屏,由 `yida-page-uiux` Step 0 判定),主色相可自立、不必严格跟品牌——此时把下文 `readBrandColor` 的取值换成自定的主色即可(antd `colorPrimary` / 图表色都改喂自定色),**语义色仍固定、去 AI 味红线仍生效**。拿不准就按「跟随品牌」这个更安全的默认走。
|
|
9
|
+
|
|
10
|
+
## 核心事实:CSS 变量能穿透,antd token 不能
|
|
11
|
+
|
|
12
|
+
Canvas 的 `runtimeCode` 在**宿主页真实 `window`** 里 `new Function` 执行(见 SKILL.md「运行时事实」),组件挂在宿主 DOM 树内。由此得到主色落地的分界:
|
|
13
|
+
|
|
14
|
+
| 消费方 | 品牌色怎么给 | 原因 |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| 普通 DOM / Tailwind 元素(`style` / `className`) | **直接用 CSS 变量** `var(--color-brand1-6)` | CSS 变量沿 DOM 树级联,Canvas 节点在宿主树内,能读到平台注入的 `--color-brand1-*` |
|
|
17
|
+
| antd 组件(Button / Table / Tabs…) | **JS 解析成真实色值**喂 `ConfigProvider.theme.token.colorPrimary` | antd 的色板(hover/active/disabled)由 JS 算法从一个真实颜色推导,`var(...)` 是字符串塞不进算法 |
|
|
18
|
+
| JS 消费的颜色:recharts `stroke`/`fill`、canvas 绘制、图表配色数组 | **JS 解析成真实色值** | 传给库的是运行时字符串,不走 CSS 级联 |
|
|
19
|
+
|
|
20
|
+
所以只有「JS 要拿到真实颜色」的场景才需要读值,其余直接用 CSS 变量最省事。
|
|
21
|
+
|
|
22
|
+
## 读品牌色的 helper(JS 消费场景用)
|
|
23
|
+
|
|
24
|
+
因为跑在真 window,直接读根节点计算样式即可。带兜底,读不到时退平台默认蓝。
|
|
25
|
+
|
|
26
|
+
```jsx
|
|
27
|
+
// 品牌色阶:1 最浅 → 6 主色 → 10 最深,与平台 --color-brand1-* 对齐
|
|
28
|
+
function readBrandColor(level, fallback) {
|
|
29
|
+
try {
|
|
30
|
+
var el = document.documentElement;
|
|
31
|
+
var v = getComputedStyle(el).getPropertyValue('--color-brand1-' + (level || 6)).trim();
|
|
32
|
+
return v || fallback;
|
|
33
|
+
} catch (e) {
|
|
34
|
+
return fallback;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// hook 形式:首帧同步取值,无闪烁
|
|
39
|
+
function useBrandColor(level, fallback) {
|
|
40
|
+
var s = React.useState(function () { return readBrandColor(level, fallback); });
|
|
41
|
+
return s[0];
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
> ⚠️ **变量作用域**:若平台把 `--color-brand1-*` 定义在某个容器而非 `:root`,`document.documentElement` 可能读到空串 → 命中 fallback。更稳的做法是给组件根节点挂 `ref`,在 `useEffect` 里读 `getComputedStyle(rootRef.current)`(组件节点一定在变量作用域内),读到后 `setState` 触发一次重渲染。先用 `documentElement` 同步取,空串再降级到 ref 方案即可。
|
|
46
|
+
|
|
47
|
+
## antd:ConfigProvider 注入 colorPrimary
|
|
48
|
+
|
|
49
|
+
用 `readBrandColor` 取主色,交给 `ConfigProvider`,antd 会自动推导 hover/active/disabled 整套色板。语义色(success/warning/error)用 antd 默认,不覆盖,保证语义稳定。
|
|
50
|
+
|
|
51
|
+
```jsx
|
|
52
|
+
import React from 'react';
|
|
53
|
+
import { ConfigProvider, Button, Table } from 'antd';
|
|
54
|
+
|
|
55
|
+
function readBrandColor(level, fallback) {
|
|
56
|
+
try {
|
|
57
|
+
var v = getComputedStyle(document.documentElement)
|
|
58
|
+
.getPropertyValue('--color-brand1-' + (level || 6)).trim();
|
|
59
|
+
return v || fallback;
|
|
60
|
+
} catch (e) { return fallback; }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function YidaComp(props) {
|
|
64
|
+
var colorPrimary = readBrandColor(6, '#1677ff'); // 缺失时退平台默认蓝
|
|
65
|
+
return (
|
|
66
|
+
<ConfigProvider
|
|
67
|
+
theme={{
|
|
68
|
+
token: {
|
|
69
|
+
colorPrimary: colorPrimary, // 主色跟随 App 品牌
|
|
70
|
+
borderRadius: 8, // 圆角等非主色 token 可按视觉方向调
|
|
71
|
+
},
|
|
72
|
+
// 不覆盖 colorSuccess/colorWarning/colorError,语义色保持固定
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
<div style={{ padding: 16 }}>
|
|
76
|
+
<Button type="primary">主操作</Button>
|
|
77
|
+
</div>
|
|
78
|
+
</ConfigProvider>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default YidaComp;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**要点**:`ConfigProvider` 包在组件最外层,页面内所有 antd 组件才统一吃到品牌色。只设 `colorPrimary` 一个入口,不要逐组件手写颜色。
|
|
86
|
+
|
|
87
|
+
## Tailwind:CSS 变量直接用
|
|
88
|
+
|
|
89
|
+
Canvas 节点在宿主树内,Tailwind 运行时对普通元素直接用 arbitrary value 引用 CSS 变量即可,**不需要 JS**:
|
|
90
|
+
|
|
91
|
+
```jsx
|
|
92
|
+
// 主色文字 / 背景 / 边框,直接引平台变量,跟随 App 主题
|
|
93
|
+
<div className="text-[var(--color-brand1-6)] border border-[var(--color-brand1-3)]">…</div>
|
|
94
|
+
<button className="bg-[var(--color-brand1-6)] hover:bg-[var(--color-brand1-5)] text-white rounded-lg px-4 py-2">
|
|
95
|
+
主操作
|
|
96
|
+
</button>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
色阶对应(与 native `design-system.md` 一致):主色 `brand1-6`、填充按钮 hover 亮一档 `brand1-5`、按下深一档 `brand1-7`、通用浅色 hover 底 `brand1-1`、选中/标签浅底 `brand1-2`。
|
|
100
|
+
|
|
101
|
+
## 图表 / recharts:用解析后的品牌色
|
|
102
|
+
|
|
103
|
+
图表颜色是 JS 传给库的字符串,必须用 `readBrandColor`,不能硬编码 `#1677ff`。
|
|
104
|
+
|
|
105
|
+
```jsx
|
|
106
|
+
import React from 'react';
|
|
107
|
+
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
|
|
108
|
+
|
|
109
|
+
function readBrandColor(level, fallback) {
|
|
110
|
+
try {
|
|
111
|
+
var v = getComputedStyle(document.documentElement)
|
|
112
|
+
.getPropertyValue('--color-brand1-' + (level || 6)).trim();
|
|
113
|
+
return v || fallback;
|
|
114
|
+
} catch (e) { return fallback; }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function YidaComp(props) {
|
|
118
|
+
var brand = readBrandColor(6, '#1677ff');
|
|
119
|
+
var data = [
|
|
120
|
+
{ name: '1月', value: 120 }, { name: '2月', value: 200 },
|
|
121
|
+
{ name: '3月', value: 150 }, { name: '4月', value: 320 },
|
|
122
|
+
];
|
|
123
|
+
return (
|
|
124
|
+
<div style={{ width: '100%', height: 300, padding: 16 }}>
|
|
125
|
+
<ResponsiveContainer width="100%" height="100%">
|
|
126
|
+
<LineChart data={data}>
|
|
127
|
+
<XAxis dataKey="name" />
|
|
128
|
+
<YAxis />
|
|
129
|
+
<Tooltip />
|
|
130
|
+
<Line type="monotone" dataKey="value" stroke={brand} />
|
|
131
|
+
</LineChart>
|
|
132
|
+
</ResponsiveContainer>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default YidaComp;
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
多色系列需要区分时,用品牌色 + 中性色/语义色,不要整排饱和撞色(见 `yida-page-uiux` 去 AI 味清单)。
|
|
141
|
+
|
|
142
|
+
## 自查清单(主色相关)
|
|
143
|
+
|
|
144
|
+
- 页面最外层有 `ConfigProvider` 且 `token.colorPrimary` 来自 `readBrandColor`,不是硬编码色值。
|
|
145
|
+
- Tailwind 主色类用 `var(--color-brand1-*)`,没有散落的 `#1677ff` / `bg-blue-500`。
|
|
146
|
+
- 图表 / canvas 绘制颜色走 `readBrandColor`,无硬编码蓝。
|
|
147
|
+
- 语义色(成功/警告/错误)保持 antd 默认或平台语义变量,未被主色覆盖。
|
|
148
|
+
- 视觉方向已按 `yida-page-uiux` 决策:不是默认蓝 + 大圆角 + emoji 的 AI 味套版。
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Code Canvas 数据桥指南(自写 HTTP 读写宜搭数据)
|
|
2
|
+
|
|
3
|
+
Code Canvas 运行时**没有** `this.utils.yida.*` / `dataSourceMap` / `this.$(fieldId)` 实例数据桥(核实自 `factory.tsx`:物料只透传 `code / runtimeCode / importedModules / pageType`,`YidaComp` 是普通函数组件,wrapper 只注入 `window`)。因此 `YidaComp` 要读写宜搭数据,只能**自己补一座 HTTP 桥**。本文件给出干净、可复用、合规的写法。
|
|
4
|
+
|
|
5
|
+
## 三条数据路径,先选对
|
|
6
|
+
|
|
7
|
+
| 路径 | 是否可在浏览器(Canvas)直接调 | 说明 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| 宜搭开放 API(OpenAPI,`appKey`/`appSecret` 签名) | **不可** | 需服务端签名;在浏览器里必然泄露 secret。只能由后端 / 连接器代理调,Canvas 不直连。 |
|
|
10
|
+
| 平台已配置**连接器**(HTTP 连接器暴露的同源代理端点) | **推荐** | 同源 `fetch(url, { credentials: 'include' })` 带 cookie 即可,鉴权与密钥留在平台侧,符合数据源治理。 |
|
|
11
|
+
| 内部表单数据端点(同源、依赖登录 cookie + CSRF) | 可,但要谨慎 | 与 native `this.utils.yida.searchFormDatas` 命中的是同类端点;需自行带 CSRF token,端点随环境可能变化,不要硬编码跨域绝对地址。 |
|
|
12
|
+
|
|
13
|
+
选路原则:**优先走连接器代理**(编码规则 #6「不要绕过数据源治理」)。真需要直连内部端点时,只用**同源相对路径** + `credentials: 'include'`,绝不在源码里硬编码 Cookie / CSRF / appSecret。
|
|
14
|
+
|
|
15
|
+
## 可复用读数据 Hook
|
|
16
|
+
|
|
17
|
+
```jsx
|
|
18
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
19
|
+
|
|
20
|
+
// 通用:同源、带 cookie、可取消、带 loading/error 的 fetch hook
|
|
21
|
+
function useYidaFetch(buildRequest, deps) {
|
|
22
|
+
var stateHook = React.useState({ loading: false, data: null, error: null });
|
|
23
|
+
var state = stateHook[0];
|
|
24
|
+
var setState = stateHook[1];
|
|
25
|
+
var abortRef = React.useRef(null);
|
|
26
|
+
|
|
27
|
+
var run = React.useCallback(function () {
|
|
28
|
+
if (abortRef.current) { abortRef.current.abort(); }
|
|
29
|
+
var controller = new AbortController();
|
|
30
|
+
abortRef.current = controller;
|
|
31
|
+
setState({ loading: true, data: null, error: null });
|
|
32
|
+
|
|
33
|
+
var req = buildRequest(); // { url, method, body }
|
|
34
|
+
return fetch(req.url, {
|
|
35
|
+
method: req.method || 'GET',
|
|
36
|
+
credentials: 'include', // 同源带登录 cookie,不手动塞 Cookie 头
|
|
37
|
+
headers: { 'Content-Type': 'application/json' },
|
|
38
|
+
body: req.body ? JSON.stringify(req.body) : undefined,
|
|
39
|
+
signal: controller.signal,
|
|
40
|
+
})
|
|
41
|
+
.then(function (resp) {
|
|
42
|
+
if (!resp.ok) { throw new Error('HTTP ' + resp.status); }
|
|
43
|
+
return resp.json();
|
|
44
|
+
})
|
|
45
|
+
.then(function (json) {
|
|
46
|
+
// 宜搭返回体通常形如 { success, result / data, errorMsg },按真实结构解析
|
|
47
|
+
if (json && json.success === false) {
|
|
48
|
+
throw new Error(json.errorMsg || 'request failed');
|
|
49
|
+
}
|
|
50
|
+
setState({ loading: false, data: json.result || json.data || json, error: null });
|
|
51
|
+
})
|
|
52
|
+
.catch(function (err) {
|
|
53
|
+
if (err.name === 'AbortError') { return; }
|
|
54
|
+
setState({ loading: false, data: null, error: err.message });
|
|
55
|
+
});
|
|
56
|
+
}, deps || []);
|
|
57
|
+
|
|
58
|
+
React.useEffect(function () {
|
|
59
|
+
run();
|
|
60
|
+
return function () { if (abortRef.current) { abortRef.current.abort(); } };
|
|
61
|
+
}, deps || []);
|
|
62
|
+
|
|
63
|
+
return { loading: state.loading, data: state.data, error: state.error, refetch: run };
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
要点:
|
|
68
|
+
|
|
69
|
+
- `credentials: 'include'` 让浏览器带上同源登录态,**不要**手动拼 `Cookie` / `x-csrf-token` 硬编码值;如需 CSRF,从页面已有上下文动态读取,别写死。
|
|
70
|
+
- 用 `AbortController` 在卸载 / 依赖变化时取消,避免 setState-after-unmount(对应编码规则 #5 副作用清理)。
|
|
71
|
+
- 解析响应按**真实返回结构**处理(宜搭常见 `{ success, result, errorMsg }`),不要假设字段名。
|
|
72
|
+
|
|
73
|
+
## 在组件里用
|
|
74
|
+
|
|
75
|
+
```jsx
|
|
76
|
+
function YidaComp(props) {
|
|
77
|
+
var appType = props.appType || '<APP_TYPE>'; // 来自 props 或页面约定
|
|
78
|
+
var formUuid = props.formUuid || '<FORM_UUID>';
|
|
79
|
+
|
|
80
|
+
var q = useYidaFetch(function () {
|
|
81
|
+
return {
|
|
82
|
+
url: '/your-connector-proxy/searchFormDatas', // 连接器同源代理端点(示意)
|
|
83
|
+
method: 'POST',
|
|
84
|
+
body: { appType: appType, formUuid: formUuid, pageSize: 20, pageNumber: 1 },
|
|
85
|
+
};
|
|
86
|
+
}, [appType, formUuid]);
|
|
87
|
+
|
|
88
|
+
if (q.loading) { return <div>加载中…</div>; }
|
|
89
|
+
if (q.error) { return <div style={{ color: 'red' }}>加载失败:{q.error}</div>; }
|
|
90
|
+
|
|
91
|
+
var rows = (q.data && q.data.data) || [];
|
|
92
|
+
return (
|
|
93
|
+
<ul>
|
|
94
|
+
{rows.map(function (row) { return <li key={row.formInstanceId}>{row.title}</li>; })}
|
|
95
|
+
</ul>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default YidaComp;
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`url`、`body` 字段按你实际接的连接器 / 端点契约填;上面是结构示意,不是可直接跑的真实端点。
|
|
103
|
+
|
|
104
|
+
## 写数据(新增 / 更新 / 删除)额外红线
|
|
105
|
+
|
|
106
|
+
- **确认再写**:删除、批量更新等不可逆操作,先让用户在 UI 里显式确认,不在 `useEffect` 里静默触发。
|
|
107
|
+
- **幂等**:提交按钮加 loading 锁与去重键,避免重复写入。
|
|
108
|
+
- **权限**:写操作是否允许由平台权限决定,浏览器侧不要伪造身份;失败按后端返回的 `errorMsg` 提示,不吞错。
|
|
109
|
+
- **不硬编码密钥**:任何 `appSecret` / 签名逻辑都必须留在服务端 / 连接器,Canvas 源码里只出现同源相对路径与业务参数。
|
|
110
|
+
|
|
111
|
+
## 与其他技能的边界
|
|
112
|
+
|
|
113
|
+
- 需要**创建 / 管理 HTTP 连接器** → `yida-connector`。
|
|
114
|
+
- 需要**通过数据源调用连接器**的页面级配置 → `yida-data-source-connectors`。
|
|
115
|
+
- 页面**强依赖实例数据桥**(表单内字段双向绑定、提交流程深度耦合)→ 回退 `yida-custom-page`(native 免费提供该桥)。
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Code Canvas 依赖白名单与 CDN 加载
|
|
2
|
+
|
|
3
|
+
本文件承载 Code Canvas 的依赖白名单、windowAlias 映射、编译改写规则,以及「预发正常、线上 `antd is not defined`」的根因与物料侧修复方向。核实自 `vc-deep-yida/src/components/yida-code-canvas` 源码(`dependencies.ts` / `factory.tsx`)。
|
|
4
|
+
|
|
5
|
+
## 依赖白名单(核实自 `yida-code-canvas/dependencies.ts`)
|
|
6
|
+
|
|
7
|
+
编译阶段把 `import` 改写为下列白名单的 `windowAlias` 引用,运行时按 `windowAlias` 加载到 `window` 上。带 `${cdn}` 的资源在预发走 `dev.g.alicdn.com`、线上走 `g.alicdn.com`(由 `isProd` 决定,见下方「预发/线上差异」)。
|
|
8
|
+
|
|
9
|
+
| 包名 | windowAlias | 资源 |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| react | `React` | g.alicdn.com react 18.3.1 |
|
|
12
|
+
| react-dom | `ReactDOM` | g.alicdn.com react-dom 18.3.1 |
|
|
13
|
+
| antd | `antd` | g.alicdn.com antd **5.23.3** `antd-with-locales.js`(**依赖外部 `window.dayjs`**)|
|
|
14
|
+
| @ant-design/icons | `icons` | g.alicdn.com ant-design-icons 5.5.1 |
|
|
15
|
+
| dayjs | `dayjs` | 仅 `assetUrlPlaceholder`(dev CDN 的 locale 文件),**无 `assetUrl`** → 画布自身不会加载 dayjs core,依赖宿主提供 |
|
|
16
|
+
| ahooks | `ahooks` | `${cdn}/platform/yida-assets/ahooks.js`(默认追加) |
|
|
17
|
+
| d3 | `d3` | g.alicdn.com d3 7.9.0 |
|
|
18
|
+
| recharts | `Recharts` | g.alicdn.com recharts 2.15.0 |
|
|
19
|
+
| @radix-ui/themes | `Radix` | `${cdn}/.../radix.js` + `radix.css` |
|
|
20
|
+
| lucide-react | `DynamicIcon` | `${cdn}/.../lucideReact.js` |
|
|
21
|
+
| framer-motion | `FramerMotion` | `${cdn}/.../framerMotion.js` |
|
|
22
|
+
| yida-plugin-markdown | `YidaMarkdown` | moduleFederation 0.0.4 |
|
|
23
|
+
|
|
24
|
+
新增依赖必须同时满足:① 编译能把 import 抽进 `importedModules` 并映射到 windowAlias(见 `canvas-compile.js` 的 `MODULE_ALIAS_MAP`);② 上表或平台运行时能把依赖加载到 window;③ `runtimeCode` 引用的变量名与 windowAlias 一致;④ CSS 资源可加载,否则组件可能渲染但样式/弹层异常。白名单外的包(yida-utils、`@ali/deep`、原生字段组件等)不能 `import`。
|
|
25
|
+
|
|
26
|
+
编译位置:OpenYida CLI **本地用 Babel** 把源码转译为 `runtimeCode` + `importedModules`(`import`→`window.<别名>`、`export default`→`YidaComp`、依赖名正则抽取),不调用任何在线编译服务,因此不依赖登录态、不经过风控。别名映射逐条镜像自 `dependencies.ts` 的 `getModuleAliasMap()`;运行时消费契约见 `factory.tsx`(`new Function` 执行 `runtimeCode` 取 `YidaComp`)。
|
|
27
|
+
|
|
28
|
+
## 预发正常、线上报 `antd is not defined` 的根因
|
|
29
|
+
|
|
30
|
+
现象:预发(`pre-*.alibaba-inc.com`)页面正常;线上(如 `*.aliwork.com`)白屏,控制台报 `antd is not defined` + `antd-with-locales.js` / `dayjs.js` 抛 `Cannot read properties of undefined (reading 'extend')`。
|
|
31
|
+
|
|
32
|
+
根因(核实自源码):
|
|
33
|
+
|
|
34
|
+
- `antd-with-locales.js`(UMD)初始化时会执行 `dayjs.extend(...)`,其中 `dayjs` 是**外部依赖**,解析为 `window.dayjs`。若加载 antd 前 `window.dayjs` 未就绪 → `undefined.extend` 抛错 → antd 模块未定义 → 业务代码引用 `antd` 报 `antd is not defined`。
|
|
35
|
+
- 正确顺序见 `vc-procode-comp/view.js`:先 `window.dayjs = window.__YCCommon?.Common?.dayjs` 再注入 antd。**但 `yida-code-canvas/factory.tsx` 原实现没有这步**:它 `loadDependencies` 时对 antd 只 `push(assetUrl)`,而 dayjs 条目只有 `assetUrlPlaceholder`、无 `assetUrl`,画布自身根本不会加载 dayjs;且加载前 `if (window[windowAlias]) return` 会跳过已存在依赖。
|
|
36
|
+
- 于是环境差异被放大:
|
|
37
|
+
- **预发**:宿主设计器 / 同页其它 procode 组件已把 `window.antd`(含 dayjs)挂到全局 → 画布命中 `if (window.antd) return`,直接复用宿主 antd,正常。
|
|
38
|
+
- **线上该租户域**:宿主未预置 `window.antd` / `window.dayjs` → 画布现加载 `antd-with-locales.js`,但没有先设 `window.dayjs` → antd 初始化即抛错。(浏览器 debugger 里可见 `finalDependencies` 含 `antd` 却不含 `dayjs`,`window.dayjs` 为 `undefined`,即命中此缺陷。)
|
|
39
|
+
- 附带因素:`isProd = location.hostname.indexOf('pre-') === -1` 是脆弱的域名启发式,非 `pre-` 前缀域一律判为线上,走 `g.alicdn.com` 前缀;antd 本身两端都硬编码 `g.alicdn.com`,故该差异不是 antd 主因,但会影响 `${cdn}` 类依赖(ahooks/radix/lucide/framer-motion/tailwind)的可达性。
|
|
40
|
+
|
|
41
|
+
## 物料侧修复方向
|
|
42
|
+
|
|
43
|
+
这是**物料层缺陷**(`factory.tsx` 未在 antd 前保证 `window.dayjs`),页面作者难以在 Canvas 源码内自救(`import dayjs` 也因 dayjs 无 `assetUrl` 不落地)。正确修复在 `factory.tsx` 的 `loadDependencies`:当 `finalDependencies` 含 `antd` 且 `window.dayjs` 未就绪时,**先**复用宿主 `window.__YCCommon?.Common?.dayjs`,否则**单独 `await` 加载 dayjs core**(如 `g.alicdn.com/code/lib/dayjs/<ver>/dayjs.min.js`),确保早于 antd 脚本执行,再进入原有 `assetsUrls` 批量加载。要点:dayjs 必须在**独立且被 await 的加载步骤**里先就绪,避免与 antd 放同一批并行加载产生竞态。
|
|
44
|
+
|
|
45
|
+
临时规避(未改物料时):让宿主页先行加载 antd/dayjs,或页面改用不依赖 dayjs 的轻组件(避开 DatePicker、时间 locale 等触发 `dayjs.extend` 的能力)。
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Code Canvas 运行时事实与 EmployeeField 验证
|
|
2
|
+
|
|
3
|
+
本文件承载 `yida-canvas-custom-page` 的运行时细节与原生组件验证示例,SKILL.md 只保留摘要与链接。
|
|
4
|
+
|
|
5
|
+
## 运行时事实(以 `vc-deep-yida/src/components/yida-code-canvas` 源码为准)
|
|
6
|
+
|
|
7
|
+
- `factory.tsx` 读取 `runtimeCode` 和 `importedModules`;用 `<Component {...props} />` 渲染,**只透传** `code / runtimeCode / importedModules / pageType` 四个属性。
|
|
8
|
+
- `prototype.tsx` 的物料配置也只声明这四个属性,**没有** `dataSource` / `dataSourceMap` 绑定。
|
|
9
|
+
- 代码经 `new Function` 包裹执行,wrapper 只注入 `window`(`iframeWindow` / `parentWindow`),**不注入 `this` 上下文**。因此 `YidaComp` 是普通 React 函数组件,**`this.utils.yida.*`、`this.dataSourceMap`、`export function didMount()` 等普通页面契约都不可用**。
|
|
10
|
+
- 代码执行后必须返回 `YidaComp`、`YidaComp.default` 或组件函数。
|
|
11
|
+
- Canvas 要读宜搭数据,只能在组件内**自写 HTTP 调用**(fetch 宜搭开放 API / 连接器)或依赖 props 注入,即“自己补一座数据桥”。需要平台数据桥的数据驱动页请回退 `yida-custom-page`。
|
|
12
|
+
|
|
13
|
+
> 📦 依赖白名单表、windowAlias 映射、编译端点,以及「预发正常、线上 `antd is not defined`」根因与物料侧修复方向已拆到 [dependencies-and-cdn.md](dependencies-and-cdn.md)。
|
|
14
|
+
|
|
15
|
+
## 宜搭原生组件判断
|
|
16
|
+
|
|
17
|
+
`EmployeeField`、`SelectField`、`DepartmentSelectField`、`AttachmentField` 等宜搭原生字段组件不能因为表单 Schema 支持同名字段,就默认在 Code Canvas JSX 中可用。使用前必须确认:
|
|
18
|
+
|
|
19
|
+
- `importedModules` 包含可解析的宜搭组件库,例如 `@ali/deep` 或平台约定的字段组件包。
|
|
20
|
+
- Code Canvas 运行时有对应依赖映射和 CSS。
|
|
21
|
+
- 组件所需页面上下文、组织权限、弹层容器、移动端版本均可用。
|
|
22
|
+
- `onChange` 返回值结构能被后续数据保存或查询逻辑消费。
|
|
23
|
+
|
|
24
|
+
如果缺少任一证据,降级为普通 UI:用 antd 或自定义控件选择候选用户,值只存已知 userId / unionId / 文本快照,不伪造成原生字段组件。
|
|
25
|
+
|
|
26
|
+
## EmployeeField 验证示例
|
|
27
|
+
|
|
28
|
+
示例只用于 Code Canvas 验证,不要复制到普通 `.oyd.jsx` 自定义页:
|
|
29
|
+
|
|
30
|
+
```jsx
|
|
31
|
+
import React, { useState } from 'react';
|
|
32
|
+
import { EmployeeField } from '@ali/deep';
|
|
33
|
+
|
|
34
|
+
function YidaComp(props) {
|
|
35
|
+
var ReactRef = React;
|
|
36
|
+
var state = ReactRef.useState([]);
|
|
37
|
+
var value = state[0];
|
|
38
|
+
var setValue = state[1];
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div style={{ padding: 16 }}>
|
|
42
|
+
<EmployeeField
|
|
43
|
+
label="负责人"
|
|
44
|
+
placeholder="请选择负责人"
|
|
45
|
+
multiple={false}
|
|
46
|
+
value={value}
|
|
47
|
+
onChange={(nextValue) => {
|
|
48
|
+
console.log('employee value', nextValue);
|
|
49
|
+
setValue(nextValue || []);
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
<pre style={{ marginTop: 12, fontSize: 12 }}>
|
|
53
|
+
{JSON.stringify(value, null, 2)}
|
|
54
|
+
</pre>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default YidaComp;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
验收点:
|
|
63
|
+
|
|
64
|
+
- 设计器属性中 `importedModules` 是否包含 `@ali/deep` 或等价字段组件库。
|
|
65
|
+
- 页面控制台没有 `EmployeeField is not defined`、样式缺失、弹层挂载错误。
|
|
66
|
+
- 成员弹层能打开、搜索、选择、清空。
|
|
67
|
+
- PC 和移动端都能完成选择。
|
|
68
|
+
- `onChange` 输出的值结构被记录下来,后续代码按真实结构处理。
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yida-canvas-upgrade
|
|
3
|
+
description: 将 OpenYida 原普通自定义页面链路升级/迁移为宜搭 Code Canvas 链路。适用于用户要求“从 openyida 原来的链路升级成 canvas 链路”“普通 .oyd.jsx / Jsx 页面转 Code Canvas”“renderJsx 页面迁移到 YidaCodeCanvas”“把 openyida publish 链路改为 runtimeCode + importedModules 链路”。不要用于从零编写 Code Canvas 页面;新建 Code Canvas 页面使用 yida-canvas-custom-page。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OpenYida 普通页面升级到 Code Canvas
|
|
7
|
+
|
|
8
|
+
## 核心定位
|
|
9
|
+
|
|
10
|
+
本技能是**存量页面迁移链路**:把已有的 OpenYida 原生自定义页转换到 Code Canvas 链路,产出等价的 React18 函数组件源码并承载到 `YidaCodeCanvas`。
|
|
11
|
+
|
|
12
|
+
- 源链路:OpenYida 原生自定义页,通常是 `project/pages/src/*.oyd.jsx`、`export function renderJsx()`、`_customState`、`this.utils.yida.*`,`openyida publish` 发布为 `Jsx` 组件。
|
|
13
|
+
- 目标链路:宜搭 Code Canvas,页面 Schema 中承载 `YidaCodeCanvas` 组件,组件属性包含 `code`、`runtimeCode`、`importedModules`,运行时执行 `YidaComp`。
|
|
14
|
+
|
|
15
|
+
从零新写 Code Canvas 页面切到 `yida-canvas-custom-page`;继续维护原生 OpenYida 页面切到 `yida-custom-page`。
|
|
16
|
+
|
|
17
|
+
## 迁移前必须确认
|
|
18
|
+
|
|
19
|
+
| 检查项 | 命令 / 做法 |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| 登录态只读验证 | `openyida env --json` + `openyida login --check-only --json` |
|
|
22
|
+
| 原页面源码 | 找到 `.oyd.jsx` / `.jsx` 源文件;不要只看 dist 编译产物 |
|
|
23
|
+
| 目标页面 Schema | 先执行 `openyida get-schema <appType> <formUuid>`,再用 create_file / Write / file edit tool 按需保存到 `<projectRoot>/.cache/openyida/canvas-upgrade/<name>-schema.json`;不要用 shell 重定向 |
|
|
24
|
+
| Code Canvas 发布能力 | 已内置:产出的 `.canvas.jsx` 用 `openyida publish <源文件> <appType> <formUuid>` 即自动走 Canvas 链路(`.canvas.jsx` 扩展名识别,CLI 本地用 Babel 编译出 `runtimeCode` + `importedModules` 并写 `YidaCodeCanvas` Schema),无需设计器手工添加 |
|
|
25
|
+
| 原页面数据依赖 | 列出 `this.utils.yida.*`、`this.dataSourceMap.*`、连接器、外部脚本、全局变量 |
|
|
26
|
+
|
|
27
|
+
`openyida publish` 已能把 `.canvas.jsx` 发布为 `YidaCodeCanvas` 页面;升级的真正门槛在**源码等价改写**(尤其 `this.utils.yida.*` / `dataSourceMap` 无 Canvas 对应物),而非发布能力。改写受阻、无法保证等价时,只交付源码草案和迁移报告,不要声称已完成升级。
|
|
28
|
+
|
|
29
|
+
## 可迁移性分级
|
|
30
|
+
|
|
31
|
+
| 原页面能力 | 迁移建议 |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| 纯展示 UI、静态数据、轻交互 | 可直接迁为 `YidaComp` 函数组件 |
|
|
34
|
+
| 普通 React 状态 / `_customState` | 改为 `useState` / `useMemo` / `useEffect` |
|
|
35
|
+
| `didMount` / `didUnmount` | 改为 `useEffect(() => { ...; return cleanup; }, [])` |
|
|
36
|
+
| ECharts / d3 / recharts | 优先使用 Code Canvas 依赖白名单;不在白名单则先补依赖或降级 |
|
|
37
|
+
| `this.utils.yida.*` 表单 API | 不能默认照搬;需要通过 props、数据源注入、或保留普通页面链路 |
|
|
38
|
+
| `this.dataSourceMap.*` | 需要确认 Code Canvas props 是否透传数据源;未验证前不要迁移为可运行承诺 |
|
|
39
|
+
| `this.utils.toast/dialog/router` | 改为 antd Message/Modal 或由 props 注入的能力;需验证 |
|
|
40
|
+
| 字段组件如 `EmployeeField` | 按 `yida-canvas-custom-page` 的依赖映射规则先做最小验证 |
|
|
41
|
+
| `openyida publish` | 仍是最终发布方式:native 源码发普通 `Jsx` 页面,`.canvas.jsx` 源码(或加 `--canvas`)发 `YidaCodeCanvas` 页面。迁移就是把源码改写成 `.canvas.jsx` 后重新 `publish` |
|
|
42
|
+
|
|
43
|
+
## 输出产物位置
|
|
44
|
+
|
|
45
|
+
- 可维护的 Code Canvas 源码放在 `project/pages/src/<页面名>.canvas.jsx`。
|
|
46
|
+
- 临时分析、Schema 回读、编译返回值、依赖清单放在 `.cache/openyida/canvas-upgrade/`。
|
|
47
|
+
- 不要把一次性 `runtimeCode`、接口抓包、调试 JSON 放到仓库根目录。
|
|
48
|
+
|
|
49
|
+
## 迁移步骤
|
|
50
|
+
|
|
51
|
+
1. **盘点原页面**
|
|
52
|
+
- 读取源文件,不从 `project/pages/dist/*.js` 反推。
|
|
53
|
+
- 列出导出的函数、顶层常量、`_customState` 字段、生命周期、API 调用、第三方脚本。
|
|
54
|
+
- 标记依赖 `this` 的能力:`this.utils`、`this.dataSourceMap`、`this.state`、`this.setState`。
|
|
55
|
+
|
|
56
|
+
2. **判断是否能自动升级**
|
|
57
|
+
- 如果页面主要是 UI + 本地状态 + 白名单图表库,可以继续生成 Canvas 源码。
|
|
58
|
+
- 如果页面强依赖 `this.utils.yida.*` 或设计器数据源,但 Code Canvas props 未验证,先输出阻塞点和迁移计划。
|
|
59
|
+
- 如果目标诉求其实是字段结构、流程、报表、权限,停止迁移,切到对应配置型技能。
|
|
60
|
+
|
|
61
|
+
3. **生成 Canvas 源码**
|
|
62
|
+
- 把 `export function renderJsx()` 的 JSX 提取到 `function YidaComp(props)`。
|
|
63
|
+
- 把 `_customState` 拆为 React state;复杂派生数据用 `useMemo`。
|
|
64
|
+
- 把生命周期副作用放进 `useEffect` 并返回 cleanup。
|
|
65
|
+
- 把普通页面 API 胶水抽成 props 调用或明确的 TODO,不要假装可用。
|
|
66
|
+
- 保持 UI 结构和文案尽量不变,先做等价迁移,再考虑重构。
|
|
67
|
+
|
|
68
|
+
4. **处理依赖**
|
|
69
|
+
- import 只使用 Code Canvas 依赖白名单内的包(能被 CLI 编译识别并注入 `importedModules`)。
|
|
70
|
+
- 对照 `yida-canvas-custom-page` 的依赖规则检查 `importedModules`。
|
|
71
|
+
- 外部 CDN 脚本要么换成白名单依赖,要么在最小验证页确认可加载。
|
|
72
|
+
|
|
73
|
+
5. **发布 / 验证 Schema**
|
|
74
|
+
- 用 `openyida publish project/pages/src/<页面名>.canvas.jsx <appType> <formUuid>` 发布:`.canvas.jsx` 自动走 Canvas 链路,CLI 本地用 Babel 编译出 `runtimeCode` + `importedModules` 并写入 `YidaCodeCanvas` Schema(`code` / `runtimeCode` / `importedModules` 由 CLI 填充,无需手工拼 Schema)。
|
|
75
|
+
- 源码改写受阻、无法保证等价时,只交付 `.canvas.jsx` 草案和升级报告,不要发布覆盖线上页。
|
|
76
|
+
- 发布后回读 Schema,确认组件树中存在 `YidaCodeCanvas`,且 `runtimeCode` 不是空。
|
|
77
|
+
|
|
78
|
+
6. **运行验收**
|
|
79
|
+
- 打开真实页面,确认首屏非空。
|
|
80
|
+
- 控制台无依赖缺失、`YidaComp` 未定义、CSS 缺失等错误。
|
|
81
|
+
- 核心交互能触发;移动端至少做一次宽度/弹层检查。
|
|
82
|
+
- 如涉及 `EmployeeField`,按 `yida-canvas-custom-page` 的 EmployeeField 验收点逐项验证。
|
|
83
|
+
|
|
84
|
+
## 转换模式与升级报告
|
|
85
|
+
|
|
86
|
+
- 转换核心:`_customState` → `useState`/`useMemo`;`didMount`/`didUnmount` → `useEffect` + cleanup;删掉 `forceUpdate`/timestamp 强刷;`this.utils.yida.*`/`dataSourceMap` 抽成 props 或明确 TODO(Canvas 无数据桥,不要假装可用);JSX 从 `renderJsx()` 提到 `function YidaComp(props)` 并 `export default`。
|
|
87
|
+
- 迁移完成或受阻时输出简短升级报告(含 source/target、status、依赖、schema write path、blockers、验收勾选项)。
|
|
88
|
+
|
|
89
|
+
> 📖 before/after 完整转换 JSX 示例 + 升级报告模板 → [references/migration-examples.md](references/migration-examples.md)
|
|
90
|
+
|
|
91
|
+
## 常见阻塞
|
|
92
|
+
|
|
93
|
+
| 阻塞 | 处理 |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| 源码无法等价改写(`this.utils.yida.*` / `dataSourceMap` 深度耦合) | Canvas 无实例数据桥,需重写为自建 HTTP 桥或 props;未验证前只交付草案和迁移报告,不发布覆盖 |
|
|
96
|
+
| 依赖不在 Code Canvas 白名单 | 补依赖映射、换已支持库,或保留原链路 |
|
|
97
|
+
| 原页面强依赖 `this.utils.yida.*` | 需要 props / 数据源注入方案;未验证前不要承诺可运行 |
|
|
98
|
+
| 原页面使用宜搭原生字段组件 | 先用 `yida-canvas-custom-page` 做最小验证 |
|
|
99
|
+
| Schema 回读不是 `YidaCodeCanvas` | 说明仍在普通 `Jsx` 链路,升级未完成 |
|