openyida 2026.7.7 → 2026.7.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.
Files changed (63) hide show
  1. package/README.md +4 -2
  2. package/bin/yida.js +4 -4
  3. package/lib/app/canvas-compile.js +303 -0
  4. package/lib/app/publish.js +190 -39
  5. package/lib/auth/auth.js +11 -3
  6. package/lib/auth/login.js +34 -15
  7. package/lib/core/command-manifest.js +1 -1
  8. package/lib/core/env.js +19 -4
  9. package/lib/core/locales/ar.js +6 -2
  10. package/lib/core/locales/de.js +6 -2
  11. package/lib/core/locales/en.js +7 -3
  12. package/lib/core/locales/es.js +6 -2
  13. package/lib/core/locales/fr.js +6 -2
  14. package/lib/core/locales/hi.js +6 -2
  15. package/lib/core/locales/ja.js +7 -3
  16. package/lib/core/locales/ko.js +6 -2
  17. package/lib/core/locales/pt.js +6 -2
  18. package/lib/core/locales/vi.js +6 -2
  19. package/lib/core/locales/zh-HK.js +7 -3
  20. package/lib/core/locales/zh.js +7 -3
  21. package/lib/core/utils.js +195 -15
  22. package/lib/samples/yida-custom-page/custom-page-template.js +6 -6
  23. package/lib/samples/yida-custom-page/design-tokens.js +14 -12
  24. package/package.json +1 -1
  25. package/scripts/e2e-real/skill-coverage.js +4 -0
  26. package/scripts/validate-ci.sh +6 -0
  27. package/scripts/validate-package-size.js +12 -1
  28. package/yida-skills/SKILL.md +19 -5
  29. package/yida-skills/references/development-rules.md +2 -2
  30. package/yida-skills/references/field-and-url-reference.md +14 -1
  31. package/yida-skills/skills/yida-app/SKILL.md +3 -2
  32. package/yida-skills/skills/yida-canvas-custom-page/SKILL.md +113 -0
  33. package/yida-skills/skills/yida-canvas-custom-page/references/canvas-authoring-examples.md +163 -0
  34. package/yida-skills/skills/yida-canvas-custom-page/references/canvas-design-system.md +148 -0
  35. package/yida-skills/skills/yida-canvas-custom-page/references/data-bridge-guide.md +115 -0
  36. package/yida-skills/skills/yida-canvas-custom-page/references/dependencies-and-cdn.md +45 -0
  37. package/yida-skills/skills/yida-canvas-custom-page/references/employeefield-verification.md +68 -0
  38. package/yida-skills/skills/yida-canvas-upgrade/SKILL.md +99 -0
  39. package/yida-skills/skills/yida-canvas-upgrade/references/migration-examples.md +69 -0
  40. package/yida-skills/skills/yida-custom-page/SKILL.md +7 -2
  41. package/yida-skills/skills/yida-custom-page/examples/attachment-upload.js +1 -1
  42. package/yida-skills/skills/yida-custom-page/references/coding-guide.md +3 -3
  43. package/yida-skills/skills/yida-custom-page/references/component-jsx-guide.md +3 -2
  44. package/yida-skills/skills/yida-custom-page/references/design-system.md +30 -13
  45. package/yida-skills/skills/yida-nav-shell/SKILL.md +119 -0
  46. package/yida-skills/skills/yida-nav-shell/references/nav-shell-patterns.md +302 -0
  47. package/yida-skills/skills/yida-page-config/SKILL.md +1 -0
  48. package/yida-skills/skills/yida-page-uiux/SKILL.md +68 -0
  49. package/yida-skills/skills/yida-page-uiux/references/scenes/dashboard.md +128 -0
  50. package/yida-skills/skills/yida-page-uiux/references/scenes/detail.md +79 -0
  51. package/yida-skills/skills/yida-page-uiux/references/scenes/landing.md +101 -0
  52. package/yida-skills/skills/yida-page-uiux/references/scenes/list.md +78 -0
  53. package/yida-skills/skills/yida-page-uiux/references/scenes/workbench.md +74 -0
  54. package/yida-skills/skills/yida-page-uiux/references/visual-decision-engine.md +151 -0
  55. package/yida-skills/skills/yida-page-uiux/workflow/output-decision-block.md +36 -0
  56. package/yida-skills/skills/yida-page-uiux/workflow/step-0-nav-shape.md +39 -0
  57. package/yida-skills/skills/yida-page-uiux/workflow/step-1-page-type.md +25 -0
  58. package/yida-skills/skills/yida-page-uiux/workflow/step-2-intent-decode.md +25 -0
  59. package/yida-skills/skills/yida-page-uiux/workflow/step-3-scene-routing.md +29 -0
  60. package/yida-skills/skills/yida-page-uiux/workflow/step-4-visual-decision.md +32 -0
  61. package/yida-skills/skills/yida-page-uiux/workflow/step-5-icon-and-assets.md +36 -0
  62. package/yida-skills/skills/yida-page-uiux/workflow/step-6-deai-selfcheck.md +37 -0
  63. package/yida-skills/skills/yida-publish-page/SKILL.md +7 -3
