neo-cmp-cli 1.13.15 → 1.13.17

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 (180) 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/neoLogin.js +1 -1
  5. package/dist/package.json.js +1 -1
  6. package/docs//351/200/232/347/224/250/344/273/243/347/220/206/346/216/245/345/217/243/forward.zip +0 -0
  7. package/docs//351/200/232/347/224/250/344/273/243/347/220/206/346/216/245/345/217/243//350/207/252/345/256/232/344/271/211API:/351/200/232/347/224/250/344/273/243/347/220/206/346/216/245/345/217/243/344/275/277/347/224/250/350/257/264/346/230/216.md +13 -0
  8. package/package.json +1 -1
  9. package/template/antd-custom-cmp-template/package.json +1 -1
  10. package/template/asset-manage-template/package.json +2 -2
  11. package/template/echarts-custom-cmp-template/package.json +1 -1
  12. package/template/empty-custom-cmp-template/package.json +2 -2
  13. package/template/map-custom-cmp-template/package.json +1 -1
  14. package/template/neo-bi-cmps/neo.config.js +7 -1
  15. package/template/neo-bi-cmps/package.json +8 -7
  16. package/template/neo-bi-cmps/public/403.html +77 -0
  17. package/template/neo-bi-cmps/src/assets/icon/barChart.svg +1 -0
  18. package/template/neo-bi-cmps/src/assets/icon/card.svg +1 -0
  19. package/template/neo-bi-cmps/src/assets/icon/filter.svg +1 -0
  20. package/template/neo-bi-cmps/src/assets/icon/funnel.svg +1 -0
  21. package/template/neo-bi-cmps/src/assets/icon/tab.svg +1 -0
  22. package/template/neo-bi-cmps/src/components/filterBar__c/README.md +3 -14
  23. package/template/neo-bi-cmps/src/components/filterBar__c/common.scss +29 -0
  24. package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +668 -146
  25. package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +26 -48
  26. package/template/neo-bi-cmps/src/components/filterBar__c/style.scss +46 -139
  27. package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.tsx +11 -10
  28. package/template/neo-bi-cmps/src/components/targetNumber__c/index.tsx +9 -16
  29. package/template/neo-bi-cmps/src/utils/common.ts +231 -0
  30. package/template/neo-bi-cmps/src/utils/filter2chartFilter.ts +268 -0
  31. package/template/neo-bi-cmps/src/utils/filterBar.ts +140 -0
  32. package/template/neo-bi-cmps/src/utils/pipelineFunnel.ts +341 -0
  33. package/template/neo-bi-cmps/src/utils/queryByCustomSQL.ts +117 -0
  34. package/template/neo-bi-cmps/src/utils/requestDebounce.ts +22 -0
  35. package/template/neo-bi-cmps/src/utils/simpleTable.tsx +344 -0
  36. package/template/neo-bi-cmps/src/utils/stageSwitch.ts +15 -0
  37. package/template/neo-bi-cmps/src/utils/stageTimeChart.ts +90 -0
  38. package/template/neo-bi-cmps/src/utils/targetNumber.ts +12 -0
  39. package/template/neo-custom-cmp-template/package.json +2 -2
  40. package/template/neo-h5-cmps/package.json +2 -2
  41. package/template/neo-order-cmps/package.json +2 -2
  42. package/template/neo-pipeline-cmps/.prettierrc.js +12 -0
  43. package/template/neo-pipeline-cmps/@types/neo-ui-common.d.ts +36 -0
  44. package/template/neo-pipeline-cmps/README.md +99 -0
  45. package/template/neo-pipeline-cmps/commitlint.config.js +59 -0
  46. package/template/neo-pipeline-cmps/neo.config.js +124 -0
  47. package/template/neo-pipeline-cmps/package.json +66 -0
  48. package/template/neo-pipeline-cmps/public/403.html +77 -0
  49. package/template/neo-pipeline-cmps/public/css/base.css +283 -0
  50. package/template/neo-pipeline-cmps/public/demo.html +2453 -0
  51. package/template/neo-pipeline-cmps/public/scripts/app/bluebird.js +6679 -0
  52. package/template/neo-pipeline-cmps/public/template.html +13 -0
  53. package/template/neo-pipeline-cmps/src/assets/css/common.scss +127 -0
  54. package/template/neo-pipeline-cmps/src/assets/css/mixin.scss +47 -0
  55. package/template/neo-pipeline-cmps/src/assets/icon/barChart.svg +1 -0
  56. package/template/neo-pipeline-cmps/src/assets/icon/card.svg +1 -0
  57. package/template/neo-pipeline-cmps/src/assets/icon/filter.svg +1 -0
  58. package/template/neo-pipeline-cmps/src/assets/icon/funnel.svg +1 -0
  59. package/template/neo-pipeline-cmps/src/assets/icon/tab.svg +1 -0
  60. package/template/neo-pipeline-cmps/src/assets/img/AIBtn.gif +0 -0
  61. package/template/neo-pipeline-cmps/src/assets/img/NeoCRM.jpg +0 -0
  62. package/template/neo-pipeline-cmps/src/assets/img/aiLogo.png +0 -0
  63. package/template/neo-pipeline-cmps/src/assets/img/card-list.svg +1 -0
  64. package/template/neo-pipeline-cmps/src/assets/img/contact-form.svg +1 -0
  65. package/template/neo-pipeline-cmps/src/assets/img/custom-form.svg +1 -0
  66. package/template/neo-pipeline-cmps/src/assets/img/custom-widget.svg +1 -0
  67. package/template/neo-pipeline-cmps/src/assets/img/data-list.svg +1 -0
  68. package/template/neo-pipeline-cmps/src/assets/img/detail.svg +1 -0
  69. package/template/neo-pipeline-cmps/src/assets/img/favicon.png +0 -0
  70. package/template/neo-pipeline-cmps/src/assets/img/map.svg +1 -0
  71. package/template/neo-pipeline-cmps/src/assets/img/search.svg +1 -0
  72. package/template/neo-pipeline-cmps/src/assets/img/table.svg +1 -0
  73. package/template/neo-pipeline-cmps/src/components/filterBar__c/README.md +24 -0
  74. package/template/neo-pipeline-cmps/src/components/filterBar__c/common.scss +29 -0
  75. package/template/neo-pipeline-cmps/src/components/filterBar__c/index.tsx +730 -0
  76. package/template/neo-pipeline-cmps/src/components/filterBar__c/model.ts +50 -0
  77. package/template/neo-pipeline-cmps/src/components/filterBar__c/style.scss +119 -0
  78. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/index.tsx +415 -0
  79. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +79 -0
  80. package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/style.scss +83 -0
  81. package/template/neo-pipeline-cmps/src/components/showHealthResult__c/index.tsx +463 -0
  82. package/template/neo-pipeline-cmps/src/components/showHealthResult__c/model.ts +45 -0
  83. package/template/neo-pipeline-cmps/src/components/showHealthResult__c/style.scss +137 -0
  84. package/template/neo-pipeline-cmps/src/components/simpleTable__c/README.md +90 -0
  85. package/template/neo-pipeline-cmps/src/components/simpleTable__c/common.scss +195 -0
  86. package/template/neo-pipeline-cmps/src/components/simpleTable__c/index.tsx +665 -0
  87. package/template/neo-pipeline-cmps/src/components/simpleTable__c/model.ts +124 -0
  88. package/template/neo-pipeline-cmps/src/components/simpleTable__c/style.scss +193 -0
  89. package/template/neo-pipeline-cmps/src/components/stageSwitch__c/index.tsx +511 -0
  90. package/template/neo-pipeline-cmps/src/components/stageSwitch__c/model.ts +70 -0
  91. package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageSwitch__c/style.scss +4 -2
  92. package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/index.tsx +455 -0
  93. package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/model.ts +103 -0
  94. package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageTimeChart__c/style.scss +3 -2
  95. package/template/neo-pipeline-cmps/src/utils/common.ts +229 -0
  96. package/template/neo-pipeline-cmps/src/utils/filter2chartFilter.ts +268 -0
  97. package/template/neo-pipeline-cmps/src/utils/filterBar.ts +140 -0
  98. package/template/neo-pipeline-cmps/src/utils/pipelineFunnel.ts +343 -0
  99. package/template/neo-pipeline-cmps/src/utils/queryByCustomSQL.ts +117 -0
  100. package/template/neo-pipeline-cmps/src/utils/requestDebounce.ts +22 -0
  101. package/template/neo-pipeline-cmps/src/utils/simpleTable.tsx +344 -0
  102. package/template/neo-pipeline-cmps/src/utils/stageSwitch.ts +15 -0
  103. package/template/neo-pipeline-cmps/src/utils/stageTimeChart.ts +90 -0
  104. package/template/neo-pipeline-cmps/src/utils/targetNumber.ts +12 -0
  105. package/template/neo-pipeline-cmps/tsconfig.json +40 -0
  106. package/template/neo-web-entity-grid/package.json +2 -2
  107. package/template/neo-web-form/package.json +2 -2
  108. package/template/react-custom-cmp-template/package.json +1 -1
  109. package/template/react-ts-custom-cmp-template/package.json +1 -1
  110. package/template/vue2-custom-cmp-template/package.json +1 -1
  111. package/template/neo-bi-cmps/.npmrc copy +0 -1
  112. package/template/neo-bi-cmps/docs/gartner-pipeline-apis.md +0 -251
  113. package/template/neo-bi-cmps/docs/gartner-pipeline-prd.md +0 -389
  114. package/template/neo-bi-cmps/docs/neo-backend-dev/SKILL.md +0 -188
  115. package/template/neo-bi-cmps/docs/neo-backend-dev/references/01-Trigger/345/274/200/345/217/221.md +0 -183
  116. 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 +0 -196
  117. 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 +0 -346
  118. 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 +0 -188
  119. package/template/neo-bi-cmps/docs/neo-backend-dev/references/05-/351/241/265/351/235/242/345/274/200/345/217/221.md +0 -293
  120. 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 +0 -175
  121. 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 +0 -313
  122. package/template/neo-bi-cmps/docs/neo-backend-dev/references/auth-config.md +0 -77
  123. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/deploy_server_script.py +0 -118
  124. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/download_server_script.py +0 -74
  125. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entity_desc.py +0 -69
  126. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entitylist.py +0 -87
  127. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/query_crm.py +0 -65
  128. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/uninstall_server_script.py +0 -48
  129. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/update_model_jar.py +0 -49
  130. package/template/neo-bi-cmps/docs/neo-frontend-dev/SKILL.md +0 -138
  131. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/auth-config.md +0 -77
  132. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/component-dev.md +0 -205
  133. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/entityTable-example.md +0 -167
  134. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/templates.md +0 -38
  135. package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entity_desc.py +0 -69
  136. package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entitylist.py +0 -87
  137. package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/query_crm.py +0 -65
  138. 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 +0 -10
  139. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/README.md +0 -52
  140. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/index.tsx +0 -183
  141. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/model.ts +0 -90
  142. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/style.scss +0 -218
  143. package/template/neo-bi-cmps/src/components/forecastChart__c/README.md +0 -31
  144. package/template/neo-bi-cmps/src/components/forecastChart__c/index.tsx +0 -158
  145. package/template/neo-bi-cmps/src/components/forecastChart__c/model.ts +0 -40
  146. package/template/neo-bi-cmps/src/components/forecastChart__c/style.scss +0 -154
  147. package/template/neo-bi-cmps/src/components/forecastGrid__c/README.md +0 -36
  148. package/template/neo-bi-cmps/src/components/forecastGrid__c/index.tsx +0 -86
  149. package/template/neo-bi-cmps/src/components/forecastGrid__c/model.ts +0 -62
  150. package/template/neo-bi-cmps/src/components/forecastGrid__c/style.scss +0 -48
  151. package/template/neo-bi-cmps/src/components/gapCloser__c/README.md +0 -24
  152. package/template/neo-bi-cmps/src/components/gapCloser__c/index.tsx +0 -100
  153. package/template/neo-bi-cmps/src/components/gapCloser__c/model.ts +0 -46
  154. package/template/neo-bi-cmps/src/components/gapCloser__c/style.scss +0 -60
  155. package/template/neo-bi-cmps/src/components/kpiCards__c/README.md +0 -35
  156. package/template/neo-bi-cmps/src/components/kpiCards__c/index.tsx +0 -70
  157. package/template/neo-bi-cmps/src/components/kpiCards__c/model.ts +0 -50
  158. package/template/neo-bi-cmps/src/components/kpiCards__c/style.scss +0 -33
  159. package/template/neo-bi-cmps/src/components/oppList__c/README.md +0 -52
  160. package/template/neo-bi-cmps/src/components/oppList__c/index.tsx +0 -285
  161. package/template/neo-bi-cmps/src/components/oppList__c/model.ts +0 -86
  162. package/template/neo-bi-cmps/src/components/oppList__c/style.scss +0 -133
  163. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/index.tsx +0 -130
  164. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/model.ts +0 -66
  165. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/style.scss +0 -133
  166. package/template/neo-bi-cmps/src/components/stageSwitch__c/index.tsx +0 -118
  167. package/template/neo-bi-cmps/src/components/stageSwitch__c/model.ts +0 -92
  168. package/template/neo-bi-cmps/src/components/stageTimeChart__c/index.tsx +0 -126
  169. package/template/neo-bi-cmps/src/components/stageTimeChart__c/model.ts +0 -57
  170. package/template/neo-bi-cmps/src/components/tabSwitch__c/README.md +0 -37
  171. package/template/neo-bi-cmps/src/components/tabSwitch__c/index.tsx +0 -80
  172. package/template/neo-bi-cmps/src/components/tabSwitch__c/model.ts +0 -45
  173. package/template/neo-bi-cmps/src/components/tabSwitch__c/style.scss +0 -37
  174. package/template/neo-bi-cmps/src/utils/axiosFetcher.ts +0 -37
  175. package/template/neo-bi-cmps/src/utils/queryObjectData.ts +0 -76
  176. package/template/neo-bi-cmps/src/utils/xobjects.ts +0 -162
  177. /package/template/neo-bi-cmps/{docs/prototype-pipeline-forecasting.html → public/demo.html} +0 -0
  178. /package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/pipelineFunnel__c/README.md +0 -0
  179. /package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageSwitch__c/README.md +0 -0
  180. /package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageTimeChart__c/README.md +0 -0
