neo-cmp-cli 1.13.16 → 1.13.18

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 (182) hide show
  1. package/README.md +2 -1
  2. package/dist/index2.js +1 -1
  3. package/dist/main2.js +1 -1
  4. package/dist/neo/env.js +1 -1
  5. package/dist/neo/neoLogin.js +1 -1
  6. package/dist/neo/pushCmp.js +1 -1
  7. package/dist/package.json.js +1 -1
  8. package/package.json +3 -2
  9. package/template/antd-custom-cmp-template/package.json +1 -1
  10. package/template/asset-manage-template/docs/README.md +1 -232
  11. package/template/asset-manage-template/package.json +2 -2
  12. package/template/echarts-custom-cmp-template/package.json +1 -1
  13. package/template/empty-custom-cmp-template/package.json +2 -2
  14. package/template/map-custom-cmp-template/package.json +1 -1
  15. package/template/neo-bi-cmps/neo.config.js +7 -1
  16. package/template/neo-bi-cmps/package.json +8 -7
  17. package/template/neo-bi-cmps/public/403.html +77 -0
  18. package/template/neo-bi-cmps/public/demo.html +2453 -0
  19. package/template/neo-bi-cmps/src/assets/icon/barChart.svg +1 -0
  20. package/template/neo-bi-cmps/src/assets/icon/card.svg +1 -0
  21. package/template/neo-bi-cmps/src/assets/icon/filter.svg +1 -0
  22. package/template/neo-bi-cmps/src/assets/icon/funnel.svg +1 -0
  23. package/template/neo-bi-cmps/src/assets/icon/tab.svg +1 -0
  24. package/template/neo-bi-cmps/src/components/filterBar__c/README.md +3 -14
  25. package/template/neo-bi-cmps/src/components/filterBar__c/common.scss +29 -0
  26. package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +668 -146
  27. package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +26 -48
  28. package/template/neo-bi-cmps/src/components/filterBar__c/style.scss +46 -139
  29. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.tsx +11 -10
  30. package/template/neo-bi-cmps/src/components/targetNumber__c/index.tsx +9 -16
  31. package/template/neo-bi-cmps/src/components/targetNumber__c/model.ts +1 -1
  32. package/template/neo-bi-cmps/src/utils/common.ts +231 -0
  33. package/template/neo-bi-cmps/src/utils/filter2chartFilter.ts +268 -0
  34. package/template/neo-bi-cmps/src/utils/filterBar.ts +140 -0
  35. package/template/neo-bi-cmps/src/utils/pipelineFunnel.ts +341 -0
  36. package/template/{neo-h5-cmps/src/utils/queryObjectData.ts → neo-bi-cmps/src/utils/queryByCustomSQL.ts} +18 -13
  37. package/template/neo-bi-cmps/src/utils/requestDebounce.ts +22 -0
  38. package/template/neo-bi-cmps/src/utils/simpleTable.tsx +344 -0
  39. package/template/neo-bi-cmps/src/utils/stageSwitch.ts +15 -0
  40. package/template/neo-bi-cmps/src/utils/stageTimeChart.ts +90 -0
  41. package/template/neo-bi-cmps/src/utils/targetNumber.ts +12 -0
  42. package/template/neo-custom-cmp-template/docs/README.md +0 -231
  43. package/template/neo-custom-cmp-template/package.json +2 -2
  44. package/template/neo-h5-cmps/package.json +2 -2
  45. package/template/neo-h5-cmps/src/components/entityList__c/index.tsx +1 -2
  46. package/template/neo-h5-cmps/src/components/entityTabs__c/index.tsx +1 -1
  47. package/template/neo-h5-cmps/src/components/globalSearchInput__c/index.tsx +1 -1
  48. package/template/neo-h5-cmps/src/components/openChatPageBtn__c/index.tsx +1 -2
  49. package/template/neo-order-cmps/package.json +2 -2
  50. package/template/neo-pipeline-cmps/.prettierrc.js +12 -0
  51. package/template/neo-pipeline-cmps/@types/neo-ui-common.d.ts +36 -0
  52. package/template/neo-pipeline-cmps/README.md +99 -0
  53. package/template/neo-pipeline-cmps/commitlint.config.js +59 -0
  54. package/template/neo-pipeline-cmps/neo.config.js +135 -0
  55. package/template/neo-pipeline-cmps/package.json +66 -0
  56. package/template/neo-pipeline-cmps/public/403.html +77 -0
  57. package/template/neo-pipeline-cmps/public/css/base.css +283 -0
  58. package/template/neo-pipeline-cmps/public/demo.html +2453 -0
  59. package/template/neo-pipeline-cmps/public/scripts/app/bluebird.js +6679 -0
  60. package/template/neo-pipeline-cmps/public/template.html +13 -0
  61. package/template/neo-pipeline-cmps/src/assets/css/common.scss +127 -0
  62. package/template/neo-pipeline-cmps/src/assets/css/mixin.scss +47 -0
  63. package/template/neo-pipeline-cmps/src/assets/icon/barChart.svg +1 -0
  64. package/template/neo-pipeline-cmps/src/assets/icon/card.svg +1 -0
  65. package/template/neo-pipeline-cmps/src/assets/icon/filter.svg +1 -0
  66. package/template/neo-pipeline-cmps/src/assets/icon/funnel.svg +1 -0
  67. package/template/neo-pipeline-cmps/src/assets/icon/tab.svg +1 -0
  68. package/template/neo-pipeline-cmps/src/assets/img/AIBtn.gif +0 -0
  69. package/template/neo-pipeline-cmps/src/assets/img/NeoCRM.jpg +0 -0
  70. package/template/neo-pipeline-cmps/src/assets/img/aiLogo.png +0 -0
  71. package/template/neo-pipeline-cmps/src/assets/img/card-list.svg +1 -0
  72. package/template/neo-pipeline-cmps/src/assets/img/contact-form.svg +1 -0
  73. package/template/neo-pipeline-cmps/src/assets/img/custom-form.svg +1 -0
  74. package/template/neo-pipeline-cmps/src/assets/img/custom-widget.svg +1 -0
  75. package/template/neo-pipeline-cmps/src/assets/img/data-list.svg +1 -0
  76. package/template/neo-pipeline-cmps/src/assets/img/detail.svg +1 -0
  77. package/template/neo-pipeline-cmps/src/assets/img/favicon.png +0 -0
  78. package/template/neo-pipeline-cmps/src/assets/img/map.svg +1 -0
  79. package/template/neo-pipeline-cmps/src/assets/img/search.svg +1 -0
  80. package/template/neo-pipeline-cmps/src/assets/img/table.svg +1 -0
  81. package/template/neo-pipeline-cmps/src/components/filterBar__c/README.md +24 -0
  82. package/template/neo-pipeline-cmps/src/components/filterBar__c/common.scss +29 -0
  83. package/template/neo-pipeline-cmps/src/components/filterBar__c/index.tsx +731 -0
  84. package/template/neo-pipeline-cmps/src/components/filterBar__c/model.ts +52 -0
  85. package/template/neo-pipeline-cmps/src/components/filterBar__c/style.scss +119 -0
  86. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/README.md +39 -0
  87. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/index.tsx +416 -0
  88. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +80 -0
  89. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/style.scss +83 -0
  90. package/template/neo-pipeline-cmps/src/components/showHealthResult__c/index.tsx +470 -0
  91. package/template/neo-pipeline-cmps/src/components/showHealthResult__c/model.ts +45 -0
  92. package/template/neo-pipeline-cmps/src/components/showHealthResult__c/style.scss +137 -0
  93. package/template/neo-pipeline-cmps/src/components/simpleTable__c/README.md +89 -0
  94. package/template/neo-pipeline-cmps/src/components/simpleTable__c/common.scss +195 -0
  95. package/template/neo-pipeline-cmps/src/components/simpleTable__c/index.tsx +667 -0
  96. package/template/neo-pipeline-cmps/src/components/simpleTable__c/model.ts +124 -0
  97. package/template/neo-pipeline-cmps/src/components/simpleTable__c/style.scss +192 -0
  98. package/template/neo-pipeline-cmps/src/components/stageSwitch__c/README.md +36 -0
  99. package/template/neo-pipeline-cmps/src/components/stageSwitch__c/index.tsx +513 -0
  100. package/template/neo-pipeline-cmps/src/components/stageSwitch__c/model.ts +71 -0
  101. package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageSwitch__c/style.scss +4 -2
  102. package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/README.md +37 -0
  103. package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/index.tsx +455 -0
  104. package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/model.ts +106 -0
  105. package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageTimeChart__c/style.scss +3 -2
  106. package/template/neo-pipeline-cmps/src/utils/common.ts +229 -0
  107. package/template/neo-pipeline-cmps/src/utils/filter2chartFilter.ts +266 -0
  108. package/template/neo-pipeline-cmps/src/utils/filterBar.ts +140 -0
  109. package/template/neo-pipeline-cmps/src/utils/pipelineFunnel.ts +343 -0
  110. package/template/neo-pipeline-cmps/src/utils/queryByCustomSQL.ts +121 -0
  111. package/template/neo-pipeline-cmps/src/utils/requestDebounce.ts +22 -0
  112. package/template/neo-pipeline-cmps/src/utils/simpleTable.tsx +349 -0
  113. package/template/neo-pipeline-cmps/src/utils/stageSwitch.ts +15 -0
  114. package/template/neo-pipeline-cmps/src/utils/stageTimeChart.ts +90 -0
  115. package/template/neo-pipeline-cmps/src/utils/targetNumber.ts +12 -0
  116. package/template/neo-pipeline-cmps/tsconfig.json +40 -0
  117. package/template/neo-web-entity-grid/package.json +2 -2
  118. package/template/neo-web-form/package.json +2 -2
  119. package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +161 -41
  120. package/template/neo-web-form/src/components/batchAddTable__c/model.ts +4 -2
  121. package/template/react-custom-cmp-template/package.json +1 -1
  122. package/template/react-ts-custom-cmp-template/package.json +1 -1
  123. package/template/vue2-custom-cmp-template/package.json +1 -1
  124. package/template/asset-manage-template/src/utils/axiosFetcher.ts +0 -37
  125. package/template/asset-manage-template/src/utils/queryObjectData.ts +0 -112
  126. package/template/asset-manage-template/src/utils/xobjects.ts +0 -162
  127. package/template/neo-bi-cmps/.npmrc copy +0 -1
  128. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/README.md +0 -52
  129. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/index.tsx +0 -183
  130. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/model.ts +0 -90
  131. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/style.scss +0 -218
  132. package/template/neo-bi-cmps/src/components/forecastChart__c/README.md +0 -31
  133. package/template/neo-bi-cmps/src/components/forecastChart__c/index.tsx +0 -158
  134. package/template/neo-bi-cmps/src/components/forecastChart__c/model.ts +0 -40
  135. package/template/neo-bi-cmps/src/components/forecastChart__c/style.scss +0 -154
  136. package/template/neo-bi-cmps/src/components/forecastGrid__c/README.md +0 -36
  137. package/template/neo-bi-cmps/src/components/forecastGrid__c/index.tsx +0 -86
  138. package/template/neo-bi-cmps/src/components/forecastGrid__c/model.ts +0 -62
  139. package/template/neo-bi-cmps/src/components/forecastGrid__c/style.scss +0 -48
  140. package/template/neo-bi-cmps/src/components/gapCloser__c/README.md +0 -24
  141. package/template/neo-bi-cmps/src/components/gapCloser__c/index.tsx +0 -100
  142. package/template/neo-bi-cmps/src/components/gapCloser__c/model.ts +0 -46
  143. package/template/neo-bi-cmps/src/components/gapCloser__c/style.scss +0 -60
  144. package/template/neo-bi-cmps/src/components/kpiCards__c/README.md +0 -35
  145. package/template/neo-bi-cmps/src/components/kpiCards__c/index.tsx +0 -70
  146. package/template/neo-bi-cmps/src/components/kpiCards__c/model.ts +0 -50
  147. package/template/neo-bi-cmps/src/components/kpiCards__c/style.scss +0 -33
  148. package/template/neo-bi-cmps/src/components/oppList__c/README.md +0 -52
  149. package/template/neo-bi-cmps/src/components/oppList__c/index.tsx +0 -285
  150. package/template/neo-bi-cmps/src/components/oppList__c/model.ts +0 -86
  151. package/template/neo-bi-cmps/src/components/oppList__c/style.scss +0 -133
  152. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/README.md +0 -39
  153. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/index.tsx +0 -130
  154. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/model.ts +0 -66
  155. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/style.scss +0 -133
  156. package/template/neo-bi-cmps/src/components/stageSwitch__c/README.md +0 -36
  157. package/template/neo-bi-cmps/src/components/stageSwitch__c/index.tsx +0 -118
  158. package/template/neo-bi-cmps/src/components/stageSwitch__c/model.ts +0 -92
  159. package/template/neo-bi-cmps/src/components/stageTimeChart__c/README.md +0 -37
  160. package/template/neo-bi-cmps/src/components/stageTimeChart__c/index.tsx +0 -126
  161. package/template/neo-bi-cmps/src/components/stageTimeChart__c/model.ts +0 -57
  162. package/template/neo-bi-cmps/src/components/tabSwitch__c/README.md +0 -37
  163. package/template/neo-bi-cmps/src/components/tabSwitch__c/index.tsx +0 -80
  164. package/template/neo-bi-cmps/src/components/tabSwitch__c/model.ts +0 -45
  165. package/template/neo-bi-cmps/src/components/tabSwitch__c/style.scss +0 -37
  166. package/template/neo-bi-cmps/src/utils/axiosFetcher.ts +0 -37
  167. package/template/neo-bi-cmps/src/utils/queryObjectData.ts +0 -76
  168. package/template/neo-bi-cmps/src/utils/xobjects.ts +0 -162
  169. package/template/neo-custom-cmp-template/src/utils/axiosFetcher.ts +0 -37
  170. package/template/neo-custom-cmp-template/src/utils/queryObjectData.ts +0 -112
  171. package/template/neo-custom-cmp-template/src/utils/xobjects.ts +0 -162
  172. package/template/neo-h5-cmps/src/utils/axiosFetcher.ts +0 -37
  173. package/template/neo-h5-cmps/src/utils/xobjects.ts +0 -167
  174. package/template/neo-order-cmps/src/utils/axiosFetcher.ts +0 -37
  175. package/template/neo-order-cmps/src/utils/queryObjectData.ts +0 -112
  176. package/template/neo-order-cmps/src/utils/xobjects.ts +0 -162
  177. package/template/neo-web-entity-grid/src/utils/axiosFetcher.ts +0 -37
  178. package/template/neo-web-entity-grid/src/utils/queryObjectData.ts +0 -112
  179. package/template/neo-web-entity-grid/src/utils/xobjects.ts +0 -167
  180. package/template/neo-web-form/src/utils/axiosFetcher.ts +0 -37
  181. package/template/neo-web-form/src/utils/queryObjectData.ts +0 -112
  182. package/template/neo-web-form/src/utils/xobjects.ts +0 -167