@@ -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> > .cache/openyida/canvas-upgrade/<name>-schema.json` |
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` 链路,升级未完成 |
@@ -0,0 +1,69 @@
1
+ # Canvas 升级:转换模式示例与报告模板
2
+
3
+ 本文件承载 `yida-canvas-upgrade` 的完整代码示例与升级报告模板,SKILL.md 只保留步骤与链接。
4
+
5
+ ## 转换模式示例
6
+
7
+ 普通页面(native `.oyd.jsx`,`_customState` + `forceUpdate` + `renderJsx`):
8
+
9
+ ```jsx
10
+ var _customState = { count: 0 };
11
+
12
+ export function add() {
13
+ _customState.count += 1;
14
+ this.forceUpdate();
15
+ }
16
+
17
+ export function renderJsx() {
18
+ var self = this;
19
+ return <button onClick={(e) => { self.add(); }}>{_customState.count}</button>;
20
+ }
21
+ ```
22
+
23
+ Code Canvas(真 React18 hooks,导出 `YidaComp`):
24
+
25
+ ```jsx
26
+ import React, { useState } from 'react';
27
+
28
+ function YidaComp(props) {
29
+ var state = useState(0);
30
+ var count = state[0];
31
+ var setCount = state[1];
32
+
33
+ return (
34
+ <button onClick={() => { setCount(count + 1); }}>
35
+ {count}
36
+ </button>
37
+ );
38
+ }
39
+
40
+ export default YidaComp;
41
+ ```
42
+
43
+ 转换要点:
44
+ - `_customState` 字段 → `useState`;复杂派生数据 → `useMemo`。
45
+ - `didMount` / `didUnmount` → `useEffect(() => { ...; return cleanup; }, [])`。
46
+ - `this.forceUpdate()` / `setCustomState` 强刷 → 删除,靠 hooks 自然重渲染。
47
+ - `this.utils.yida.*` / `this.dataSourceMap.*` 无法照搬,抽成 props 调用或明确 TODO,不要假装可用(Canvas 无平台数据桥)。
48
+
49
+ ## 升级报告模板
50
+
51
+ 迁移完成或受阻时,输出简短报告:
52
+
53
+ ```markdown
54
+ ## Canvas Upgrade Report
55
+
56
+ - Source page: project/pages/src/xxx.oyd.jsx
57
+ - Target page: APP_XXX / FORM-XXX
58
+ - Upgrade status: ready | blocked | partially-ready
59
+ - Generated canvas source: project/pages/src/xxx.canvas.jsx
60
+ - Required dependencies: react, antd, ...
61
+ - Schema write path: designer | verified-api | not-available
62
+ - Blockers:
63
+ - ...
64
+ - Verification:
65
+ - [ ] YidaCodeCanvas exists in schema
66
+ - [ ] importedModules contains required dependencies
67
+ - [ ] first screen renders
68
+ - [ ] core interactions work
69
+ ```
@@ -1,10 +1,12 @@
1
1
  ---
2
2
  name: yida-custom-page
3
- description: 宜搭自定义页面 JSX 开发规范。React 16 宜搭原生 export function 页面模式,宜搭 JS API 调用,状态管理与编码约束。适用于编写自定义页面代码时。
3
+ description: 宜搭 native 自定义页面 JSX 开发规范(React 16 宜搭原生 export function renderJsx 模式、宜搭 JS API、状态管理与编码约束)。是**回退链路**:仅当页面强依赖原生实例数据桥(this.$(fieldId) 表单字段双向绑定、this.utils.yida.*、dataSourceMap、提交流程深度耦合)时才用;纯展示无刷新页也可用。其余自定义页面(含只需开放 API HTTP 读数据的页)默认走 yida-canvas-custom-page。
4
4
  ---
5
5
 
6
6
  # 自定义页面开发
7
7
 
8
+ > **先确认链路**:自定义页面**默认走 Code Canvas** `yida-canvas-custom-page`(现代 React 交互 / hooks / 可视化 / AI / 需崩溃隔离,含只需开放 API HTTP 读数据的页)。本 native 技能是**回退链路**,只处理:① 强依赖原生实例数据桥——表单内字段双向绑定 `this.$(fieldId)`、`this.utils.yida.*`、`dataSourceMap`、提交流程深度耦合——的页面(Canvas 无 `this` 桥,重写代价过高时留 native);② 纯展示、无运行态刷新页。把已有 native 页升级到 Canvas 走 `yida-canvas-upgrade`。
9
+
8
10
  ## 核心规则