@@ -0,0 +1,665 @@
1
+ /**
2
+ * @file XObject 数据表格组件(简化版)
3
+ * @description 基于 Neo 平台的 XObject 实体对象数据表格组件,仅支持数据展示
4
+ */
5
+ import * as React from 'react';
6
+ import { Table, Spin, Empty, Popover } from 'antd';
7
+ // @ts-ignore
8
+ import { xObject } from 'neo-open-api'; // Neo Open API
9
+ // @ts-ignore
10
+ import isEqual from 'lodash/isEqual';
11
+
12
+ // Neo Open API// 引入 neo-ui-common / BaseCmp
13
+ // @ts-ignore
14
+ import { BaseCmp } from 'neo-ui-common';
15
+
16
+ // 引入 neo-ui-common / NeoEvent
17
+ // @ts-ignore
18
+ import { NeoEvent } from 'neo-ui-common';
19
+
20
+ import queryByCustomSQL from '../../utils/queryByCustomSQL';
21
+ import {
22
+ closeRangeFromFilter,
23
+ entityTypeIdForWhere,
24
+ normalizeOwnerIdsForWhere,
25
+ getDefaultFilterByProps,
26
+ } from '../../utils/common';
27
+ import {
28
+ buildHistoryMap,
29
+ closeDateChangeTooltipTitle,
30
+ closeDateTrend,
31
+ formatActivityDateTime,
32
+ formatCloseDate,
33
+ formatMoneyCell,
34
+ formatMoneySummaryAmount,
35
+ formatPercentCell,
36
+ isMoneyField,
37
+ moneyChangeTooltipTitle,
38
+ moneyTrend,
39
+ renderOppHealthAssessmentLevel,
40
+ renderTrendCell,
41
+ renderWinRateHoverCard,
42
+ sumMoneyRows,
43
+ type FieldInfo,
44
+ type HistoryOppSnap,
45
+ } from '../../utils/simpleTable';
46
+
47
+ import ShowHealthResult from '../showHealthResult__c';
48
+
49
+ import './common.scss';
50
+ import './style.scss';
51
+
52
+ export type { HistoryOppSnap } from '../../utils/simpleTable';
53
+
54
+ /** 商机详情页 hash 路由(与平台 neoweb 一致) */
55
+ function buildOpportunityDetailUrl(id: string, objectApiKey: string): string {
56
+ const q = new URLSearchParams({
57
+ objectApiKey,
58
+ recordId: id,
59
+ });
60
+ return `/bff/neoweb#/entityDetail/opportunity/${id}?${q.toString()}`;
61
+ }
62
+
63
+ /** 不在表格中展示的字段(仍可通过 xObjectDataApi.fields 拉取,用于 hover 卡片 / tooltip) */
64
+ const HIDDEN_TABLE_COLUMN_KEYS = new Set([
65
+ 'customItem244__c',
66
+ 'customItem241__c',
67
+ 'customItem242__c',
68
+ 'customItem246__c',
69
+ 'customItem247__c',
70
+ 'customItem245__c',
71
+ // 'objectId', // 跳转到详情页需要
72
+ // 'businessTypeId' // 跳转到详情页需要
73
+ ]);
74
+
75
+ /** 历史商机 SQL 与 stageSwitch 一致;字段顺序需与 query fields 一致(数组行按列下标解析) */
76
+ const HISTORY_OPPORTUNITY_BI_KEY = 'biCustomModel_397169_20260401104916618';
77
+ const HISTORY_QUERY_FIELDS = [
78
+ // 'id', // 历史版本ID
79
+ 'opportunity_1__id', // 商机ID
80
+ 'opportunity_1_closeDate',
81
+ 'opportunity_1_opportunityName',
82
+ 'opportunity_1_saleStageId',
83
+ 'opportunity_1_money',
84
+ ] as const;
85
+
86
+ const defaultXObjectDataApi = {
87
+ xObjectApiKey: 'opportunity',
88
+ fields: [
89
+ 'opportunityName', // OpportunityStageName 商机名称
90
+ 'money', // Amount
91
+ 'closeDate', // Close Date
92
+ 'customItem248__c', // Stage
93
+ // 'oppHealthAssessmentScore',
94
+ 'recentActivityRecordTime',
95
+ 'oppHealthAssessmentLevel', // 1.AI Score; hover卡片:调用商机健康度接口
96
+ 'customItem239__c', // 2.AI Win Rate: hover卡片展示的3个信息,对应商机上的3个字段(baseline、positiveFactors、negativeFactors)
97
+ 'customItem244__c', // baseline
98
+ 'customItem241__c', // positiveFactors
99
+ 'customItem242__c', // negativeFactors
100
+ 'customItem246__c', // AmoutChanged
101
+ 'customItem247__c', // ClosedDateChanged
102
+ 'customItem245__c', // StageChanged
103
+ // 'objectId', // 跳转到详情页需要
104
+ // 'businessTypeId' // 跳转到详情页需要
105
+ ],
106
+ page: 1,
107
+ pageSize: 50,
108
+ };
109
+ /**
110
+ * 组件属性接口
111
+ */
112
+ interface SimpleTableProps {
113
+ /** XObject 实体对象的 API Key,用于标识要操作的数据对象 */
114
+ xObjectDataApi: {
115
+ xObjectApiKey: string;
116
+ fields: string[];
117
+ fieldDescList?: any[];
118
+ autoFetchData?: boolean;
119
+ };
120
+ /** Neo 平台传递的数据,包含系统信息等 */
121
+ data?: any;
122
+ entityData?: any; // Neo 平台自动获取的实体数据
123
+ /** 是否为自定义实体对象 */
124
+ custom?: boolean;
125
+ /** 组件类名 */
126
+ className?: string;
127
+ /** 表格标题,优先使用此配置 */
128
+ title?: string;
129
+ /** 标题下方说明文案 */
130
+ description?: string;
131
+ /** 当前激活的销售阶段 */
132
+ activeStage?: string;
133
+ }
134
+
135
+ /**
136
+ * 组件状态接口
137
+ */
138
+ interface SimpleTableState {
139
+ /** 表格标题 */
140
+ title?: string;
141
+ fieldList: FieldInfo[];
142
+ /** 表格数据源 */
143
+ dataSource: any[];
144
+ /** 加载状态 */
145
+ loading: boolean;
146
+ /** 错误信息 */
147
+ error: string | null;
148
+ activeStage: string; // 当前激活的销售阶段
149
+ /**
150
+ * 与 FilterBar 事件 payload 一致(closeDateCustomRange / opportunityOwner / businessType 等);
151
+ * 默认值为 defaultFilter,getFilterWhere 仅从中取值
152
+ */
153
+ filter: any;
154
+ }
155
+
156
+ /**
157
+ * XObject 数据表格组件(简化版)
158
+ * 仅支持数据展示,不支持增删改查操作
159
+ */
160
+ export default class SimpleTable extends BaseCmp<
161
+ SimpleTableProps,
162
+ SimpleTableState
163
+ > {
164
+ constructor(props: SimpleTableProps) {
165
+ super(props);
166
+
167
+ // 初始化组件状态
168
+ this.state = {
169
+ fieldList: [],
170
+ dataSource: [],
171
+ loading: false,
172
+ error: null,
173
+ activeStage: props.activeStage || 'Prospecting',
174
+ filter: getDefaultFilterByProps(props),
175
+ };
176
+
177
+ this.loadData = this.loadData.bind(this);
178
+ this.loadFieldList = this.loadFieldList.bind(this);
179
+ this.getCurFieldList = this.getCurFieldList.bind(this);
180
+ this.getMoneyFieldApiKey = this.getMoneyFieldApiKey.bind(this);
181
+ this.generateColumns = this.generateColumns.bind(this);
182
+ this.getFilterWhere = this.getFilterWhere.bind(this);
183
+ this.setFilter = this.setFilter.bind(this);
184
+ this.updateActiveStage = this.updateActiveStage.bind(this);
185
+ }
186
+
187
+ componentDidMount() {
188
+ // const { xObjectDataApi } = this.props;
189
+ // const { xObjectApiKey } = xObjectDataApi || {};
190
+ const { xObjectApiKey } = defaultXObjectDataApi;
191
+
192
+ if (xObjectApiKey) {
193
+ // 初始化字段列表、加载数据
194
+ this.loadFieldList();
195
+ this.loadData();
196
+ }
197
+
198
+ /*
199
+ // 广播事件:更新 FilterBar filter
200
+ NeoEvent.listen('updateFilterData', (filterData: any) => {
201
+ console.log('SimpleTable 监听到了一个广播事件 updateFilterData: ', filterData);
202
+ this.setFilter(filterData);
203
+ });
204
+
205
+ // 广播事件:更新当前激活的销售阶段
206
+ NeoEvent.listen('updateActiveStage', (activeStage: string) => {
207
+ console.log('SimpleTable 监听到了一个广播事件 updateActiveStage: ', activeStage);
208
+ this.updateActiveStage(activeStage);
209
+ });
210
+ */
211
+ }
212
+
213
+ /**
214
+ * 加载字段列表
215
+ * 从 Neo 平台获取 XObject 的字段描述信息
216
+ */
217
+ async loadFieldList() {
218
+ const xObjectDataApi: any = defaultXObjectDataApi;
219
+
220
+ // 方式一:直接从 props.xObjectDetailApi 中获取字段描述
221
+ if (xObjectDataApi && xObjectDataApi.fieldDescList) {
222
+ this.setState({ fieldList: xObjectDataApi.fieldDescList });
223
+ } else {
224
+ // 方式二:自行通过 OpenAPI SDK 获取字段描述
225
+ if (!xObjectDataApi.xObjectApiKey) return;
226
+ try {
227
+ const resultData = await xObject.getDesc(xObjectDataApi.xObjectApiKey);
228
+ if (resultData && resultData.status) {
229
+ const result = resultData.data || {};
230
+ const fieldList = result.fields || [];
231
+ this.setState({ fieldList, title: result.label });
232
+ }
233
+ } catch (error) {
234
+ console.error('获取字段列表失败:', error);
235
+ }
236
+ }
237
+ }
238
+
239
+ /**
240
+ * 当前表格可见字段列表(与列配置一致)
241
+ */
242
+ getCurFieldList(): FieldInfo[] {
243
+ // const { xObjectDataApi } = this.props;
244
+ const xObjectDataApi: any = defaultXObjectDataApi;
245
+ const { fieldList } = this.state;
246
+ const { fields, fieldDescList } = xObjectDataApi || {};
247
+ let curFieldList =
248
+ fieldDescList && fieldDescList.length > 0 ? fieldDescList : fieldList;
249
+
250
+ if (fields && fields.length > 0) {
251
+ curFieldList = curFieldList.filter((field: FieldInfo) =>
252
+ fields.includes(field.apiKey),
253
+ );
254
+ }
255
+ return curFieldList;
256
+ }
257
+
258
+ /**
259
+ * 汇总用金额字段 apiKey(与金额列一致,取第一个货币字段)
260
+ */
261
+ getMoneyFieldApiKey(): string | null {
262
+ const moneyField = this.getCurFieldList().find((f) => isMoneyField(f));
263
+ return moneyField?.apiKey ?? null;
264
+ }
265
+
266
+ /**
267
+ * 生成表格列配置
268
+ * @returns 表格列配置数组
269
+ */
270
+ generateColumns() {
271
+ const xObjectDataApi: any = defaultXObjectDataApi;
272
+
273
+ const curFieldList = this.getCurFieldList().filter(
274
+ (field) => !HIDDEN_TABLE_COLUMN_KEYS.has(field.apiKey),
275
+ );
276
+
277
+ // 根据字段列表生成基础列配置
278
+ const columns: any[] = curFieldList.map((field) => {
279
+ const col: any = {
280
+ title: field.label,
281
+ dataIndex: field.apiKey,
282
+ key: field.apiKey,
283
+ ellipsis: true,
284
+ };
285
+ if (
286
+ field.apiKey === 'oppHealthAssessmentLevel' ||
287
+ field.apiKey === 'customItem239__c'
288
+ ) {
289
+ col.title = (
290
+ <span className="simpleTable-th-title-with-ai-icon">
291
+ <span className="simpleTable-th-ai-icon" />
292
+ <span>{field.label}</span>
293
+ </span>
294
+ );
295
+ }
296
+ if (field.apiKey === 'closeDate') {
297
+ col.render = (text: unknown, record: any) => {
298
+ const display = formatCloseDate(text);
299
+ const trend = closeDateTrend(text, record?.historyData);
300
+ if (trend !== 'up' && trend !== 'down') {
301
+ return display;
302
+ }
303
+ return renderTrendCell({
304
+ displayText: display,
305
+ trend,
306
+ tooltip: closeDateChangeTooltipTitle(record, text),
307
+ });
308
+ };
309
+ } else if (field.apiKey === 'opportunityName') {
310
+ col.render = (text: unknown, record: any) => {
311
+ const display = text == null || text === '' ? '—' : String(text);
312
+ const id =
313
+ record?.id != null && record.id !== '' ? String(record.id) : '';
314
+ const objectApiKey = xObjectDataApi?.xObjectApiKey;
315
+ if (!id || !objectApiKey) {
316
+ return display;
317
+ }
318
+ const url = buildOpportunityDetailUrl(id, objectApiKey);
319
+ return (
320
+ <a
321
+ className="simpleTable-opp-name-link"
322
+ href={url}
323
+ target="_blank"
324
+ /*
325
+ onClick={(e) => {
326
+ e.preventDefault();
327
+ window.location.assign(url);
328
+ }}
329
+ */
330
+ >
331
+ {display}
332
+ </a>
333
+ );
334
+ };
335
+ } else if (field.apiKey === 'recentActivityRecordTime') {
336
+ col.render = (text: unknown) => formatActivityDateTime(text);
337
+ } else if (field.apiKey === 'oppHealthAssessmentLevel') {
338
+ col.render = (text: unknown, record: any) => {
339
+ const cell = renderOppHealthAssessmentLevel(text);
340
+ const id =
341
+ record?.id != null && record.id !== '' ? String(record.id) : '';
342
+ const xObjectApiKey = xObjectDataApi?.xObjectApiKey;
343
+ if (!id || !xObjectApiKey) {
344
+ return cell;
345
+ }
346
+ return (
347
+ <Popover
348
+ content={
349
+ <div className="simpleTable-health-popover-body">
350
+ <ShowHealthResult id={id} xObjectApiKey={xObjectApiKey} />
351
+ </div>
352
+ }
353
+ trigger="hover"
354
+ placement="topLeft"
355
+ overlayClassName="simpleTable-health-popover-overlay"
356
+ mouseEnterDelay={0.08}
357
+ >
358
+ <span className="simpleTable-health-popover-trigger">{cell}</span>
359
+ </Popover>
360
+ );
361
+ };
362
+ } else if (field.apiKey === 'customItem239__c') {
363
+ col.align = 'center';
364
+ col.render = (_text: unknown, record: any) => (
365
+ <Popover
366
+ content={renderWinRateHoverCard(record)}
367
+ trigger="hover"
368
+ placement="topLeft"
369
+ overlayClassName="simpleTable-winrate-popover"
370
+ mouseEnterDelay={0.08}
371
+ >
372
+ <span className="simpleTable-winrate-trigger">
373
+ {formatPercentCell(record?.customItem239__c)}
374
+ </span>
375
+ </Popover>
376
+ );
377
+ } else if (isMoneyField(field)) {
378
+ // col.align = 'right';
379
+ col.render = (text: unknown, record: any) => {
380
+ const display = formatMoneyCell(text);
381
+ const trend = moneyTrend(text, record?.historyData);
382
+ if (trend !== 'up' && trend !== 'down') {
383
+ return display;
384
+ }
385
+ return renderTrendCell({
386
+ displayText: display,
387
+ trend,
388
+ tooltip: moneyChangeTooltipTitle(record, text),
389
+ });
390
+ };
391
+ }
392
+ return col;
393
+ });
394
+
395
+ return columns;
396
+ }
397
+
398
+ /**
399
+ * 将 FilterBar 结构 filter 转为 xObject.√ 的 where 字符串数组(与 stageSwitch 一致,SDK 按顺序 and 拼接)
400
+ */
401
+ getFilterWhere(): string[] {
402
+ const raw = this.state.filter;
403
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
404
+ return [];
405
+ }
406
+ const curFilter = raw as Record<string, unknown>;
407
+ const where: string[] = [];
408
+
409
+ const range = closeRangeFromFilter(curFilter.closeDateCustomRange);
410
+ if (range) {
411
+ where.push(`closeDate >= ${range.start} and closeDate <= ${range.end}`);
412
+ }
413
+
414
+ const ownerIds = normalizeOwnerIdsForWhere(curFilter.opportunityOwner);
415
+ if (ownerIds.length === 1) {
416
+ where.push(`ownerId = ${ownerIds[0]}`);
417
+ } else if (ownerIds.length > 1) {
418
+ where.push(`ownerId in (${ownerIds.join(', ')})`);
419
+ }
420
+
421
+ const entityTypeId = entityTypeIdForWhere(curFilter.businessType);
422
+ if (entityTypeId != null) {
423
+ where.push(`entityType = ${entityTypeId}`);
424
+ }
425
+
426
+ /*
427
+ // 如果 activeStage 存在,则添加 stage 过滤条件
428
+ if (this.state.activeStage) {
429
+ // customItem248__c 是销售阶段字段
430
+ where.push(`customItem248__c = '${this.state.activeStage}'`);
431
+ }
432
+ */
433
+
434
+ return where;
435
+ }
436
+
437
+ /** 与 stageSwitch一致:历史商机自定义 SQL 的 where(opportunity_1_* 字段) */
438
+ getHistoryOpportunityQueryFilter(): string[] {
439
+ const raw = this.state.filter;
440
+ const curFilter: Record<string, unknown> =
441
+ raw && typeof raw === 'object' && !Array.isArray(raw)
442
+ ? Object.keys(raw as object).length > 0
443
+ ? (raw as Record<string, unknown>)
444
+ : defaultFilter
445
+ : defaultFilter;
446
+
447
+ const where: string[] = [];
448
+
449
+ const range = closeRangeFromFilter(curFilter.closeDateCustomRange);
450
+ if (range) {
451
+ where.push(
452
+ `opportunity_1_closeDate >= ${range.start} and opportunity_1_closeDate <= ${range.end}`,
453
+ );
454
+ }
455
+
456
+ const ownerIds = normalizeOwnerIdsForWhere(curFilter.opportunityOwner);
457
+ if (ownerIds.length === 1) {
458
+ where.push(`opportunity_1_ownerId = ${ownerIds[0]}`);
459
+ } else if (ownerIds.length > 1) {
460
+ where.push(`opportunity_1_ownerId in (${ownerIds.join(', ')})`);
461
+ }
462
+
463
+ const entityTypeId = entityTypeIdForWhere(curFilter.businessType);
464
+ if (entityTypeId != null) {
465
+ where.push(`opportunity_1_entityType = ${entityTypeId}`);
466
+ }
467
+
468
+ if (curFilter.changesSinceCustomTime) {
469
+ where.push(`version = ${curFilter.changesSinceCustomTime}`);
470
+ }
471
+
472
+ return where;
473
+ }
474
+
475
+ /** 查询期间初历史商机列表(与 stageSwitch.fetchHistoryOpportunityList 同源,字段按需求扩展) */
476
+ async fetchHistoryOpportunityList(): Promise<unknown[]> {
477
+ const result = await queryByCustomSQL({
478
+ xObjectApiKey: HISTORY_OPPORTUNITY_BI_KEY,
479
+ fields: [...HISTORY_QUERY_FIELDS],
480
+ page: 1,
481
+ pageSize: 1000,
482
+ where: this.getHistoryOpportunityQueryFilter(),
483
+ });
484
+
485
+ return result?.status ? result.data ?? [] : [];
486
+ }
487
+
488
+ /**
489
+ * 加载表格数据
490
+ */
491
+ @NeoEvent.function
492
+ async loadData() {
493
+ // const api = this.props.xObjectDataApi || {};
494
+ const api: any = defaultXObjectDataApi;
495
+ const { xObjectApiKey } = api;
496
+ if (!xObjectApiKey) return;
497
+
498
+ this.setState({ loading: true, error: null });
499
+
500
+ try {
501
+ const rawWhere = (api as { where?: unknown }).where;
502
+ const baseWhere: string[] = Array.isArray(rawWhere)
503
+ ? rawWhere.filter((w): w is string => typeof w === 'string')
504
+ : typeof rawWhere === 'string' && rawWhere
505
+ ? [rawWhere]
506
+ : [];
507
+ const filterWhere =
508
+ xObjectApiKey === 'opportunity' ? this.getFilterWhere() : [];
509
+ const [result, historyRows] = await Promise.all([
510
+ xObject.query({
511
+ ...api,
512
+ where: [...baseWhere, ...filterWhere],
513
+ }),
514
+ this.fetchHistoryOpportunityList(),
515
+ ]);
516
+
517
+ if (result && result.status) {
518
+ let records = result.data || [];
519
+
520
+ console.log('[SimpleTable__c] records:', records, historyRows);
521
+
522
+ // 根据阶段过滤数据
523
+ if (this.state.activeStage) {
524
+ records = records.filter((record: any) => {
525
+ return record.customItem248__c === this.state.activeStage;
526
+ });
527
+ }
528
+
529
+ const historyMap =
530
+ xObjectApiKey === 'opportunity'
531
+ ? buildHistoryMap(historyRows)
532
+ : new Map<string, HistoryOppSnap>();
533
+
534
+ const merged = records.map((r: any) => {
535
+ const id = r?.id != null ? String(r.id) : '';
536
+ const historyData = id ? historyMap.get(id) ?? null : null;
537
+ return { ...r, historyData };
538
+ });
539
+
540
+ this.setState({
541
+ dataSource: merged,
542
+ loading: false,
543
+ });
544
+ } else {
545
+ this.setState({
546
+ error: result?.msg || '获取数据失败',
547
+ loading: false,
548
+ });
549
+ }
550
+ } catch (error: any) {
551
+ this.setState({
552
+ error: error.message || '获取数据失败',
553
+ loading: false,
554
+ });
555
+ }
556
+ }
557
+
558
+ /**
559
+ * 与 stageSwitch 一致:更新 FilterBar filter 并重新拉取表格数据。
560
+ */
561
+ @NeoEvent.function
562
+ setFilter(filter?: any) {
563
+ if (isEqual(filter, this.state.filter)) {
564
+ return;
565
+ }
566
+ console.log('[SimpleTable__c] setFilter:', filter);
567
+ this.setState({ filter }, () => {
568
+ this.loadData();
569
+ });
570
+ }
571
+
572
+ /**
573
+ * 更新当前激活的销售阶段
574
+ */
575
+ @NeoEvent.function
576
+ updateActiveStage(activeStage?: string) {
577
+ if (isEqual(activeStage, this.state.activeStage)) {
578
+ return;
579
+ }
580
+ console.log('[SimpleTable__c] updateActiveStage:', activeStage);
581
+ this.setState({ activeStage: activeStage || '' }, () => {
582
+ this.loadData();
583
+ });
584
+ }
585
+
586
+ /**
587
+ * 渲染组件
588
+ * @returns 组件 JSX 元素
589
+ */
590
+ render() {
591
+ const { title: stateTitle, dataSource, loading, error } = this.state;
592
+ const { className, title: propsTitle, description } = this.props;
593
+ // const { xObjectApiKey } = this.props.xObjectDataApi || {};
594
+ const { xObjectApiKey } = defaultXObjectDataApi;
595
+ const columns = this.generateColumns();
596
+
597
+ // 优先使用 props.title,其次使用 state.title,最后使用默认值
598
+ const displayTitle = propsTitle || stateTitle || '数据表格';
599
+
600
+ const moneyKey = this.getMoneyFieldApiKey();
601
+ const totalAmount = moneyKey ? sumMoneyRows(dataSource, moneyKey) : 0;
602
+ const oppCount = dataSource.length;
603
+ const oppWord = oppCount === 1 ? 'opportunity' : 'opportunities';
604
+ const summaryText = `Total: ${formatMoneySummaryAmount(
605
+ totalAmount,
606
+ )} · ${oppCount} ${oppWord}`;
607
+
608
+ console.log(
609
+ 'simpleTable__c / dataSource:',
610
+ dataSource,
611
+ this.props,
612
+ this.state,
613
+ );
614
+
615
+ return (
616
+ <div className={`simpleTable__c ${className}`} data-time="2026.4.15 01">
617
+ <div className="table-wrapper">
618
+ <div className="panel-header">
619
+ <div className="panel-header-text">
620
+ <h3>
621
+ {displayTitle} - {this.state.activeStage}
622
+ </h3>
623
+ {description ? (
624
+ <p className="panel-header-description">{description}</p>
625
+ ) : null}
626
+ </div>
627
+ <div className="panel-header-summary" title={summaryText}>
628
+ Total:{' '}
629
+ <span className="summary-amount">
630
+ {formatMoneySummaryAmount(totalAmount)}
631
+ </span>
632
+ {' · '}
633
+ <span className="summary-amount">{oppCount}</span> {oppWord}
634
+ </div>
635
+ </div>
636
+ <div className="table-container">
637
+ <Spin spinning={loading} tip="加载数据中...">
638
+ {error ? (
639
+ <Empty
640
+ image={Empty.PRESENTED_IMAGE_SIMPLE}
641
+ description={
642
+ <div>
643
+ <div style={{ color: '#ff4d4f', marginBottom: 8 }}>
644
+ {error}
645
+ </div>
646
+ </div>
647
+ }
648
+ />
649
+ ) : (
650
+ <Table
651
+ key={`${xObjectApiKey}-${columns.length}`}
652
+ columns={columns}
653
+ dataSource={dataSource}
654
+ rowKey="id"
655
+ pagination={false}
656
+ scroll={{ x: 'max-content' }}
657
+ />
658
+ )}
659
+ </Spin>
660
+ </div>
661
+ </div>
662
+ </div>
663
+ );
664
+ }
665
+ }