Skip to content
This repository was archived by the owner on Mar 18, 2026. It is now read-only.

Commit 82a18c5

Browse files
author
opencode
committed
Merge origin/main and add check_login_only function
2 parents 18911bb + 6d1778a commit 82a18c5

9 files changed

Lines changed: 201 additions & 189 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
const REACT_HOOKS = [
5+
'useState',
6+
'useEffect',
7+
'useContext',
8+
'useReducer',
9+
'useCallback',
10+
'useMemo',
11+
'useRef',
12+
'useImperativeHandle',
13+
'useLayoutEffect',
14+
'useDebugValue',
15+
'useDeferredValue',
16+
'useTransition',
17+
'useId',
18+
'useSyncExternalStore',
19+
'useInsertionEffect'
20+
];
21+
22+
function checkFile(filePath) {
23+
const content = fs.readFileSync(filePath, 'utf-8');
24+
const errors = [];
25+
26+
for (const hook of REACT_HOOKS) {
27+
const patterns = [
28+
new RegExp(`\\b${hook}\\s*=`),
29+
new RegExp(`\\bReact\\.${hook}\\b`),
30+
new RegExp(`import\\s+\\{[^}]*\\b${hook}\\b[^}]*\\}\\s+from\\s+['"]react['"]`),
31+
new RegExp(`const\\s+\\{\\s*${hook}\\s*\\}\\s*=\\s*React`)
32+
];
33+
34+
for (const pattern of patterns) {
35+
if (pattern.test(content)) {
36+
errors.push(`${hook} (${path.basename(filePath)})`);
37+
}
38+
}
39+
}
40+
41+
return errors;
42+
}
43+
44+
function scanDirectory(dir, relativePath = '') {
45+
let allErrors = [];
46+
47+
if (!fs.existsSync(dir)) {
48+
return allErrors;
49+
}
50+
51+
const entries = fs.readdirSync(dir, { withFileTypes: true });
52+
53+
for (const entry of entries) {
54+
const fullPath = path.join(dir, entry.name);
55+
const relPath = path.join(relativePath, entry.name);
56+
57+
if (entry.isDirectory()) {
58+
if (entry.name === 'node_modules' || entry.name === '.git') {
59+
continue;
60+
}
61+
allErrors = allErrors.concat(scanDirectory(fullPath, relPath));
62+
} else if (entry.isFile() && entry.name.endsWith('.js')) {
63+
allErrors = allErrors.concat(checkFile(fullPath));
64+
}
65+
}
66+
67+
return allErrors;
68+
}
69+
70+
const skillsDir = path.join(__dirname, '..', 'skills');
71+
const errors = scanDirectory(skillsDir);
72+
73+
if (errors.length === 0) {
74+
console.log('✅ 未检测到 React Hooks 使用');
75+
process.exit(0);
76+
} else {
77+
console.error('❌ 检测到禁止使用的 React Hooks:');
78+
errors.forEach(err => console.error(` - ${err}`));
79+
console.error('\n宜搭自定义页面必须使用类组件模式,禁止使用 React Hooks');
80+
console.error('请参考 yida-custom-page skill 的开发规范');
81+
process.exit(1);
82+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
function findProjectRoot(startDir) {
5+
let current = startDir;
6+
while (true) {
7+
const hasReadme = fs.existsSync(path.join(current, 'README.md'));
8+
const hasGit = fs.existsSync(path.join(current, '.git')) && fs.statSync(path.join(current, '.git')).isDirectory();
9+
10+
if (hasReadme || hasGit) {
11+
return current;
12+
}
13+
14+
const parent = path.dirname(current);
15+
if (parent === current) {
16+
return startDir;
17+
}
18+
current = parent;
19+
}
20+
}
21+
22+
const scriptDir = path.join(__dirname, 'skills', 'yida-login', 'scripts');
23+
const result = findProjectRoot(scriptDir);
24+
25+
const yidaSkillsRoot = path.resolve(__dirname, '..', '..');
26+
27+
if (result === yidaSkillsRoot) {
28+
console.log(`✅ find_project_root 正确找到项目根目录: ${result}`);
29+
process.exit(0);
30+
} else {
31+
console.error(`❌ find_project_root 找到错误的根目录: ${result}`);
32+
console.error(` 预期: ${yidaSkillsRoot}`);
33+
process.exit(1);
34+
}

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ jobs:
3131
- name: Validate SKILL.md integrity
3232
run: node .github/scripts/validate-skill-md.js
3333

34+
- name: Validate project root detection
35+
run: node .github/scripts/validate-project-root.js
36+
37+
- name: Validate no React Hooks in JSX
38+
run: node .github/scripts/validate-no-react-hooks.js
39+
3440
- name: Install test dependencies
3541
run: npm install
3642

pr-body.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
## Summary
2+
3+
在 SKILL.md 中添加「⚠️ 常见错误」速查章节,避免开发者犯常见错误:
4+
5+
- 输入框使用 `defaultValue` + 直接赋值,而非 `value` + `setCustomState`
6+
- 禁止使用 Hooks(useState, useEffect)
7+
- 必须使用 `renderJsx` 函数名
8+
- 使用模块级 `_customState` 管理状态
9+
10+
## Changes
11+
12+
- 在「何时使用」章节后添加「⚠️ 常见错误」速查表格
13+
- 修复原文档中的一些格式问题
14+
15+
## Preview
16+
17+
在「何时使用」章节后添加了速查表格:
18+
19+
| 错误写法 | 正确写法 | 说明 |
20+
|----------|----------|------|
21+
| input value + setCustomState | input defaultValue + _customState.xxx = | 避免输入框失焦 |
22+
| useState, useEffect | 禁止使用 | 仅支持 React 16 |
23+
| render() {} | renderJsx() {} | 必须用 renderJsx |
24+
| this.setState({...}) | _customState.xxx = ... | 用模块级状态对象 |

skills/yida-create-form-page/SKILL.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,14 @@ yida-create-form-page/
556556
| `appType` | String || 应用 ID |
557557
| `formDataJson` | String || 表单数据(JSON 字符串) |
558558

559+
> ⚠️ **注意**:DateField 字段的值必须是**时间戳(毫秒)**,不能是字符串。例如:
560+
> ```javascript
561+
> // ✅ 正确
562+
> dateField_xxx: new Date().getTime()
563+
> // ❌ 错误
564+
> dateField_xxx: '2024-01-15'
565+
> ```
566+
559567
```javascript
560568
this.utils.yida.saveFormData({
561569
formUuid: 'FORM-XXX',
@@ -578,6 +586,8 @@ this.utils.yida.saveFormData({
578586
| `formInstId` | String || 表单实例 ID |
579587
| `updateFormDataJson` | String || 需要更新的字段(JSON 字符串) |
580588

589+
> ⚠️ **注意**:DateField 字段的值必须是**时间戳(毫秒)**
590+
581591
```javascript
582592
this.utils.yida.updateFormData({
583593
formInstId: 'FINST-XXX',

skills/yida-create-page/SKILL.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ yida-create-page/
116116

117117
1. **创建应用** → 使用 `yida-create-app` 技能获取 `appType`
118118
2. **创建自定义页面** → 本技能,获取 `pageId`(formUuid)
119-
3. **编写 JSX 源码** → 参考 `yida` 技能的开发规范
120-
4. **部署页面代码** → 使用 `yida-publish` 技能将代码部署到该页面
119+
3. **编写 JSX 源码****必须先加载 `yida-custom-page` skill**,严格按照其开发规范编写代码
120+
4. **部署页面代码** → 使用 `yida-publish-page` 技能将代码部署到该页面
121+
122+
> ⚠️ **重要警告**:宜搭自定义页面使用类组件模式,**禁止使用 React Hooks**(useState/useEffect)。编写代码前必须先加载 `yida-custom-page` skill 查看完整的开发规范。
121123
122124
> **提示**:如果需要创建的是表单页面(带字段的数据收集页),请使用 `yida-create-form-page` 技能。

skills/yida-custom-page/SKILL.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,42 @@ var inputEl = document.getElementById("my-input");
306306
if (inputEl) { inputEl.value = ""; }
307307
```
308308

309+
9. **DateField 必须是时间戳格式**:保存日期字段时,值必须是 **时间戳(毫秒)**,不能是字符串。示例:
310+
```javascript
311+
// ❌ 错误:字符串格式
312+
dateField_xxx: '2024-01-15'
313+
314+
// ✅ 正确:时间戳格式
315+
dateField_xxx: new Date().getTime()
316+
```
317+
318+
10. **多端适配**:宜搭自定义页面会在 PC 端和移动端同时展示,样式需要兼容两种设备。使用 `this.utils.isMobile()` 判断设备类型,动态调整布局和样式:
319+
```javascript
320+
const isMobile = this.utils.isMobile();
321+
322+
var styles = {
323+
container: {
324+
padding: isMobile ? '12px' : '16px', // 移动端padding更小
325+
minHeight: '100vh'
326+
},
327+
card: {
328+
padding: isMobile ? '12px' : '16px', // 移动端更紧凑
329+
marginBottom: isMobile ? '8px' : '12px'
330+
}
331+
};
332+
```
333+
334+
11. **清除默认 padding 和圆角**:宜搭自定义页面容器有默认 padding 和圆角,需要强制覆盖:
335+
```javascript
336+
var styles = {
337+
container: {
338+
padding: '0 16px',
339+
borderRadius: '0 !important', // 清除默认圆角
340+
minHeight: '100vh'
341+
}
342+
};
343+
```
344+
309345
---
310346

311347
## API 参考

0 commit comments

Comments
 (0)