9
11
 
10
12
  ### 致命规则(FATAL)
@@ -30,6 +32,7 @@ description: 宜搭自定义页面 JSX 开发规范。React 16 宜搭原生 expo
30
32
 
31
33
  影响代码质量和用户体验:
32
34
 
35
+ 0. **写 UI 前先定视觉方向**:面向用户的页面在动手写 JSX 前,先读 `skills/yida-page-uiux/SKILL.md` 完成「视觉方向决策」(页面类型判定 + 差异化 + 去 AI 味自检),拿到「视觉方向决策块」后再按本技能 `references/design-system.md` 的 token/组件实现;不要直接套用统一灰白底 + 8px 圆角卡片网格的默认模板(那正是 AI 味来源)。
33
36
  1. **代码生成前确认功能摘要**:详见 [编码指南 编注 0](references/coding-guide.md)
34
37
  2. **pageSize 推荐 50,最大 100**:列表/看板默认 `pageSize: 50`;分页接口 `searchFormDatas` 等的 `pageSize` 最大 100
35
38
  3. **didUnmount 清理定时器**:在 `didUnmount` 中清理所有 `setInterval`/`setTimeout`,防止内存泄漏
@@ -42,7 +45,8 @@ description: 宜搭自定义页面 JSX 开发规范。React 16 宜搭原生 expo
42
45
  10. **Tabs 显隐控制**:下拉值变更后自动回退到第一个可见 Tab,内容区用 `display: none` 保留 DOM
43
46
  11. **加载态必须可恢复**:列表/看板页默认保留空态或演示数据;接口失败、超时或返回异常时必须把 `loading` 置回 `false`,不要只渲染“正在加载...”挡住整页
44
47
  12. **禁止可见原生下拉**:筛选、预约、审批等用户可见下拉交互不要使用 `<select>`;使用 Tailwind className 组合 `button + menu + option` 的自定义下拉组件
45
- 13. **发布前必须跑检查链路**:先执行 `openyida check-page <file>` `openyida compile <file>`;若出现 warning/error,按规则修复后再发布
48
+ 13. **严禁 emoji**:页面渲染出来的任何位置(标题、按钮、标签、状态、空态文案、图表标题等)**一律禁止出现 emoji**(😀🚀✅⚠️📦📊 等一切彩色符号字符)。需要图标一律用功能性内联 SVG(见 `skills/yida-page-uiux` 图标策略);需要状态标记用文字 + 语义色标签。emoji 是最明显的 AI 味来源之一,且跨端显示不一致。JS 注释里也不要留装饰性符号。
49
+ 14. **发布前必须跑检查链路**:先执行 `openyida check-page <file>` 和 `openyida compile <file>`;若出现 warning/error,按规则修复后再发布
46
50
 
47
51
  > 每条规则的代码示例、反模式和常见错误见 [编码指南](references/coding-guide.md)(编写代码前强制必读)。
48
52
  > 运行时易错点、`check-page` 规则和兼容层自动修复边界见 [运行时护栏](references/runtime-guardrails.md)。
@@ -166,6 +170,7 @@ openyida check-page pages/src/home.oyd.jsx --json # 输出机器可读的
166
170
  | 文档 | 覆盖范围 | 何时阅读 |
167
171
  |------|---------|---------|
168
172
  | **本技能文档** | | |
173
+ | [视觉方向决策(去 AI 味)](../yida-page-uiux/SKILL.md) | 页面类型 playbook、5 维差异化引擎、去 AI 味黑名单/8 问自检、图标策略 | 实现面向用户的 UI 且要求好看/去 AI 味时,**先于写代码**阅读并产出决策块 |
169
174
  | [编码指南](references/coding-guide.md) | 文件结构模板、状态管理、生命周期、19 条编码规范 | 编写任何页面代码前必读 |
170
175
  | [运行时护栏](references/runtime-guardrails.md) | pageSize、loading 恢复、ECharts DOM 时序、setState 约束、check-page 规则映射 | 编写列表、看板、图表或接口页面前必读 |
171
176
  | [设计规范](references/design-system.md) | 色彩/圆角/字体/间距系统、7 类组件样式模板、8 条反模式 | 实现 UI 样式时必读 |