@@ -0,0 +1,83 @@
1
+ .pipelineFunnel__c {
2
+ background: #fff;
3
+ border-radius: 8px;
4
+ padding: 12px 20px;
5
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
6
+ min-height: 400px;
7
+
8
+ .funnel-header {
9
+ display: flex;
10
+ justify-content: space-between;
11
+ align-items: center;
12
+ margin-bottom: 4px;
13
+ }
14
+
15
+ .funnel-title {
16
+ font-size: 14px;
17
+ font-weight: 600;
18
+ margin: 0;
19
+ }
20
+
21
+ .ai-btn {
22
+ cursor: pointer;
23
+ font-size: 16px;
24
+ transition: transform 0.2s;
25
+
26
+ &:hover {
27
+ transform: scale(1.1);
28
+ }
29
+ }
30
+
31
+ .funnel-total {
32
+ text-align: center;
33
+ margin-bottom: 0;
34
+
35
+ .funnel-total-label {
36
+ font-size: 12px;
37
+ color: #888;
38
+ }
39
+
40
+ .funnel-total-value {
41
+ font-size: 20px;
42
+ font-weight: 700;
43
+ }
44
+ }
45
+
46
+ .funnel-body {
47
+ min-height: 220px;
48
+ }
49
+
50
+ .funnel-chart-wrap {
51
+ width: 100%;
52
+ position: relative;
53
+ }
54
+
55
+ .funnel-chart {
56
+ min-height: 200px;
57
+ }
58
+
59
+ .funnel-legend {
60
+ display: flex;
61
+ gap: 12px;
62
+ justify-content: center;
63
+ flex-wrap: wrap;
64
+ margin-top: 8px;
65
+ font-size: 11px;
66
+ color: #666;
67
+ }
68
+
69
+ .legend-item {
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 4px;
73
+ }
74
+
75
+ .legend-dot {
76
+ display: inline-block;
77
+ width: 10px;
78
+ height: 10px;
79
+ border-radius: 50%;
80
+ margin-right: 4px;
81
+ vertical-align: middle;
82
+ }
83
+ }
@@ -0,0 +1,470 @@
1
+ /**
2
+ * @file Opportunity Health Assessment Result Display
3
+ * @description Calls /rest/ai/v2.0/agent/apps/health_assessment/get_health_assessment_result, displays assessmentConclusion summary and ECharts radar chart based on assessmentDimension
4
+ */
5
+ import * as React from 'react';
6
+ import * as echarts from 'echarts';
7
+ // @ts-ignore
8
+ import { request } from 'neo-open-api';
9
+ // @ts-ignore
10
+ import { BaseCmp, NeoEvent } from 'neo-ui-common';
11
+
12
+ import './style.scss';
13
+
14
+ const HEALTH_ASSESSMENT_URL =
15
+ '/rest/ai/v2.0/agent/apps/health_assessment/get_health_assessment_result';
16
+
17
+ export type HealthScoreRow = { name: string; score: number };
18
+
19
+ interface ShowHealthResultProps {
20
+ /** Entity API Key, corresponding to request body object_api_key */
21
+ xObjectApiKey?: string;
22
+ /** Business data ID, corresponding to request body data_id */
23
+ id?: string;
24
+ className?: string;
25
+ style?: React.CSSProperties;
26
+ data?: any;
27
+ }
28
+
29
+ interface ShowHealthResultState {
30
+ loading: boolean;
31
+ error: string | null;
32
+ summaryText: string;
33
+ scoreRows: HealthScoreRow[];
34
+ }
35
+
36
+ function unwrapResultRecord(res: any): Record<string, unknown> {
37
+ if (!res || typeof res !== 'object') return {};
38
+ const r = res as Record<string, unknown>;
39
+ const inner = r.data;
40
+ if (inner && typeof inner === 'object' && !Array.isArray(inner)) {
41
+ const d = inner as Record<string, unknown>;
42
+ if ('assessmentConclusion' in d || 'assessmentDimension' in d) {
43
+ return d;
44
+ }
45
+ }
46
+ if ('assessmentConclusion' in r || 'assessmentDimension' in r) {
47
+ return r;
48
+ }
49
+ if (inner && typeof inner === 'object') {
50
+ return inner as Record<string, unknown>;
51
+ }
52
+ return {};
53
+ }
54
+
55
+ function parseSummaryFromConclusion(conclusion: unknown): string {
56
+ if (conclusion == null) return '';
57
+ if (typeof conclusion === 'string') return conclusion.trim();
58
+ if (Array.isArray(conclusion)) {
59
+ const parts = conclusion
60
+ .map((x: any) => {
61
+ if (!x || typeof x !== 'object') return '';
62
+ return (
63
+ x.summary ?? x.conclusion ?? x.desc ?? x.description ?? x.remark ?? ''
64
+ );
65
+ })
66
+ .filter((s: string) => String(s).trim());
67
+ return parts.join('\n').trim();
68
+ }
69
+ if (typeof conclusion === 'object') {
70
+ const o = conclusion as Record<string, unknown>;
71
+ const text =
72
+ o.summary ?? o.conclusion ?? o.text ?? o.assessmentSummary ?? '';
73
+ return String(text ?? '').trim();
74
+ }
75
+ return String(conclusion).trim();
76
+ }
77
+
78
+ /**
79
+ * Radar chart only uses the API field assessmentDimension, which must be { name, score }[].
80
+ * Does not read conclusion or parse other key names (such as label / value / items wrappers).
81
+ */
82
+ function scoreRowsFromAssessmentDimension(
83
+ dimension: unknown,
84
+ ): HealthScoreRow[] {
85
+ if (!Array.isArray(dimension)) return [];
86
+ const rows: HealthScoreRow[] = [];
87
+ for (const item of dimension) {
88
+ if (!item || typeof item !== 'object') continue;
89
+ const o = item as Record<string, unknown>;
90
+ if (!('name' in o) || !('score' in o)) continue;
91
+ const name = String(o.name ?? '').trim();
92
+ if (!name) continue;
93
+ const scoreRaw = o.score;
94
+ let score = 0;
95
+ if (typeof scoreRaw === 'number' && Number.isFinite(scoreRaw)) {
96
+ score = scoreRaw;
97
+ } else if (scoreRaw != null && scoreRaw !== '') {
98
+ const p = parseFloat(String(scoreRaw));
99
+ score = Number.isFinite(p) ? p : 0;
100
+ }
101
+ rows.push({ name, score });
102
+ }
103
+ return rows;
104
+ }
105
+
106
+ /**
107
+ * Radar axis outer label: no 6-character truncation; English wraps by word, non-spaced text (e.g. Chinese) wraps at fixed width.
108
+ * When too many lines, only the last line gets an ellipsis, other lines are shown as completely as possible.
109
+ */
110
+ function wrapRadarAxisName(
111
+ text: string,
112
+ maxLineLen = 18,
113
+ maxLines = 8,
114
+ ): string {
115
+ const t = text.trim();
116
+ if (!t) return t;
117
+ if (t.length <= maxLineLen) return t;
118
+
119
+ const lines: string[] = [];
120
+ const push = (s: string) => {
121
+ if (s) lines.push(s);
122
+ };
123
+
124
+ if (/\s/.test(t)) {
125
+ const words = t.split(/\s+/).filter(Boolean);
126
+ let cur = '';
127
+ for (const w of words) {
128
+ if (w.length > maxLineLen) {
129
+ push(cur);
130
+ cur = '';
131
+ for (let i = 0; i < w.length; i += maxLineLen) {
132
+ push(w.slice(i, i + maxLineLen));
133
+ }
134
+ continue;
135
+ }
136
+ const next = cur ? `${cur} ${w}` : w;
137
+ if (next.length <= maxLineLen) cur = next;
138
+ else {
139
+ push(cur);
140
+ cur = w;
141
+ }
142
+ }
143
+ push(cur);
144
+ } else {
145
+ for (let i = 0; i < t.length; i += maxLineLen) {
146
+ push(t.slice(i, i + maxLineLen));
147
+ }
148
+ }
149
+
150
+ if (lines.length <= maxLines) return lines.join('\n');
151
+
152
+ const head = lines.slice(0, maxLines - 1);
153
+ const rest = lines.slice(maxLines - 1).join('');
154
+ const last =
155
+ rest.length > maxLineLen ? `${rest.slice(0, maxLineLen - 1)}…` : `${rest}…`;
156
+ return [...head, last].join('\n');
157
+ }
158
+
159
+ function buildHealthRadarOption(
160
+ items: HealthScoreRow[],
161
+ ): echarts.EChartsOption {
162
+ if (!items.length) {
163
+ return {
164
+ graphic: [
165
+ {
166
+ type: 'text',
167
+ left: 'center',
168
+ top: 'middle',
169
+ style: {
170
+ text: 'No radar chart dimension data available',
171
+ fill: '#999',
172
+ fontSize: 12,
173
+ },
174
+ },
175
+ ],
176
+ };
177
+ }
178
+
179
+ // Each axis has its own max to avoid low-score dimensions being squeezed; health scores are typically 0-100
180
+ const indicator = items.map((i) => ({
181
+ name: i.name,
182
+ max: Math.max(10, i.score, 1),
183
+ }));
184
+
185
+ return {
186
+ tooltip: {
187
+ trigger: 'item',
188
+ },
189
+ radar: {
190
+ indicator,
191
+ radius: '52%',
192
+ center: ['50%', '52%'],
193
+ axisName: {
194
+ color: '#666',
195
+ fontSize: 10,
196
+ lineHeight: 13,
197
+ formatter(name: string) {
198
+ return wrapRadarAxisName(name);
199
+ },
200
+ },
201
+ splitLine: {
202
+ lineStyle: { color: '#e5e7eb' },
203
+ },
204
+ splitArea: { show: false },
205
+ axisLine: {
206
+ lineStyle: { color: '#e5e7eb' },
207
+ },
208
+ },
209
+ series: [
210
+ {
211
+ type: 'radar',
212
+ data: [
213
+ {
214
+ value: items.map((i) => i.score),
215
+ name: 'Health Score',
216
+ areaStyle: {
217
+ color: 'rgba(99,102,241,0.2)',
218
+ },
219
+ lineStyle: {
220
+ color: '#6366f1',
221
+ width: 2,
222
+ },
223
+ itemStyle: {
224
+ color: '#6366f1',
225
+ },
226
+ },
227
+ ],
228
+ },
229
+ ],
230
+ };
231
+ }
232
+
233
+ class ShowHealthResult extends BaseCmp<
234
+ ShowHealthResultProps,
235
+ ShowHealthResultState
236
+ > {
237
+ private radarChartRef = React.createRef<HTMLDivElement>();
238
+
239
+ private radarChartInstance: echarts.ECharts | null = null;
240
+
241
+ private radarResizeObserver: ResizeObserver | null = null;
242
+
243
+ constructor(props: ShowHealthResultProps) {
244
+ super(props);
245
+ this.state = {
246
+ loading: false,
247
+ error: null,
248
+ summaryText: '',
249
+ scoreRows: [],
250
+ };
251
+
252
+ this.initRadarChart = this.initRadarChart.bind(this);
253
+ this.updateRadarChart = this.updateRadarChart.bind(this);
254
+ this.bindRadarResize = this.bindRadarResize.bind(this);
255
+ this.unbindRadarResize = this.unbindRadarResize.bind(this);
256
+ this.handleRadarWindowResize = this.handleRadarWindowResize.bind(this);
257
+ this.disposeRadarChart = this.disposeRadarChart.bind(this);
258
+ }
259
+
260
+ componentDidMount() {
261
+ this.fetchHealthAssessment();
262
+ }
263
+
264
+ componentDidUpdate(
265
+ prevProps: ShowHealthResultProps,
266
+ prevState: ShowHealthResultState,
267
+ ) {
268
+ if (
269
+ prevProps.id !== this.props.id ||
270
+ prevProps.xObjectApiKey !== this.props.xObjectApiKey
271
+ ) {
272
+ this.fetchHealthAssessment();
273
+ }
274
+
275
+ const hasBlock =
276
+ !this.state.error &&
277
+ (this.state.summaryText || this.state.scoreRows.length > 0);
278
+
279
+ if (!hasBlock) {
280
+ if (this.radarChartInstance) {
281
+ this.disposeRadarChart();
282
+ }
283
+ return;
284
+ }
285
+
286
+ if (!this.radarChartInstance && this.radarChartRef.current) {
287
+ this.initRadarChart();
288
+ } else if (prevState.scoreRows !== this.state.scoreRows) {
289
+ this.updateRadarChart();
290
+ }
291
+ }
292
+
293
+ componentWillUnmount() {
294
+ this.disposeRadarChart();
295
+ }
296
+
297
+ disposeRadarChart() {
298
+ this.unbindRadarResize();
299
+ if (this.radarChartInstance) {
300
+ this.radarChartInstance.dispose();
301
+ this.radarChartInstance = null;
302
+ }
303
+ }
304
+
305
+ initRadarChart() {
306
+ if (!this.radarChartRef.current || this.radarChartInstance) return;
307
+ this.radarChartInstance = echarts.init(this.radarChartRef.current);
308
+ this.bindRadarResize();
309
+ this.updateRadarChart();
310
+ }
311
+
312
+ bindRadarResize() {
313
+ if (typeof window === 'undefined') return;
314
+ window.addEventListener('resize', this.handleRadarWindowResize);
315
+ if (typeof ResizeObserver === 'undefined') return;
316
+ this.radarResizeObserver = new ResizeObserver(() => {
317
+ this.radarChartInstance?.resize();
318
+ });
319
+ const el = this.radarChartRef.current;
320
+ if (el) this.radarResizeObserver.observe(el);
321
+ }
322
+
323
+ unbindRadarResize() {
324
+ if (typeof window !== 'undefined') {
325
+ window.removeEventListener('resize', this.handleRadarWindowResize);
326
+ }
327
+ this.radarResizeObserver?.disconnect();
328
+ this.radarResizeObserver = null;
329
+ }
330
+
331
+ handleRadarWindowResize() {
332
+ this.radarChartInstance?.resize();
333
+ }
334
+
335
+ updateRadarChart() {
336
+ if (!this.radarChartInstance) return;
337
+ const opt = buildHealthRadarOption(this.state.scoreRows);
338
+ this.radarChartInstance.setOption(opt, { notMerge: true });
339
+ requestAnimationFrame(() => {
340
+ this.radarChartInstance?.resize();
341
+ });
342
+ }
343
+
344
+ async fetchHealthAssessment() {
345
+ const xObjectApiKey = this.props.xObjectApiKey;
346
+ const id = this.props.id;
347
+
348
+ this.setState({ loading: true, error: null });
349
+
350
+ try {
351
+ const res = await request({
352
+ url: HEALTH_ASSESSMENT_URL,
353
+ method: 'POST',
354
+ data: {
355
+ data_id: id,
356
+ object_api_key: String(xObjectApiKey),
357
+ },
358
+ });
359
+
360
+ const record = unwrapResultRecord(res);
361
+ const conclusion = record.assessmentConclusion;
362
+ const dimension = record.assessmentDimension;
363
+
364
+ const summaryText = parseSummaryFromConclusion(conclusion);
365
+ const scoreRows = scoreRowsFromAssessmentDimension(dimension);
366
+
367
+ this.setState(
368
+ {
369
+ loading: false,
370
+ error: null,
371
+ summaryText,
372
+ scoreRows,
373
+ },
374
+ () => {
375
+ if (!this.radarChartInstance && this.radarChartRef.current) {
376
+ this.initRadarChart();
377
+ } else {
378
+ this.updateRadarChart();
379
+ }
380
+ },
381
+ );
382
+ } catch (e: any) {
383
+ console.error('showHealthResult health assessment API failed:', e);
384
+ this.setState(
385
+ {
386
+ loading: false,
387
+ error: e?.message || 'Request failed',
388
+ summaryText: '',
389
+ scoreRows: [],
390
+ },
391
+ () => this.disposeRadarChart(),
392
+ );
393
+ }
394
+ }
395
+
396
+ @NeoEvent.function
397
+ async refreshData() {
398
+ await this.fetchHealthAssessment();
399
+ }
400
+
401
+ render() {
402
+ const { className, style } = this.props;
403
+ const { loading, error, summaryText, scoreRows } = this.state;
404
+
405
+ console.log('showHealthResult__c:', scoreRows, this.props);
406
+
407
+ return (
408
+ <div
409
+ className={`showHealthResult__c ${className || ''}`}
410
+ style={style}
411
+ data-time="2026.4.17 01"
412
+ >
413
+ {loading && (
414
+ <div className="showHealthResult__c__loading" aria-busy="true">
415
+ <span className="showHealthResult__c__loadingDot" />
416
+ <span className="showHealthResult__c__loadingDot" />
417
+ <span className="showHealthResult__c__loadingDot" />
418
+ <span className="showHealthResult__c__loadingText">
419
+ Fetching health assessment...
420
+ </span>
421
+ </div>
422
+ )}
423
+
424
+ {error && !loading && (
425
+ <div
426
+ className="showHealthResult__c__placeholder showHealthResult__c__placeholder--error"
427
+ role="alert"
428
+ >
429
+ <div className="showHealthResult__c__placeholderBody">
430
+ <div className="showHealthResult__c__placeholderTitle">
431
+ Unable to retrieve assessment results at this time
432
+ </div>
433
+ <div className="showHealthResult__c__placeholderMsg">{error}</div>
434
+ </div>
435
+ </div>
436
+ )}
437
+
438
+ {!loading && !error && !summaryText && !scoreRows.length && (
439
+ <div className="showHealthResult__c__placeholder showHealthResult__c__placeholder--empty">
440
+ <div className="showHealthResult__c__placeholderBody">
441
+ <div className="showHealthResult__c__placeholderTitle">
442
+ No assessment data available
443
+ </div>
444
+ <div className="showHealthResult__c__placeholderHint">
445
+ The current record may not have generated a health assessment
446
+ conclusion yet. Please try again later or check if the data is
447
+ complete.
448
+ </div>
449
+ </div>
450
+ </div>
451
+ )}
452
+
453
+ {(summaryText || scoreRows.length > 0) && (
454
+ <>
455
+ <div className="showHealthResult__c__summary" id="stSummary">
456
+ {summaryText || ' '}
457
+ </div>
458
+ <div
459
+ ref={this.radarChartRef}
460
+ className="showHealthResult__c__radar"
461
+ style={{ width: '100%', height: 300 }}
462
+ />
463
+ </>
464
+ )}
465
+ </div>
466
+ );
467
+ }
468
+ }
469
+
470
+ export default ShowHealthResult;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @file Opportunity Health Assessment Result Display - Editor model
3
+ */
4
+ export class ShowHealthResultModel {
5
+ label: string = 'Health Score Display';
6
+
7
+ description: string =
8
+ 'Calls the health assessment API to display conclusion summary and radar chart';
9
+
10
+ iconUrl: string = 'https://custom-widgets.bj.bcebos.com/detail.svg';
11
+
12
+ targetPage: string[] = ['all'];
13
+
14
+ targetDevice: string = 'all';
15
+
16
+ defaultComProps = {
17
+ xObjectApiKey: 'opportunity',
18
+ id: '1627009883848800',
19
+ };
20
+
21
+ functions = [
22
+ {
23
+ apiKey: 'refreshData',
24
+ label: 'Refresh health data',
25
+ helpTextKey: 'Re-request the health assessment API',
26
+ },
27
+ ];
28
+
29
+ propsSchema = [
30
+ {
31
+ type: 'panelInput',
32
+ name: 'xObjectApiKey',
33
+ label: 'Entity key (xObjectApiKey)',
34
+ placeholder: 'e.g. opportunity',
35
+ },
36
+ {
37
+ type: 'panelInput',
38
+ name: 'id',
39
+ label: 'Business data ID (data_id)',
40
+ placeholder: 'Business record primary key',
41
+ },
42
+ ];
43
+ }
44
+
45
+ export default ShowHealthResultModel;