neo-cmp-cli 1.13.11 → 1.13.13

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 (100) hide show
  1. package/dist/neo/neoService.js +1 -1
  2. package/dist/package.json.js +1 -1
  3. package/package.json +1 -1
  4. package/template/antd-custom-cmp-template/package.json +1 -1
  5. package/template/asset-manage-template/package.json +1 -1
  6. package/template/echarts-custom-cmp-template/package.json +1 -1
  7. package/template/empty-custom-cmp-template/package.json +1 -1
  8. package/template/map-custom-cmp-template/package.json +1 -1
  9. package/template/neo-bi-cmps/docs/gartner-pipeline-apis.md +279 -0
  10. package/template/neo-bi-cmps/docs/gartner-pipeline-prd.md +389 -0
  11. package/template/neo-bi-cmps/docs/neo-backend-dev/SKILL.md +188 -0
  12. package/template/neo-bi-cmps/docs/neo-backend-dev/references/01-Trigger/345/274/200/345/217/221.md +183 -0
  13. package/template/neo-bi-cmps/docs/neo-backend-dev/references/02-/350/207/252/345/256/232/344/271/211API/345/274/200/345/217/221.md +196 -0
  14. package/template/neo-bi-cmps/docs/neo-backend-dev/references/03-SDK/345/267/245/345/205/267/347/261/273/346/216/245/345/217/243.md +346 -0
  15. package/template/neo-bi-cmps/docs/neo-backend-dev/references/04-/350/256/241/345/210/222/344/275/234/344/270/232/345/274/200/345/217/221.md +188 -0
  16. package/template/neo-bi-cmps/docs/neo-backend-dev/references/05-/351/241/265/351/235/242/345/274/200/345/217/221.md +293 -0
  17. package/template/neo-bi-cmps/docs/neo-backend-dev/references/06-/346/265/201/347/250/213/346/211/251/345/261/225/345/274/200/345/217/221.md +175 -0
  18. package/template/neo-bi-cmps/docs/neo-backend-dev/references/PaaS/345/271/263/345/217/260/345/274/200/345/217/221/346/211/213/345/206/214/350/247/243/350/257/273.md +313 -0
  19. package/template/neo-bi-cmps/docs/neo-backend-dev/references/auth-config.md +77 -0
  20. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/deploy_server_script.py +118 -0
  21. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/download_server_script.py +74 -0
  22. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entity_desc.py +69 -0
  23. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entitylist.py +87 -0
  24. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/query_crm.py +65 -0
  25. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/uninstall_server_script.py +48 -0
  26. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/update_model_jar.py +49 -0
  27. package/template/neo-bi-cmps/docs/neo-frontend-dev/SKILL.md +138 -0
  28. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/auth-config.md +77 -0
  29. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/component-dev.md +205 -0
  30. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/entityTable-example.md +167 -0
  31. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/templates.md +38 -0
  32. package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entity_desc.py +69 -0
  33. package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entitylist.py +87 -0
  34. package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/query_crm.py +65 -0
  35. package/template/neo-bi-cmps/docs/prototype-pipeline-forecasting.html +2453 -0
  36. package/template/neo-bi-cmps/docs//350/264/246/345/217/267/347/233/270/345/205/263/344/277/241/346/201/257.md +10 -0
  37. package/template/neo-bi-cmps/package.json +1 -1
  38. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/README.md +52 -0
  39. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/index.tsx +176 -0
  40. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/model.ts +49 -0
  41. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/style.scss +218 -0
  42. package/template/neo-bi-cmps/src/components/filterBar__c/README.md +35 -0
  43. package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +186 -0
  44. package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +72 -0
  45. package/template/neo-bi-cmps/src/components/filterBar__c/style.scss +212 -0
  46. package/template/neo-bi-cmps/src/components/forecastChart__c/README.md +31 -0
  47. package/template/neo-bi-cmps/src/components/forecastChart__c/index.tsx +161 -0
  48. package/template/neo-bi-cmps/src/components/forecastChart__c/model.ts +39 -0
  49. package/template/neo-bi-cmps/src/components/forecastChart__c/style.scss +154 -0
  50. package/template/neo-bi-cmps/src/components/forecastGrid__c/README.md +36 -0
  51. package/template/neo-bi-cmps/src/components/forecastGrid__c/index.tsx +86 -0
  52. package/template/neo-bi-cmps/src/components/forecastGrid__c/model.ts +34 -0
  53. package/template/neo-bi-cmps/src/components/forecastGrid__c/style.scss +48 -0
  54. package/template/neo-bi-cmps/src/components/gapCloser__c/README.md +24 -0
  55. package/template/neo-bi-cmps/src/components/gapCloser__c/index.tsx +95 -0
  56. package/template/neo-bi-cmps/src/components/gapCloser__c/model.ts +43 -0
  57. package/template/neo-bi-cmps/src/components/gapCloser__c/style.scss +60 -0
  58. package/template/neo-bi-cmps/src/components/kpiCards__c/README.md +35 -0
  59. package/template/neo-bi-cmps/src/components/kpiCards__c/index.tsx +70 -0
  60. package/template/neo-bi-cmps/src/components/kpiCards__c/model.ts +35 -0
  61. package/template/neo-bi-cmps/src/components/kpiCards__c/style.scss +33 -0
  62. package/template/neo-bi-cmps/src/components/oppList__c/README.md +52 -0
  63. package/template/neo-bi-cmps/src/components/oppList__c/index.tsx +228 -0
  64. package/template/neo-bi-cmps/src/components/oppList__c/model.ts +40 -0
  65. package/template/neo-bi-cmps/src/components/oppList__c/style.scss +133 -0
  66. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/README.md +39 -0
  67. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/index.tsx +128 -0
  68. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/model.ts +42 -0
  69. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/style.scss +133 -0
  70. package/template/neo-bi-cmps/src/components/stageSwitch__c/README.md +36 -0
  71. package/template/neo-bi-cmps/src/components/stageSwitch__c/index.tsx +103 -0
  72. package/template/neo-bi-cmps/src/components/stageSwitch__c/model.ts +37 -0
  73. package/template/neo-bi-cmps/src/components/stageSwitch__c/style.scss +89 -0
  74. package/template/neo-bi-cmps/src/components/stageTimeChart__c/README.md +37 -0
  75. package/template/neo-bi-cmps/src/components/stageTimeChart__c/index.tsx +126 -0
  76. package/template/neo-bi-cmps/src/components/stageTimeChart__c/model.ts +35 -0
  77. package/template/neo-bi-cmps/src/components/stageTimeChart__c/style.scss +140 -0
  78. package/template/neo-bi-cmps/src/components/tabSwitch__c/README.md +37 -0
  79. package/template/neo-bi-cmps/src/components/tabSwitch__c/index.tsx +80 -0
  80. package/template/neo-bi-cmps/src/components/tabSwitch__c/model.ts +45 -0
  81. package/template/neo-bi-cmps/src/components/tabSwitch__c/style.scss +37 -0
  82. package/template/neo-custom-cmp-template/package.json +1 -1
  83. package/template/neo-custom-cmp-template/src/components/entityForm__c/index.tsx +48 -54
  84. package/template/neo-custom-cmp-template/src/components/entityForm__c/model.ts +1 -1
  85. package/template/neo-custom-cmp-template/src/components/entityForm__c/style.scss +80 -77
  86. package/template/neo-h5-cmps/package.json +1 -1
  87. package/template/neo-order-cmps/package.json +1 -1
  88. package/template/neo-web-entity-grid/package.json +1 -1
  89. package/template/neo-web-entity-grid/src/components/createForm__c/index.tsx +46 -54
  90. package/template/neo-web-entity-grid/src/components/createForm__c/resetAntd.scss +74 -0
  91. package/template/neo-web-entity-grid/src/components/createForm__c/style.scss +81 -152
  92. package/template/neo-web-entity-grid/src/components/searchForm__c/index.tsx +47 -52
  93. package/template/neo-web-entity-grid/src/components/searchForm__c/style.scss +60 -74
  94. package/template/neo-web-form/package.json +1 -1
  95. package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +16 -7
  96. package/template/neo-web-form/src/components/batchAddTable__c/style.scss +14 -0
  97. package/template/neo-web-form/src/components/batchAddTable__c/tableModal.scss +60 -13
  98. package/template/react-custom-cmp-template/package.json +1 -1
  99. package/template/react-ts-custom-cmp-template/package.json +1 -1
  100. package/template/vue2-custom-cmp-template/package.json +1 -1
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 生成实体字典:下载 model.jar,解析实体列表,通过 API 获取 label,输出 entitylist.md
4
+ 用法: python3 gen_entitylist.py <项目目录>
5
+ 示例: python3 gen_entitylist.py NEOTrail
6
+ """
7
+ import sys, os, json, zipfile, urllib.request
8
+
9
+ if len(sys.argv) < 2:
10
+ print("用法: python3 gen_entitylist.py <项目目录>")
11
+ sys.exit(1)
12
+
13
+ project_dir = sys.argv[1]
14
+ token_path = os.path.join(project_dir, '.neo-cli/token.json')
15
+ output_dir = os.path.join(project_dir, 'src/entity-model')
16
+ jar_path = os.path.join(project_dir, 'model.jar')
17
+
18
+ if not os.path.exists(token_path):
19
+ print("错误: 未找到 %s,请先执行 neo login" % token_path)
20
+ sys.exit(1)
21
+
22
+ token = json.load(open(token_path))
23
+ access_token = token['access_token']
24
+ base_url = token['instance_uri'].rstrip('/')
25
+ headers = {'Authorization': 'Bearer ' + access_token, 'Content-Type': 'application/json'}
26
+
27
+ def api_get(path):
28
+ req = urllib.request.Request(base_url + path, method='GET')
29
+ for k, v in headers.items():
30
+ req.add_header(k, v)
31
+ return json.loads(urllib.request.urlopen(req).read().decode('utf-8'))
32
+
33
+ # 1. 下载 model.jar
34
+ print("下载 model.jar ...")
35
+ jar_url_data = api_get('/rest/metadata/v2.0/scripts/packages/model/jar/url')
36
+ jar_url = jar_url_data.get('data', {}).get('records', '')
37
+ urllib.request.urlretrieve(jar_url, jar_path)
38
+ print("已下载: %s" % jar_path)
39
+
40
+ # 2. 解析 jar 获取实体列表
41
+ with zipfile.ZipFile(jar_path, 'r') as z:
42
+ classes = [n for n in z.namelist() if n.endswith('.class') and '/model/' in n]
43
+
44
+ jar_entities = {}
45
+ for c in classes:
46
+ name = c.split('/')[-1].replace('.class', '')
47
+ apiKey = name[0].lower() + name[1:]
48
+ is_standard = not (name.endswith('__c') or name.endswith('__a'))
49
+ jar_entities[apiKey] = {'className': name, 'apiKey': apiKey, 'isStandard': is_standard, 'label': ''}
50
+
51
+ # 3. 批量获取 label
52
+ print("获取实体 label ...")
53
+ label_map = {}
54
+ for custom_flag in ['true', 'false']:
55
+ try:
56
+ data = api_get('/rest/metadata/v2.0/xobjects/filter?custom=%s&active=true' % custom_flag)
57
+ for r in data.get('data', {}).get('records', []):
58
+ label_map[r.get('apiKey', '')] = r.get('label', '')
59
+ except Exception as e:
60
+ print("警告: 获取实体列表失败 (custom=%s): %s" % (custom_flag, e))
61
+
62
+ for ak, label in label_map.items():
63
+ if ak in jar_entities:
64
+ jar_entities[ak]['label'] = label
65
+
66
+ # 4. 生成 entitylist.md
67
+ entities = sorted(jar_entities.values(), key=lambda x: x['className'])
68
+ standard = [e for e in entities if e['isStandard']]
69
+ custom = [e for e in entities if not e['isStandard']]
70
+
71
+ os.makedirs(output_dir, exist_ok=True)
72
+
73
+ lines = ['# 实体字典', '',
74
+ '租户 ID: %s | 环境: %s' % (token.get('tenant_id', ''), base_url), '',
75
+ '共 %d 个实体(%d 个标准实体 + %d 个自定义实体)' % (len(entities), len(standard), len(custom)), '',
76
+ '| 实体名称 | API Key | 类型 |',
77
+ '|----------|---------|------|']
78
+ for e in entities:
79
+ label = e['label'] if e['label'] else e['className']
80
+ etype = '标准' if e['isStandard'] else '自定义'
81
+ lines.append('| %s | %s | %s |' % (label, e['apiKey'], etype))
82
+
83
+ output_path = os.path.join(output_dir, 'entitylist.md')
84
+ with open(output_path, 'w', encoding='utf-8') as f:
85
+ f.write('\n'.join(lines))
86
+
87
+ print("完成: %s (%d 个实体, %d 个 label)" % (output_path, len(entities), len(label_map)))
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 查询 CRM 系统数据工具。借助当前项目的登录状态执行 SQL 查询。
4
+ 用法: python3 query_crm.py <项目目录> "<SQL查询语句>"
5
+ 示例: python3 query_crm.py 临时验证 "SELECT id, opportunityName, amount FROM opportunity LIMIT 10"
6
+ """
7
+ import os, json, urllib.request, urllib.error, sys
8
+
9
+ if len(sys.argv) < 3:
10
+ print("用法: python3 query_crm.py <项目目录> \"<SQL查询语句>\"")
11
+ sys.exit(1)
12
+
13
+ project_dir = sys.argv[1]
14
+ sql = sys.argv[2]
15
+ token_path = os.path.join(project_dir, '.neo-cli/token.json')
16
+
17
+ if not os.path.exists(token_path):
18
+ print("错误: 未找到 %s,请先执行 neo login" % token_path)
19
+ sys.exit(1)
20
+
21
+ token = json.load(open(token_path))
22
+ access_token = token['access_token']
23
+ base_url = token['instance_uri'].rstrip('/')
24
+
25
+ print("执行查询: %s\n" % sql)
26
+
27
+ url = base_url + '/rest/data/v2/query'
28
+ encoded_sql = urllib.request.quote(sql)
29
+ full_url = url + '?q=' + encoded_sql
30
+
31
+ req = urllib.request.Request(full_url, method='GET')
32
+ req.add_header('Authorization', 'Bearer ' + access_token)
33
+ req.add_header('Content-Type', 'application/json')
34
+
35
+ try:
36
+ resp = urllib.request.urlopen(req)
37
+ data = json.loads(resp.read().decode('utf-8'))
38
+
39
+ records = data.get('result', data.get('data', {})).get('records', [])
40
+ total = data.get('result', data.get('data', {})).get('totalSize', len(records) if isinstance(records, list) else 0)
41
+
42
+ if isinstance(records, list) and len(records) > 0:
43
+ # 提取字段名
44
+ fields = list(records[0].keys()) if isinstance(records[0], dict) else []
45
+
46
+ # 打印表头
47
+ header = ' | '.join(str(f) for f in fields)
48
+ print(header)
49
+ print('-' * len(header))
50
+
51
+ # 打印数据
52
+ for r in records:
53
+ if isinstance(r, dict):
54
+ row = ' | '.join(str(r.get(f, '')) for f in fields)
55
+ print(row)
56
+
57
+ print("\n共 %s 条记录" % total)
58
+ else:
59
+ print("无数据")
60
+ print("原始响应: %s" % json.dumps(data, ensure_ascii=False)[:1000])
61
+
62
+ except urllib.error.HTTPError as e:
63
+ print("请求失败 HTTP %d: %s" % (e.code, e.read().decode('utf-8')[:500]))
64
+ except Exception as e:
65
+ print("异常: %s" % e)
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 卸载服务端代码包。
4
+ 用法: python3 uninstall_server_script.py <项目目录> <Java包名> [--force]
5
+ 示例: python3 uninstall_server_script.py NEOTrail other.xsy.account
6
+ python3 uninstall_server_script.py NEOTrail other.xsy.account --force
7
+ """
8
+ import os, json, urllib.request, urllib.error, sys
9
+
10
+ if len(sys.argv) < 3:
11
+ print("用法: python3 uninstall_server_script.py <项目目录> <Java包名> [--force]")
12
+ sys.exit(1)
13
+
14
+ project_dir = sys.argv[1]
15
+ java_package = sys.argv[2]
16
+ force = '--force' in sys.argv
17
+ token_path = os.path.join(project_dir, '.neo-cli/token.json')
18
+
19
+ if not os.path.exists(token_path):
20
+ print("错误: 未找到 %s,请先执行 neo login" % token_path)
21
+ sys.exit(1)
22
+
23
+ token = json.load(open(token_path))
24
+ access_token = token['access_token']
25
+ base_url = token['instance_uri'].rstrip('/')
26
+
27
+ # 卸载代码包
28
+ force_str = 'true' if force else 'false'
29
+ print("卸载代码包: %s (force=%s) ..." % (java_package, force_str))
30
+ url = base_url + '/rest/metadata/v2.0/scripts/packages/installations/' + urllib.request.quote(java_package) + '?force=' + force_str
31
+ req = urllib.request.Request(url, method='DELETE')
32
+ req.add_header('Authorization', access_token)
33
+
34
+ try:
35
+ resp = urllib.request.urlopen(req)
36
+ data = json.loads(resp.read().decode('utf-8'))
37
+ code = data.get('code', '')
38
+ if code in ('200', '000000'):
39
+ print("卸载成功")
40
+ elif code == '290030':
41
+ print("有任务正在运行,请使用 --force 强制卸载")
42
+ sys.exit(1)
43
+ else:
44
+ print("卸载失败: %s" % data.get('msg', code))
45
+ sys.exit(1)
46
+ except urllib.error.HTTPError as e:
47
+ print("请求失败 HTTP %d: %s" % (e.code, e.read().decode('utf-8')[:500]))
48
+ sys.exit(1)
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 更新 model.jar:触发服务端重新编译并下载最新的 model.jar。
4
+ 用法: python3 update_model_jar.py <项目目录>
5
+ 示例: python3 update_model_jar.py NEOTrail
6
+ """
7
+ import os, json, urllib.request, urllib.error, sys
8
+
9
+ if len(sys.argv) < 2:
10
+ print("用法: python3 update_model_jar.py <项目目录>")
11
+ sys.exit(1)
12
+
13
+ project_dir = sys.argv[1]
14
+ token_path = os.path.join(project_dir, '.neo-cli/token.json')
15
+ jar_path = os.path.join(project_dir, 'model.jar')
16
+
17
+ if not os.path.exists(token_path):
18
+ print("错误: 未找到 %s,请先执行 neo login" % token_path)
19
+ sys.exit(1)
20
+
21
+ token = json.load(open(token_path))
22
+ access_token = token['access_token']
23
+ base_url = token['instance_uri'].rstrip('/')
24
+
25
+ # 1. 触发编译并获取下载地址
26
+ print("触发 model.jar 编译 ...")
27
+ url = base_url + '/rest/metadata/v2.0/scripts/packages/model/jar'
28
+ req = urllib.request.Request(url, data=b'', method='POST')
29
+ req.add_header('Authorization', access_token)
30
+
31
+ try:
32
+ resp = urllib.request.urlopen(req)
33
+ data = json.loads(resp.read().decode('utf-8'))
34
+ if data.get('code') != '200':
35
+ print("编译失败: %s" % data.get('msg', ''))
36
+ sys.exit(1)
37
+ jar_url = data.get('data', {}).get('records', '')
38
+ if not jar_url:
39
+ print("错误: 未获取到下载地址")
40
+ sys.exit(1)
41
+ print("编译成功,下载地址: %s" % jar_url[:80])
42
+ except urllib.error.HTTPError as e:
43
+ print("请求失败 HTTP %d: %s" % (e.code, e.read().decode('utf-8')[:500]))
44
+ sys.exit(1)
45
+
46
+ # 2. 下载 model.jar
47
+ print("下载 model.jar ...")
48
+ urllib.request.urlretrieve(jar_url, jar_path)
49
+ print("完成: %s (%d bytes)" % (jar_path, os.path.getsize(jar_path)))
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: neo-frontend-dev
3
+ description: Neo 前端自定义组件开发技能,基于 neo-cmp-cli 工具链。当用户需要:(1) 创建 Neo 自定义组件项目,(2) 开发前端自定义组件(React/AntD),(3) 配置组件模型(propsSchema、事件、动作),(4) 使用 neo-open-api 对接实体数据源或自定义 API,(5) 发布/拉取/删除前端组件,(6) 修改系统中已有的前端组件,(7) 获取和查询实体模型时,使用此技能。
4
+ ---
5
+
6
+ # Neo 前端自定义组件开发
7
+
8
+ 基于 neo-cmp-cli 工具链,辅助完成 NeoCRM 平台前端自定义组件的开发全流程。
9
+
10
+ ## 前置条件
11
+
12
+ - 已全局安装 neo-cmp-cli:`npm i -g neo-cmp-cli`
13
+ - Node.js 环境
14
+
15
+ ## 重要约定
16
+
17
+ - 组件目录名必须以 `__c` 结尾(如 `helloNeo__c`)
18
+ - `neo login -e production` 默认登录生产环境。执行登录前必须询问用户要登录到哪个环境。环境参数:`production`、`gray`、`sandbox`、`cd`、`tencentuat`、`custom`
19
+ - `neo login` 执行后自动打开浏览器授权,轮询 `.neo-cli/token.json` 确认登录成功
20
+ - 执行 push/pull/delete 时直接执行,未登录失败后再询问环境并登录重试
21
+
22
+ ## 核心工作流
23
+
24
+ ### 1. 创建项目
25
+
26
+ 如果用户未指定项目名称,先询问。未指定路径则告知默认位置并询问是否修改。
27
+
28
+ ```bash
29
+ neo create project -n <项目名称>
30
+ ```
31
+
32
+ 项目创建后将 `neo.config.js` 中 `neoConfig` 改为正式环境:
33
+ ```javascript
34
+ neoConfig: {
35
+ neoBaseURL: 'https://crm.xiaoshouyi.com',
36
+ loginURL: 'https://login.xiaoshouyi.com/auc/oauth2/auth',
37
+ tokenURL: 'https://login.xiaoshouyi.com/auc/oauth2/token',
38
+ },
39
+ ```
40
+
41
+ ### 2. 登录租户
42
+
43
+ 项目创建后,先询问用户要登录到哪个环境(默认 production),执行登录:
44
+ ```bash
45
+ neo login -e <env>
46
+ ```
47
+ 轮询 `.neo-cli/token.json` 确认登录成功。
48
+
49
+ ### 3. 安装依赖
50
+
51
+ 登录成功后,安装前端依赖(开发前端组件前必须执行):
52
+ ```bash
53
+ cd <项目目录>
54
+ npm install
55
+ ```
56
+
57
+ ### 4. 创建组件
58
+
59
+ ```bash
60
+ neo create cmp --name <组件名称>
61
+ ```
62
+
63
+ 也可手动在 `src/components/` 下创建目录(目录名以 `__c` 结尾)。
64
+
65
+ ### 5. 组件开发
66
+
67
+ 开发前参考 [references/entityTable-example.md](references/entityTable-example.md) 了解最佳实践。
68
+
69
+ 核心文件:`index.tsx`(渲染逻辑)+ `model.ts`(设计器配置)
70
+
71
+ 开发组件时,如果用户没有明确要求可配置属性,model.ts 中的 `propsSchema` 和 `defaultComProps` 留空即可,不要主动添加可配置属性。
72
+
73
+ 详细指南见 [references/component-dev.md](references/component-dev.md)。
74
+
75
+ ### 6. 查看效果
76
+
77
+ 当前暂不支持本地预览,提示用户发布到租户后在自定义页面中查看。
78
+
79
+ ### 7. 授权与发布
80
+
81
+ 授权配置详见 [references/auth-config.md](references/auth-config.md)。
82
+
83
+ ```bash
84
+ neo login -e production
85
+ neo push cmp --name <组件名称>
86
+ neo pull cmp --name <组件名称>
87
+ neo delete cmp --name <组件名称>
88
+ neo logout
89
+ ```
90
+
91
+ ## 修改系统中已有组件
92
+
93
+ 1. 本地没有则自动 `neo pull cmp --name <组件名称>`,拉取后自动读取代码
94
+ 2. 询问用户修改哪个组件
95
+ 3. 执行修改,参考 [references/entityTable-example.md](references/entityTable-example.md)
96
+ 4. 自动递增版本号并发布,修改完成后主动询问是否发布
97
+
98
+ ## 获取实体模型
99
+
100
+ ### 生成实体字典
101
+ ```bash
102
+ python3 scripts/gen_entitylist.py <项目目录>
103
+ ```
104
+
105
+ ### 查询实体字段
106
+ 1. 在 `src/entity-model/entitylist.md` 中查找匹配实体
107
+ 2. 检查 `src/entity-model/<apiKey>.md` 是否存在
108
+ 3. 不存在则运行:`python3 scripts/gen_entity_desc.py <项目目录> <apiKey>`
109
+
110
+ ### 平台 API 参考
111
+
112
+ 所有 API 携带 `Authorization: Bearer <access_token>`。
113
+ - 获取 model.jar:`GET /rest/metadata/v2.0/scripts/packages/model/jar/url`
114
+ - 获取实体列表:`GET /rest/metadata/v2.0/xobjects/filter?custom=<true|false>&active=true`
115
+ - 获取实体字段:`GET /rest/metadata/v2.0/xobjects/<apiKey>/items`
116
+
117
+ ## 命令速查
118
+
119
+ | 命令 | 说明 |
120
+ |------|------|
121
+ | `neo create project` | 创建空项目 |
122
+ | `neo create cmp` | 创建组件 |
123
+ | `neo login -e <env>` / `neo logout` | 登录/登出 |
124
+ | `neo push cmp` | 发布组件 |
125
+ | `neo pull cmp` | 拉取组件 |
126
+ | `neo delete cmp` | 删除组件 |
127
+
128
+ ## 注意事项
129
+
130
+ - 组件目录名以 `__c` 结尾,根节点 class 名与目录名一致
131
+ - 构建时自动剔除平台预置依赖(antd 4.9.4、react 16.x 等)
132
+ - 发布时自动添加样式隔离
133
+ - 当前暂不支持本地预览,需发布后查看
134
+ - 自定义 API 的调用路径是 `/rest/data/v2.0/scripts/api/{baseUrl}/{path}`,不是 `/rest/custom/v2.0/`
135
+ - `customApi.run` 返回的 `result.data` 可能是 JSON 字符串,需要 `typeof === 'string'` 时 `JSON.parse`
136
+ - 开发涉及业务对象的功能前,必须先通过 `gen_entity_desc.py` 获取实体的真实字段模型
137
+ - 可使用 `query_crm.py` 工具验证数据查询:`python3 scripts/query_crm.py <项目目录> "<SQL>"`
138
+ - 平台预置依赖版本列表见 [references/component-dev.md](references/component-dev.md)
@@ -0,0 +1,77 @@
1
+ # 授权配置
2
+
3
+ 与 NeoCRM 平台交互(push/pull/delete)前需配置授权。
4
+
5
+ ## 方式一:OAuth2 登录授权(推荐)
6
+
7
+ ```bash
8
+ neo login
9
+ ```
10
+
11
+ 执行流程:
12
+ 1. 自动打开浏览器访问授权页面(如未自动打开,命令行会输出 URL,手动复制到浏览器)
13
+ 2. 输入 NeoCRM 账号密码并选择租户(此处选择的租户即为后续 push/pull/delete 的目标租户)
14
+ 3. 授权成功后自动跳转回本地
15
+ 4. Token 自动保存到项目目录的 `.neo-cli/token.json`
16
+
17
+ > 此步骤需要用户在浏览器中手动完成,AI 无法自动执行。
18
+
19
+ Token 有效期:
20
+ - access_token:2 小时(过期前 5 分钟自动刷新)
21
+ - refresh_token:30 天(过期需重新 `neo login`)
22
+
23
+ 登出:`neo logout`(清除本地 token)
24
+
25
+ ### 自定义环境配置
26
+
27
+ 在 `neo.config.js` 中配置(`neo login` 选择「自定义环境」时生效):
28
+
29
+ ```javascript
30
+ module.exports = {
31
+ neoConfig: {
32
+ neoBaseURL: 'https://crm-xx.xiaoshouyi.com',
33
+ loginURL: 'https://login-xx.xiaoshouyi.com/auc/oauth2/auth',
34
+ tokenURL: 'https://login-xx.xiaoshouyi.com/auc/oauth2/token',
35
+ },
36
+ }
37
+ ```
38
+
39
+ > 注意:Token 接口字段名在不同 CLI 版本中可能为 `tokenAPI` 或 `tokenURL`,两者等价。`neo init` 生成的模板中使用 `tokenURL`。
40
+
41
+ ## 方式二:密码授权
42
+
43
+ 在 `neo.config.js` 中配置:
44
+
45
+ ```javascript
46
+ module.exports = {
47
+ neoConfig: {
48
+ neoBaseURL: 'https://crm-cd.xiaoshouyi.com',
49
+ tokenAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/token',
50
+ authType: 'password',
51
+ auth: {
52
+ client_id: '<从连接器客户端信息获取>',
53
+ client_secret: '<从连接器客户端信息获取>',
54
+ username: '<销售易系统用户名>',
55
+ password: '<账号密码 + 8位安全令牌,直接拼接>'
56
+ },
57
+ },
58
+ }
59
+ ```
60
+
61
+ 获取方式:
62
+ - client_id / client_secret:通过 [销售易文档中心](https://doc.xiaoshouyi.com) 创建连接器获取
63
+ - 安全令牌:销售易文档中心 → OAuth安全认证 → 密码模式 → 获取令牌
64
+
65
+ ## 两种模式对比
66
+
67
+ | 特性 | OAuth2 | 密码模式 |
68
+ |------|--------|---------|
69
+ | 安全性 | 高(无需存储密码) | 较低 |
70
+ | Token 有效期 | 2 小时(自动刷新) | 永不过期 |
71
+ | 推荐程度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
72
+
73
+ ## 常见问题
74
+
75
+ - 浏览器无法自动打开:命令行会输出授权 URL,手动复制到浏览器
76
+ - Token 刷新失败:refresh_token 过期(30 天),重新 `neo login`
77
+ - 授权后未跳回 redirect_uri:可能被浏览器插件(如 Neo UI Extension)影响,关闭后重试
@@ -0,0 +1,205 @@
1
+ # 组件开发指南
2
+
3
+ ## 目录
4
+
5
+ - [组件模型配置](#组件模型配置)
6
+ - [使用平台实体数据源](#使用平台实体数据源)
7
+ - [使用平台自定义 API](#使用平台自定义-api)
8
+ - [添加事件与动作](#添加事件与动作)
9
+ - [平台预置依赖](#平台预置依赖)
10
+ - [样式隔离](#样式隔离)
11
+
12
+ ## 组件模型配置
13
+
14
+ 组件目录名必须以 `__c` 结尾(如 `helloNeocrm__c`),这是 NeoCRM 平台的命名约定。CLI 会自动扫描 `src/components/` 下的子目录,以目录名作为组件标识,`index.ts/.tsx/.js/.jsx` 作为组件入口,`model.[tj]s` 作为模型文件。
15
+
16
+ 组件模型文件(`model.ts`)定义组件在页面设计器中的配置信息:
17
+
18
+ | 属性 | 默认值 | 说明 |
19
+ |------|--------|------|
20
+ | cmpType | 自动生成(组件目录名) | 组件唯一标识 |
21
+ | label | - | 设计器面板中展示的名称 |
22
+ | description | - | 设计器面板中展示的描述 |
23
+ | iconUrl | - | 设计器面板中展示的图标 |
24
+ | tags | ['自定义组件'] | 分类标签 |
25
+ | exposedToDesigner | true | 是否在设计器中可见 |
26
+ | enableDuplicate | true | 是否允许多次使用 |
27
+ | targetPage | ['all'] | 适配的页面类型 |
28
+ | targetObject | ['all'] | 支持的实体类型 |
29
+ | targetApplication | ['all'] | 支持的应用类型 |
30
+ | targetDevice | 'all' | 支持的设备类型 |
31
+ | defaultComProps | - | 初次插入页面的默认属性 |
32
+ | propsSchema | - | 定义可配置属性面板 |
33
+
34
+ targetPage 可选值:`all`、`indexPage`、`entityListPage`、`entityFormPage`、`entityDetailPage`、`customPage`、`bizPage`
35
+
36
+ propsSchema 配置项类型参考 [neo-register](https://www.npmjs.com/package/neo-register)。
37
+
38
+ ## 使用平台实体数据源
39
+
40
+ 通过 `neo-open-api` 的 `xObject` 对接平台实体数据:
41
+
42
+ ```typescript
43
+ import { xObject } from 'neo-open-api';
44
+
45
+ // 查询
46
+ const { data } = await xObject.query({
47
+ xObjectApiKey: 'Contact',
48
+ fields: ['name', 'phone', 'email'],
49
+ page: 1,
50
+ pageSize: 20,
51
+ orderBy: 'createdAt desc'
52
+ });
53
+
54
+ // 创建
55
+ const { data: newRecord } = await xObject.create('Contact', {
56
+ data: { name: '张三', phone: '13800138000' }
57
+ });
58
+
59
+ // 更新
60
+ await xObject.update('Contact', recordId, {
61
+ data: { name: '张三(更新)' }
62
+ });
63
+
64
+ // 获取详情
65
+ const { data: detail } = await xObject.get('Contact', recordId);
66
+
67
+ // 删除
68
+ await xObject.delete('Contact', recordId);
69
+ ```
70
+
71
+ ## 使用平台自定义 API
72
+
73
+ 通过 `neo-open-api` 的 `customApi` 调用自定义接口:
74
+
75
+ ```typescript
76
+ import { customApi } from 'neo-open-api';
77
+
78
+ const result = await customApi.run({
79
+ apiUrl: '/rest/custom/api/endpoint',
80
+ methodType: 'POST', // 可选,默认 POST
81
+ data: { key1: 'value1' }
82
+ });
83
+ // result: { status: boolean, code, msg, data }
84
+ ```
85
+
86
+ ## 添加事件与动作
87
+
88
+ ### 可触发事件
89
+
90
+ 1. 组件中用 `@NeoEvent.dispatch` 声明事件方法:
91
+
92
+ ```typescript
93
+ // @ts-ignore
94
+ import { NeoEvent } from 'neo-ui-common';
95
+
96
+ @NeoEvent.dispatch
97
+ onSubmit() {
98
+ console.log('触发表单提交事件');
99
+ }
100
+ ```
101
+
102
+ 2. 模型中声明事件列表:
103
+
104
+ ```typescript
105
+ events = [
106
+ {
107
+ apiKey: 'onSubmit', // 与组件方法名一致
108
+ label: '提交表单后',
109
+ helpText: '表单提交后触发该事件',
110
+ },
111
+ ];
112
+ ```
113
+
114
+ ### 可执行动作(组件函数)
115
+
116
+ 1. 组件中用 `@NeoEvent.function` 声明动作方法:
117
+
118
+ ```typescript
119
+ // @ts-ignore
120
+ import { NeoEvent } from 'neo-ui-common';
121
+
122
+ @NeoEvent.function
123
+ async loadData(page = 1, pageSize = 10) {
124
+ // 加载数据逻辑
125
+ }
126
+ ```
127
+
128
+ 2. 模型中声明函数列表:
129
+
130
+ ```typescript
131
+ functions = [
132
+ {
133
+ apiKey: 'loadData',
134
+ label: '刷新表格',
135
+ helpTextKey: '刷新当前表格数据',
136
+ },
137
+ {
138
+ apiKey: 'handleDelete',
139
+ label: '删除记录',
140
+ helpTextKey: '删除指定记录',
141
+ funcInParams: [
142
+ { apiKey: 'id', label: '记录ID', type: 'String', required: true },
143
+ ],
144
+ },
145
+ ];
146
+ ```
147
+
148
+ ### 广播事件(不推荐)
149
+
150
+ 优先使用上述声明式事件/动作。如确需广播:
151
+
152
+ ```typescript
153
+ // @ts-ignore
154
+ import { NeoEvent } from 'neo-ui-common';
155
+
156
+ // 触发
157
+ NeoEvent.broadcast('eventName', data);
158
+
159
+ // 监听
160
+ NeoEvent.listen('eventName', (eventData) => { /* ... */ });
161
+ ```
162
+
163
+ ## 平台预置依赖
164
+
165
+ 构建时自动剔除以下依赖(平台已预置,组件中直接 import 即可):
166
+
167
+ | 依赖包 | 版本 |
168
+ |--------|------|
169
+ | react / react-dom | ^16.13.1 |
170
+ | mobx | ^6.3.0 |
171
+ | mobx-react | ^7.0.0 |
172
+ | mobx-state-tree | ^5.4.0 |
173
+ | echarts | 5.4.2 |
174
+ | antd | 4.9.4 |
175
+ | antd-mobile | 2.3.4 |
176
+ | @ant-design/icons | ^4.8.0 |
177
+ | axios | ^0.27.2 |
178
+ | lodash | ^4.17.21 |
179
+ | classnames | ^2.3.2 |
180
+ | qs | ^6.11.0 |
181
+ | neo-ui-component-web | ^1.0.0 |
182
+ | neo-ui-common | ^1.0.0 |
183
+ | neo-open-api | ^1.1.9 |
184
+ | amis | ^1.1.5 |
185
+ | video-react | 0.14.1 |
186
+
187
+ > 组件中使用的版本需与平台预置版本匹配,否则运行时可能异常。
188
+
189
+ ## 样式隔离
190
+
191
+ 发布和 linkDebug 时自动给组件添加样式隔离(处理 `(index|style).(scss|less)` 文件)。
192
+
193
+ 要求:组件根节点的 class 名需与组件目录名一致(如组件目录为 `helloNeocrm__c`,则根节点 class 应为 `helloNeocrm__c`),否则根节点样式失效。
194
+
195
+ Sass 注意事项:样式隔离处理时,scss 中使用 `/` 做除法会触发 Dart Sass 2.0 废弃警告,建议改用 `math.div()` 或 `calc()`。
196
+
197
+ 关闭样式隔离:在 `neo.config.js` 中设置:
198
+
199
+ ```javascript
200
+ module.exports = {
201
+ webpack: {
202
+ disableAutoAddStyleScope: true
203
+ }
204
+ }
205
+ ```