@@ -168,7 +168,7 @@ var styles = {
168
168
  height: '36px',
169
169
  padding: '0 12px',
170
170
  borderRadius: '8px',
171
- background: '#1677FF',
171
+ background: 'var(--color-brand1-6, #1677FF)',
172
172
  color: '#fff',
173
173
  border: 'none',
174
174
  fontSize: '12px',
@@ -83,7 +83,7 @@ export function injectTailwindSource() {
83
83
  '@import "tailwindcss/theme";',
84
84
  '@import "tailwindcss/preflight";',
85
85
  '@import "tailwindcss/utilities";',
86
- '@theme { --color-brand: #2F6FED; }',
86
+ '@theme { --color-brand: var(--color-brand1-6, #2F6FED); }', // 跟随 App 主题,缺失时兜底
87
87
  ].join('\n');
88
88
  document.head.appendChild(style);
89
89
  }
@@ -98,11 +98,11 @@ export function injectTailwindFallback() {
98
98
  style.innerHTML = [
99
99
  '.oyd-btn,.oyd-select-trigger,.oyd-select-option{appearance:none;-webkit-appearance:none;font-family:inherit;}',
100
100
  '.oyd-btn{height:36px;border-radius:6px;border:1px solid #D0D5DD;background:#fff;padding:0 12px;font-size:14px;cursor:pointer;}',
101
- '.oyd-btn-primary{background:#2F6FED;border-color:#2F6FED;color:#fff;}',
101
+ '.oyd-btn-primary{background:var(--color-brand1-6,#2F6FED);border-color:var(--color-brand1-6,#2F6FED);color:#fff;}',
102
102
  '.oyd-select-trigger{height:38px;border-radius:6px;border:1px solid #D0D5DD;background:#fff;padding:0 12px;font-size:14px;text-align:left;box-shadow:0 6px 14px rgba(15,23,42,.06);}',
103
103
  '.oyd-select-menu{position:absolute;z-index:30;margin-top:6px;width:100%;padding:6px;border:1px solid #E4E7EC;border-radius:10px;background:#fff;box-shadow:0 16px 32px rgba(16,24,40,.14);}',
104
104
  '.oyd-select-option{width:100%;min-height:36px;border:0;border-radius:8px;background:#fff;padding:0 10px;text-align:left;font-size:14px;cursor:pointer;}',
105
- '.oyd-select-option-active{background:#EFF6FF;color:#1D4ED8;font-weight:600;}',
105
+ '.oyd-select-option-active{background:var(--color-brand1-1,#EFF6FF);color:var(--color-brand1-6,#1D4ED8);font-weight:600;}',
106
106
  ].join('');
107
107
  document.head.appendChild(style);
108
108
  }
@@ -5,6 +5,7 @@
5
5
  ## 先说清楚边界
6
6
 
7
7
  - 不要假设自定义页面能直接 `import` 宜搭内部表单组件;当前规范下应使用原生 JSX 元素、Tailwind `className` 和必要的内联兜底样式组合。
8
+ - Code Canvas 是自定义页面的默认链路(现代 React/hooks/可视化/AI);本 native 指南只服务纯展示无刷新页和需数据桥的数据驱动页。用户要现代交互页,或提到代码画布、`YidaCodeCanvas`、`runtimeCode`、`importedModules` 时,切换到 `yida-canvas-custom-page`。
8
9
  - 不要把字段中文名当作 `fieldId`;字段 ID 必须来自 `openyida get-schema`。
9
10
  - 不要把原生表单页面的组件配置 JSON 直接复制到自定义页面 JSX;两者不是同一个运行面。
10
11
  - 如果确有平台内置选择器、上传器等 API,必须先由用户提供官方示例或在目标环境验证,再写入代码。
@@ -200,10 +201,10 @@ var styles = {
200
201
  minHeight: 36,
201
202
  padding: '8px 12px',
202
203
  textAlign: 'left',
203
- background: '#EFF6FF',
204
+ background: 'var(--color-brand1-1, #EFF6FF)',
204
205
  border: 0,
205
206
  borderRadius: 8,
206
- color: '#1D4ED8',
207
+ color: 'var(--color-brand1-6, #1D4ED8)',
207
208
  appearance: 'none',
208
209
  WebkitAppearance: 'none',
209
210
  fontFamily: 'inherit',
@@ -1,5 +1,7 @@
1
1
  # 宜搭自定义页面设计规范
2
2
 
3
+ > **本文件是 token / 组件实现层(数值层)**:提供色彩/圆角/字体/间距的具体值与组件模板。视觉**方向与差异化**(选哪种气质、如何偏离默认脸、去 AI 味)由 `../../yida-page-uiux/SKILL.md` 决定——**先定方向、再用本文件落地**。直接套用本文件的默认组合(灰白底 + 统一圆角卡片)而不先做方向决策,正是页面「有 AI 味」的根因。
4
+
3
5
  > 宜搭自定义页面默认使用 Tailwind utility `className` 组织视觉层,并保留内联 `style` 兜底。不能使用 CSS 文件、CSS Modules、shadcn/ui 或构建期样式方案。
4
6
 
5
7
  > 原因:宜搭自定义页面运行在单文件环境中,不能像普通 React 项目一样 `import` CSS。Tailwind 通过 `@tailwindcss/browser` 运行时脚本加载,默认使用已验证的 `g.alicdn.com` 地址,并开启 preflight 重置原生控件外观;关键控件仍要有 JavaScript `style` 兜底。
@@ -22,27 +24,33 @@
22
24
 
23
25
  在 `renderJsx` 顶部定义语义色彩对象,全页复用:
24
26
 
25
- > **主色说明**:宜搭平台已内置品牌色 CSS 变量,主色相关 token 直接使用平台变量,无需硬编码色值,可随平台主题自动适配。
27
+ > **主色说明**:宜搭平台已内置品牌色 CSS 变量(色阶 `--color-brand1-1` 最浅 → `--color-brand1-6` 主色 → `--color-brand1-10` 最深)。**所有主色/强调/链接/选中/info/标签系一律走平台变量,不要硬编码蓝色**,页面才能跟随 App 主题自动适配。只有语义色(成功/警告/错误)固定不随主题变,保证语义稳定。
28
+ >
29
+ > **前提是导航可见**:跟随品牌主色是为了跟应用框架融合。若页面隐藏了应用导航(`isRenderNav=false`,沉浸/独立/门户/大屏),主色相可自立、不必严格跟品牌(由 `yida-page-uiux` 的 Step 0 决策)——但**语义色仍固定、去 AI 味红线仍生效**。拿不准就按「跟随品牌」这个更安全的默认走。
30
+ >
31
+ > ⚠️ **常见错误**:把 `primaryHover` 设成 `brand1-1`(最浅档)会让填充主按钮 hover 时「泛白」;把 `hover` 设成 `brand1-9`(深档)当行 hover 底会让行「变暗」。填充按钮 hover 要比主色**亮一档**(`brand1-5`)、按下**深一档**(`brand1-7`);通用浅色 hover 底用 `brand1-1`。
26
32
 
27
33
  ```javascript
28
34
  export function renderJsx() {
29
35
  var colors = {
30
- primary: 'var(--color-brand1-6)', // 主色(品牌蓝),用于主操作按钮、链接、选中态高亮
31
- primaryHover: 'var(--color-brand1-1)', // 主色悬停态,鼠标 hover 时的按钮/链接颜色
32
- hover: 'var(--color-brand1-9)', // 通用悬停背景色,用于列表行 hover、菜单项 hover
33
- active: 'var(--color-brand1-9)', // 通用激活/按下态,点击时的视觉反馈
34
- disabled: 'var(--color-brand1-8)', // 禁用态颜色,用于不可操作的按钮、控件
35
- primaryLight: 'var(--color-brand1-2)', // 主色浅背景,用于选中行底色、标签高亮背景
36
-
37
- // 语义色
36
+ primary: 'var(--color-brand1-6)', // 主色,用于主操作按钮、链接、选中态高亮
37
+ primaryHover: 'var(--color-brand1-5)', // 主色 hover:填充按钮/链接 hover,比主色亮一档
38
+ primaryActive: 'var(--color-brand1-7)', // 主色按下:填充按钮 active,比主色深一档
39
+ hover: 'var(--color-brand1-1)', // 通用浅色 hover 底:列表行 hover、菜单项 hover
40
+ active: 'var(--color-brand1-2)', // 通用浅色激活/按下底
41
+ disabled: 'var(--color-brand1-3)', // 禁用态:浅、去饱和
42
+ primaryLight: 'var(--color-brand1-2)', // 主色浅背景:选中行底色、标签高亮背景
43
+
44
+ // 语义色(固定,不随主题变)
38
45
  success: '#52C41A',
39
46
  successLight: '#F6FFED',
40
47
  warning: '#FAAD14',
41
48
  warningLight: '#FFFBE6',
42
49
  error: '#FF4D4F',
43
50
  errorLight: '#FFF2F0',
44
- info: '#1677FF',
45
- infoLight: '#E6F4FF',
51
+ // info = 品牌信息色,跟随主题(不再固定蓝)
52
+ info: 'var(--color-brand1-6)',
53
+ infoLight: 'var(--color-brand1-1)',
46
54
 
47
55
  // 中性色(从深到浅)
48
56
  text: '#1D2129', // 主文字
@@ -176,7 +184,7 @@ btnPrimary: {
176
184
  justifyContent: 'center',
177
185
  padding: '0 16px',
178
186
  height: '32px',
179
- background: '#1677FF',
187
+ background: 'var(--color-brand1-6)', // 主色跟随 App 主题,勿硬编码蓝
180
188
  color: '#FFFFFF',
181
189
  border: 'none',
182
190
  borderRadius: '6px',
@@ -238,7 +246,7 @@ tag: function(type) {
238
246
  success: { color: '#52C41A', bg: '#F6FFED', border: '#B7EB8F' },
239
247
  warning: { color: '#FAAD14', bg: '#FFFBE6', border: '#FFE58F' },
240
248
  error: { color: '#FF4D4F', bg: '#FFF2F0', border: '#FFCCC7' },
241
- info: { color: '#1677FF', bg: '#E6F4FF', border: '#91CAFF' },
249
+ info: { color: 'var(--color-brand1-6)', bg: 'var(--color-brand1-1)', border: 'var(--color-brand1-3)' },
242
250
  default: { color: '#4E5969', bg: '#F2F3F5', border: '#E5E6EB' },
243
251
  };
244
252
  var c = colorMap[type] || colorMap.default;
@@ -305,3 +313,12 @@ empty: {
305
313
  ❌ **禁止忽略空状态**,列表/数据为空时必须有友好提示
306
314
  ❌ **禁止忽略加载状态**,异步操作必须有 loading 反馈
307
315
  ❌ **禁止移动端不适配**,所有页面必须用 `isMobile` 做响应式处理
316
+
317
+ ### 去 AI 味反模式(与 `../../yida-page-uiux/` 对齐,实现前先定视觉方向)
318
+
319
+ ❌ **禁蓝紫 AI 万能渐变背景**:不要用 `#6366f1→#a855f7` 一类蓝紫渐变当区块/卡片底;主色走平台品牌变量,强调靠语义色/点缀色而非渐变。
320
+ ❌ **禁彩色发光阴影 / 半透明彩色 blob / 渐变文字**:阴影用中性色低透明度(如 `rgba(0,0,0,.06)`),不要彩色光晕;不要背景漂浮彩色模糊球,不要文字渐变。
321
+ ❌ **禁每个卡片/章节标题前配一枚装饰线性图标**:这是最典型的 AI 味。图标只用在功能处(按钮/状态/导航),标题纯文字;同页只用一套图标风格。
322
+ ❌ **严禁 emoji(FATAL)**:页面渲染的任何位置(标题/按钮/标签/状态/空态/图表标题)一律不得出现 emoji(😀🚀✅⚠️📦📊 等),JS 注释也不留装饰符号(←→✓)。需要图标用功能性内联 SVG,需要状态用文字 + 语义色标签。emoji 跨端不一致且是最明显的 AI 味来源。
323
+
324
+ > 注:上文「统一 `borderRadius: 8px`」是数值兜底,不代表全页只能一个圆角性格。圆角**性格**(直角/微圆 vs 标准圆 vs 圆润)由 `yida-page-uiux` 按气质选定后,再在本文件圆角系统里取分层数值。
@@ -0,0 +1,119 @@
1
+ ---
2
+ name: yida-nav-shell
3
+ description: >
4
+ 宜搭自定义页面「页面内导航壳」形态目录。当自定义页隐藏了应用导航(isRenderNav=false,沉浸式/独立页/门户首页/大屏/分享页)后,应用框架的导航消失,页面要用 JSX 自绘一套导航接管应用级导航。提供侧边栏/顶部/顶部+侧边混合/浮动胶囊/标签页五种 B 端导航形态的选型、骨架、native + Canvas 代码示例、多视图切换机制与带 URL 参数的跨页跳转规范。
5
+ 当用户要求「给这个自定义页/沉浸式页面/门户/大屏 加个导航(侧边/顶部/浮动/标签)」「自定义导航」「导航隐藏了页面自己怎么做导航」「一个页面切多个视图」时触发。
6
+ 注意:本技能是**页面内 JSX 自绘导航壳**;若用户要的是**平台左侧菜单/导航分组**(真实导航树、增删分组、排序菜单项),那是 yida-nav-group,不是本技能。
7
+ ---
8
+
9
+ # yida-nav-shell — 页面内自绘导航壳
10
+
11
+ ## 定位(先读这段)
12
+
13
+ 自定义页**隐藏应用导航**(`isRenderNav=false`)后,宜搭框架的左侧/顶部导航消失,页面即整个视口。此时页面要**自己画一套导航**接管应用级导航——这就是「导航壳」。本技能是导航壳的**形态选型 + 骨架 + 代码示例**目录。
14
+
15
+ > **一句话**:`yida-page-uiux` 定视觉方向(Step 0 判定导航是否隐藏)→ 本技能选**导航壳形态**并给骨架 → `yida-custom-page`(native) / `yida-canvas-custom-page`(Canvas) 落地 JSX。
16
+
17
+ ## 边界:和 yida-nav-group 划清(重要)
18
+
19
+ | 诉求 | 归谁 |
20
+ |---|---|
21
+ | 页面里用 JSX 画侧边/顶部/浮动导航,隐藏应用导航后自建导航 | **本技能 yida-nav-shell** |
22
+ | 管理宜搭应用**左侧菜单树 / 导航分组**:新建/重命名/删除分组、移动排序表单页面、隐藏导航项 | **yida-nav-group**(平台导航,CLI 操作 navUuid) |
23
+
24
+ 用户说「加个导航」时先分清是哪一种:要**平台菜单项**→ nav-group;要**页面内导航组件**(尤其页面已隐藏应用导航)→ 本技能。
25
+
26
+ ## 前置:先确认导航确实隐藏
27
+
28
+ 导航壳的前提是应用导航已隐藏。是否隐藏、以及隐藏时的主色/视觉策略,由 `yida-page-uiux` 的 [Step 0 导航形态判定](../yida-page-uiux/workflow/step-0-nav-shape.md) 决定:
29
+
30
+ - 发布时 `openyida update-form-config <appType> <formUuid> false "<标题>"`(`isRenderNav=false`),或访问带 `?isRenderNav=false`。
31
+ - 导航**未**隐藏(默认)时无需自建导航壳,应用导航负责跨页跳转——此时一般不用本技能。
32
+
33
+ ## 严格要求(导航壳必做)
34
+
35
+ 1. **先隐藏宜搭原导航**:只要页面内自绘应用级导航壳,就必须在发布后执行 `openyida update-form-config <appType> <shellFormUuid> false "<页面标题>"`,或创建时使用能隐藏导航的模式;不能让宜搭原左侧导航和页面内导航同时出现。
36
+ 2. **导航项必须携带 URL 参数**:导航配置不要只存 `formUuid`。每个跨页导航项必须能生成完整 URL,并合并公共参数(至少 `isRenderNav=false`;跨组织场景还要保留 `corpid`;业务深链保留 `tab`、`view`、`dateRange` 等参数)。
37
+ 3. **跨自定义页默认走 `custom/{formUuid}?isRenderNav=false`**:隐藏导航壳跳到另一个自定义展示页时,优先拼 `{base_url}/{appType}/custom/{formUuid}?isRenderNav=false`。不要只用 `this.utils.router.push(formUuid, {}, false)`,它容易丢失 `isRenderNav=false`,导致目标页重新出现宜搭原导航。
38
+ 4. **表单/数据管理入口分清打开方式**:需要在导航壳内容区展示表单列表时,用 iframe 地址 `workbench/{formUuid}?iframe=true`;必须整页跳转到表单管理时,也要通过统一 URL 构造函数合并当前业务参数,并明确接受目标页可能回到宜搭工作台框架。
39
+ 5. **发布后验证最终 URL**:发布完成后至少打开一个带 `?isRenderNav=false` 的导航项 URL,确认目标页没有宜搭原导航,且 URL 参数没有被跳转逻辑吞掉。
40
+
41
+ ## 选型速查
42
+
43
+ | 形态 | 何时用 | 顶级项数量 | 移动端收敛 |
44
+ |---|---|---|---|
45
+ | **左侧边栏** | 模块多、需常驻导航的后台/门户(最通用 B 端形态) | 5–12,支持分组/二级 | 抽屉(汉堡唤出) |
46
+ | **顶部导航** | 模块少、内容要占满宽度的看板/门户 | 2–6 | 汉堡菜单 |
47
+ | **顶部 + 侧边混合** | 两级结构复杂应用(顶部分域,侧边分子模块) | 顶 2–5 × 侧 3–10 | 顶部汉堡 + 侧边抽屉 |
48
+ | **浮动导航(悬浮胶囊/Dock)** | 沉浸/大屏/展示页,chrome 要极简,导航不常驻 | 3–6 | 底部胶囊/收起 |
49
+ | **标签页** | 一个模块内切同级视图(常叠加在上面几种之上) | 2–8 | 横向滚动标签 |
50
+
51
+ 每种形态的 ASCII 骨架、选中态纪律、移动端收敛、native + Canvas 代码示例、自查清单见 [references/nav-shell-patterns.md](references/nav-shell-patterns.md)。
52
+
53
+ ## 多视图切换机制(导航壳的核心)
54
+
55
+ 导航壳 = 一个自定义页内切多个视图。两条链路:
56
+
57
+ - **native**:状态存 `_customState.activeView`,点击 `this.setCustomState({ activeView: key })`,`renderJsx` 里按 `activeView` 分支渲染内容区。
58
+ - **Canvas**:`React.useState`,或用 URL hash(`window.location.hash` + `hashchange`,`useEffect` 注册并 cleanup)做可分享/可后退的视图切换。
59
+ - **跨页跳转**:跳到别的自定义页/表单时,用 [field-and-url-reference.md](../../references/field-and-url-reference.md) 的模板拼 URL,并显式合并导航项参数。隐藏导航壳跳自定义页时,目标必须带 `?isRenderNav=false` 保持沉浸;别假设应用导航还在。
60
+
61
+ ### URL 构造模板(native)
62
+
63
+ ```jsx
64
+ var BASE_URL = 'https://www.aliwork.com';
65
+ var APP_TYPE = 'APP_XXX';
66
+ var COMMON_NAV_PARAMS = {
67
+ isRenderNav: 'false'
68
+ };
69
+
70
+ var NAV_ITEMS = [
71
+ { key: 'home', label: '首页', type: 'custom', formUuid: 'FORM-HOME', params: { tab: 'home' } },
72
+ { key: 'dashboard', label: '经营看板', type: 'custom', formUuid: 'FORM-DASHBOARD', params: { tab: 'dashboard' } },
73
+ { key: 'orders', label: '订单管理', type: 'workbench', formUuid: 'FORM-ORDERS', params: { iframe: 'true' } }
74
+ ];
75
+
76
+ export function buildQuery(params) {
77
+ var pairs = [];
78
+ Object.keys(params || {}).forEach(function(key) {
79
+ if (params[key] !== undefined && params[key] !== null && params[key] !== '') {
80
+ pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(String(params[key])));
81
+ }
82
+ });
83
+ return pairs.length ? '?' + pairs.join('&') : '';
84
+ }
85
+
86
+ export function mergeParams(baseParams, extraParams) {
87
+ return Object.assign({}, baseParams || {}, extraParams || {});
88
+ }
89
+
90
+ export function buildNavUrl(item) {
91
+ var path = item.type === 'workbench'
92
+ ? '/' + APP_TYPE + '/workbench/' + item.formUuid
93
+ : '/' + APP_TYPE + '/custom/' + item.formUuid;
94
+ var baseParams = item.type === 'workbench' ? {} : COMMON_NAV_PARAMS;
95
+ return BASE_URL + path + this.buildQuery(this.mergeParams(baseParams, item.params));
96
+ }
97
+
98
+ export function openNavItem(item) {
99
+ var url = this.buildNavUrl(item);
100
+ window.open(url, '_top');
101
+ }
102
+ ```
103
+
104
+ > 如果需要保留当前 URL 上的 `corpid`、`locale` 或业务参数,先从 `this.state.urlParams` 读出白名单字段,合并进 `COMMON_NAV_PARAMS`;不要把不明参数全量透传。
105
+
106
+ ## 纪律(B 端 + 去 AI 味)
107
+
108
+ - **选中态一眼可辨**:侧边用「左侧 3px 主色条 + 浅色底 + 字重加粗」;顶部用「底部 2px 主色下划线 + 主色文字」,别只靠淡变色。
109
+ - **图标只作功能用途**:功能性内联 SVG + 文字,同页一套风格;**禁 emoji**、禁每项前配装饰图标(详见 [yida-page-uiux 图标纪律](../yida-page-uiux/workflow/step-5-icon-and-assets.md))。
110
+ - **不做营销脸**:无巨 Logo Hero、无渐变横幅;顶部条放「应用名/模块名 + 面包屑 + 用户」,克制。
111
+ - **主色策略**:导航隐藏时主色相可自立,但要么走品牌 `var(--color-brand1-*)`、要么用自定主色一以贯之;语义色固定。
112
+
113
+ ## 参考文档
114
+
115
+ | 文档 | 覆盖范围 | 何时阅读 |
116
+ |------|---------|---------|
117
+ | [导航壳形态目录](references/nav-shell-patterns.md) | 五形态 ASCII 骨架 + 选中态 + 移动端 + native/Canvas 代码 + 多视图切换 + 自查清单 | 选定形态、要骨架/代码时 |
118
+ | [yida-page-uiux Step 0](../yida-page-uiux/workflow/step-0-nav-shape.md) | 导航是否隐藏的判定法 + 隐藏时主色/视觉策略分叉 | 动手前确认导航形态时 |
119
+ | [字段与 URL 参考](../../references/field-and-url-reference.md) | 隐藏导航 `isRenderNav=false`、跨页跳转 URL 模板 | 拼跨页跳转 URL 时 |