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,50 @@
1
+ export class FilterBarModel {
2
+ label: string = '筛选栏';
3
+ description: string = '支持日期范围、负责人、业务类型等多维度筛选';
4
+ iconUrl: string = 'https://custom-widgets.bj.bcebos.com/filter.svg';
5
+ targetPage: string[] = ['all'];
6
+ targetDevice: string = 'all';
7
+
8
+ defaultComProps = {
9
+ closeDateOptions: [
10
+ { value: 201, label: 'This Week' },
11
+ { value: 301, label: 'This Month' },
12
+ { value: 401, label: 'This Quarter' },
13
+ { value: 'custom', label: 'Custom' },
14
+ ],
15
+ /** 与商机业务类型接口返回项的 apiKey 一致时,作为 Business Type 默认选中项 */
16
+ defaultBusiType: 'defaultBusiType_1', // New Business
17
+ };
18
+
19
+ /**
20
+ * 声明当前组件会触发的所有事件(与组件内 @NeoEvent.dispatch 方法名一致)
21
+ */
22
+ events = [
23
+ {
24
+ apiKey: 'onFiltersChange',
25
+ label: '筛选条件变化后',
26
+ helpText:
27
+ '任一筛选项或自定义时间区间变更时触发;事件参数含 closeDate、closeDateCustomRange(非 custom 时为当前相对周期起止 Unix 毫秒时间戳;custom 时为 RangePicker 起止)、opportunityOwner(负责人多选 id 数组)、businessType、businessTypeLabel(业务类型展示名)、changesSince、changesSinceCustomTime(Changes Since 为 Custom 时为所选日期当日 0 点;否则在 Close Date 非 custom 时为周期起点当日 0 点)',
28
+ eventParams:
29
+ '[{"apiKey":"eventParam","children":[{"apiKey":"data","label":"当前筛选数据","type":"Object"}],"label":"事件入参","type":"Object"}]',
30
+ },
31
+ ];
32
+
33
+ functions = [
34
+ {
35
+ apiKey: 'resetFilters',
36
+ label: '重置筛选条件',
37
+ helpTextKey: '重置所有筛选条件为默认值并触发「筛选条件变化后」事件',
38
+ },
39
+ {
40
+ apiKey: 'getFilters',
41
+ label: '获取筛选条件',
42
+ helpTextKey:
43
+ '返回当前筛选快照对象;closeDate 非 custom 时 closeDateCustomRange 为相对周期起止时间戳;custom 时为自定义区间;businessTypeLabel 为业务类型展示名;changesSinceCustomTime 在 Changes Since 为 Custom 时为所选日 0 点,否则在 Close Date 非 custom 时为周期起点 0 点',
44
+ },
45
+ ];
46
+
47
+ propsSchema = [];
48
+ }
49
+
50
+ export default FilterBarModel;
@@ -0,0 +1,119 @@
1
+ .filterBar__c {
2
+ background: #fff;
3
+ border-radius: 8px;
4
+ padding: 16px 20px;
5
+ display: flex;
6
+ flex-wrap: wrap;
7
+ justify-content: flex-start;
8
+ gap: 10px 12px;
9
+ align-items: flex-start;
10
+ align-content: flex-start;
11
+ margin-bottom: 12px;
12
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
13
+
14
+ .filter-block {
15
+ display: flex;
16
+ flex-wrap: nowrap;
17
+ align-items: center;
18
+ gap: 6px 8px;
19
+ flex: 0 1 auto;
20
+ min-width: 0;
21
+ max-width: 100%;
22
+ }
23
+
24
+ .filter-field {
25
+ display: flex;
26
+ flex-wrap: nowrap;
27
+ align-items: center;
28
+ gap: 6px;
29
+ min-width: 0;
30
+
31
+ label {
32
+ font-size: 13px;
33
+ color: #666;
34
+ white-space: nowrap;
35
+ display: inline-flex;
36
+ align-items: center;
37
+ gap: 4px;
38
+ }
39
+
40
+ &.filter-field-range {
41
+ flex: 0 0 auto;
42
+ }
43
+ }
44
+
45
+ .filter-select {
46
+ min-width: 160px;
47
+ max-width: 100%;
48
+ }
49
+
50
+ .filter-select-wide {
51
+ min-width: 220px;
52
+ }
53
+
54
+ .filter-select-spin-wrap {
55
+ min-width: 220px;
56
+ min-height: 32px;
57
+ }
58
+
59
+ .filter-btn {
60
+ padding: 6px 16px;
61
+ border-radius: 6px;
62
+ font-size: 13px;
63
+ cursor: pointer;
64
+ border: 1px solid #ddd;
65
+ background: #fff;
66
+
67
+ &.active {
68
+ background: #6366f1;
69
+ color: #fff;
70
+ border-color: #6366f1;
71
+ }
72
+ }
73
+
74
+ /* Changes Since 问号提示 */
75
+ .help-tip {
76
+ position: relative;
77
+ display: inline-flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ cursor: help;
81
+ color: #999;
82
+ font-size: 12px;
83
+ margin-left: 2px;
84
+ width: 16px;
85
+ height: 16px;
86
+ border-radius: 50%;
87
+ border: 1px solid #ddd;
88
+
89
+ .help-tip-text {
90
+ display: none;
91
+ position: absolute;
92
+ bottom: calc(100% + 6px);
93
+ left: 50%;
94
+ transform: translateX(-50%);
95
+ background: #333;
96
+ color: #fff;
97
+ padding: 6px 10px;
98
+ border-radius: 6px;
99
+ font-size: 11px;
100
+ white-space: nowrap;
101
+ z-index: 100;
102
+ font-weight: 400;
103
+
104
+ &::after {
105
+ content: '';
106
+ position: absolute;
107
+ top: 100%;
108
+ left: 50%;
109
+ transform: translateX(-50%);
110
+ border: 5px solid transparent;
111
+ border-top-color: #333;
112
+ }
113
+ }
114
+
115
+ &:hover .help-tip-text {
116
+ display: block;
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,415 @@
1
+ /**
2
+ * @file Pipeline漏斗图组件
3
+ * @description 展示销售管道的漏斗转化情况,数据来自 NeoBI queryDataTask
4
+ */
5
+ import * as React from 'react';
6
+ import * as echarts from 'echarts';
7
+ import { Spin } from 'antd';
8
+ // @ts-ignore
9
+ import { request } from 'neo-open-api';
10
+ // @ts-ignore
11
+ import { BaseCmp, NeoEvent } from 'neo-ui-common';
12
+ // @ts-ignore
13
+ import isEqual from 'lodash/isEqual';
14
+ import { filter2chartFilter } from '../../utils/filter2chartFilter';
15
+ import {
16
+ buildQueryDataTaskFormBody,
17
+ formatAmountDisplay,
18
+ getDefaultFilterWhereByProps,
19
+ } from '../../utils/common';
20
+ import {
21
+ buildFunnelChartOption,
22
+ filterOutClosedLostRows,
23
+ isTruthyProp,
24
+ parseRowsToStages,
25
+ type FunnelStage,
26
+ } from '../../utils/pipelineFunnel';
27
+
28
+ import './style.scss';
29
+
30
+ const QUERY_DATA_TASK_URL = '/rest/neobi/v2.0/bestpractices/queryDataTask';
31
+
32
+ const FORM_URLENCODED_UTF8 = 'application/x-www-form-urlencoded;charset=UTF-8';
33
+
34
+ interface PipelineFunnelProps {
35
+ title?: string;
36
+ /** 视图 ID,参与 queryDataTask */
37
+ viewId?: string;
38
+ /** 视图类型,对应请求参数 type */
39
+ viewType?: string;
40
+ /**
41
+ * 为 true 时层宽随阶段顺序递减(经典漏斗),层高度按各阶段金额占比;
42
+ * 为 false(默认)时层宽按各阶段真实金额比例绘制 */
43
+ useClassicFunnelShape?: boolean;
44
+ /** Neo 注入,含当前用户 */
45
+ data?: {
46
+ __NeoCurrentUser?: { id?: string | number };
47
+ };
48
+ showAiButton?: boolean;
49
+ onStageClick?: (stageName: string) => void;
50
+ className?: string;
51
+ style?: React.CSSProperties;
52
+ }
53
+
54
+ interface PipelineFunnelState {
55
+ loading: boolean;
56
+ error: string | null;
57
+ stages: FunnelStage[];
58
+ /** 各阶段金额数值总和,用于占比 */
59
+ totalAmountNum: number;
60
+ /** 顶部展示用总金额文案 */
61
+ totalAmountLabel: string;
62
+ /** 请求体中的 filter,可由组件动作 setFilter 更新 */
63
+ filter: any;
64
+ }
65
+
66
+ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
67
+ private chartRef = React.createRef<HTMLDivElement>();
68
+
69
+ private chartInstance: echarts.ECharts | null = null;
70
+
71
+ private resizeObserver: ResizeObserver | null = null;
72
+
73
+ constructor(props: PipelineFunnelProps) {
74
+ super(props);
75
+ this.state = {
76
+ loading: false,
77
+ error: null,
78
+ stages: [],
79
+ totalAmountNum: 0,
80
+ totalAmountLabel: formatAmountDisplay(0),
81
+ filter: getDefaultFilterWhereByProps(props),
82
+ };
83
+
84
+ this.initChart = this.initChart.bind(this);
85
+ this.bindResize = this.bindResize.bind(this);
86
+ this.unbindResize = this.unbindResize.bind(this);
87
+ this.handleWindowResize = this.handleWindowResize.bind(this);
88
+ this.updateChart = this.updateChart.bind(this);
89
+ this.fetchChartData = this.fetchChartData.bind(this);
90
+ this.refreshData = this.refreshData.bind(this);
91
+ this.setFilter = this.setFilter.bind(this);
92
+ }
93
+
94
+ componentDidMount() {
95
+ this.fetchChartData();
96
+ this.initChart();
97
+ this.bindResize();
98
+
99
+ /*
100
+ // 监听一个广播事件
101
+ NeoEvent.listen('updateFilterData', (filterData: any) => {
102
+ console.log('PipelineFunnel 监听到了一个广播事件 updateFilterData: ', filterData);
103
+ this.setFilter(filterData);
104
+ });
105
+ */
106
+ }
107
+
108
+ componentDidUpdate(
109
+ prevProps: PipelineFunnelProps,
110
+ prevState: PipelineFunnelState,
111
+ ) {
112
+ const uid = this.props.data?.__NeoCurrentUser?.id;
113
+ const puid = prevProps.data?.__NeoCurrentUser?.id;
114
+ if (
115
+ this.props.viewId !== prevProps.viewId ||
116
+ this.props.viewType !== prevProps.viewType ||
117
+ uid !== puid
118
+ ) {
119
+ this.fetchChartData();
120
+ }
121
+
122
+ if (
123
+ prevState.stages !== this.state.stages ||
124
+ prevState.loading !== this.state.loading ||
125
+ isTruthyProp(prevProps.useClassicFunnelShape) !==
126
+ isTruthyProp(this.props.useClassicFunnelShape)
127
+ ) {
128
+ this.updateChart();
129
+ }
130
+
131
+ if (!this.chartInstance && this.chartRef.current) {
132
+ this.initChart();
133
+ }
134
+ }
135
+
136
+ componentWillUnmount() {
137
+ this.unbindResize();
138
+ if (this.chartInstance) {
139
+ this.chartInstance.dispose();
140
+ this.chartInstance = null;
141
+ }
142
+ }
143
+
144
+ initChart() {
145
+ if (!this.chartRef.current || this.chartInstance) return;
146
+ this.chartInstance = echarts.init(this.chartRef.current);
147
+ this.updateChart();
148
+ }
149
+
150
+ bindResize() {
151
+ window.addEventListener('resize', this.handleWindowResize);
152
+ if (typeof ResizeObserver === 'undefined') return;
153
+ this.resizeObserver = new ResizeObserver(() => {
154
+ this.chartInstance?.resize();
155
+ if (
156
+ isTruthyProp(this.props.useClassicFunnelShape) &&
157
+ this.state.stages.length > 0
158
+ ) {
159
+ this.updateChart();
160
+ }
161
+ });
162
+ const el = this.chartRef.current;
163
+ if (el) this.resizeObserver.observe(el);
164
+ }
165
+
166
+ unbindResize() {
167
+ window.removeEventListener('resize', this.handleWindowResize);
168
+ this.resizeObserver?.disconnect();
169
+ this.resizeObserver = null;
170
+ }
171
+
172
+ handleWindowResize() {
173
+ this.chartInstance?.resize();
174
+ if (
175
+ isTruthyProp(this.props.useClassicFunnelShape) &&
176
+ this.state.stages.length > 0
177
+ ) {
178
+ this.updateChart();
179
+ }
180
+ }
181
+
182
+ updateChart() {
183
+ if (!this.chartInstance) return;
184
+ const { stages } = this.state;
185
+ const { onStageClick, useClassicFunnelShape } = this.props;
186
+ const classicShape = isTruthyProp(useClassicFunnelShape);
187
+ const el = this.chartRef.current;
188
+ const fallbackH = Math.max(stages.length * 52, 220);
189
+ const chartViewHeightPx = Math.max(el?.clientHeight ?? 0, fallbackH);
190
+
191
+ try {
192
+ if (stages.length === 0) {
193
+ this.chartInstance.clear();
194
+ } else {
195
+ const option = buildFunnelChartOption(stages, classicShape, {
196
+ chartViewHeightPx: classicShape ? chartViewHeightPx : undefined,
197
+ funnelGap: 2,
198
+ });
199
+ this.chartInstance.setOption(option, true);
200
+ this.chartInstance.off('click');
201
+ this.chartInstance.on(
202
+ 'click',
203
+ (params: {
204
+ componentType?: string;
205
+ seriesType?: string;
206
+ seriesName?: string;
207
+ dataIndex?: number;
208
+ }) => {
209
+ if (
210
+ params.componentType === 'series' &&
211
+ params.seriesType === 'funnel' &&
212
+ params.seriesName !== 'PipelineName'
213
+ ) {
214
+ const idx =
215
+ typeof params.dataIndex === 'number' ? params.dataIndex : -1;
216
+ const name = stages[idx]?.name;
217
+ if (name) {
218
+ this.handleStageClick(name);
219
+ onStageClick?.(name);
220
+ }
221
+ }
222
+ },
223
+ );
224
+ }
225
+ requestAnimationFrame(() => this.chartInstance?.resize());
226
+ } catch (e) {
227
+ console.error('PipelineFunnel ECharts 更新失败:', e);
228
+ }
229
+ }
230
+
231
+ async fetchChartData() {
232
+ const { viewId, viewType } = this.props;
233
+ const userId = this.props.data?.__NeoCurrentUser?.id;
234
+
235
+ if (viewId == null || viewId === '' || userId == null || userId === '') {
236
+ this.setState({
237
+ loading: false,
238
+ error: '缺少 viewId 或当前用户 id(data.__NeoCurrentUser.id)',
239
+ stages: [],
240
+ totalAmountNum: 0,
241
+ totalAmountLabel: formatAmountDisplay(0),
242
+ });
243
+ return;
244
+ }
245
+
246
+ this.setState({ loading: true, error: null });
247
+
248
+ try {
249
+ const filterObj =
250
+ this.state.filter &&
251
+ typeof this.state.filter === 'object' &&
252
+ !Array.isArray(this.state.filter)
253
+ ? this.state.filter
254
+ : {};
255
+
256
+ const res = await request({
257
+ url: QUERY_DATA_TASK_URL,
258
+ method: 'POST',
259
+ data: buildQueryDataTaskFormBody({
260
+ viewId: String(viewId),
261
+ userId,
262
+ type: viewType ?? 'sync',
263
+ filter: filterObj,
264
+ }),
265
+ headers: {
266
+ 'Content-Type': FORM_URLENCODED_UTF8,
267
+ },
268
+ });
269
+
270
+ const status = res?.status;
271
+ const table = res?.data;
272
+
273
+ if (status !== 0 || !Array.isArray(table)) {
274
+ this.setState({
275
+ loading: false,
276
+ error: res?.message || res?.msg || '查询图表数据失败',
277
+ stages: [],
278
+ totalAmountNum: 0,
279
+ totalAmountLabel: formatAmountDisplay(0),
280
+ });
281
+ return;
282
+ }
283
+
284
+ const filteredTable = filterOutClosedLostRows(table as unknown[][]);
285
+ const { stages, totalAmountNum } = parseRowsToStages(filteredTable);
286
+
287
+ this.setState({
288
+ loading: false,
289
+ error: null,
290
+ stages,
291
+ totalAmountNum,
292
+ totalAmountLabel: formatAmountDisplay(totalAmountNum),
293
+ });
294
+ } catch (e: any) {
295
+ console.error('PipelineFunnel queryDataTask 失败:', e);
296
+ this.setState({
297
+ loading: false,
298
+ error: e?.message || '网络请求失败',
299
+ stages: [],
300
+ totalAmountNum: 0,
301
+ totalAmountLabel: formatAmountDisplay(0),
302
+ });
303
+ }
304
+ }
305
+
306
+ /**
307
+ * 刷新漏斗图数据(设计器可绑定)
308
+ */
309
+ @NeoEvent.function
310
+ async refreshData() {
311
+ await this.fetchChartData();
312
+ }
313
+
314
+ /**
315
+ * 设置过滤条件并重新拉数(设计器可绑定)
316
+ * @param filter 过滤对象,将作为 queryDataTask 的 filter 字段
317
+ */
318
+ @NeoEvent.function
319
+ setFilter(filter?: any) {
320
+ if (!filter) {
321
+ return;
322
+ }
323
+ const nextFilter = filter2chartFilter(filter);
324
+ console.log('[PipelineFunnel__c] setFilter:', filter, nextFilter);
325
+ if (!isEqual(nextFilter, this.state.filter)) {
326
+ this.setState({ filter: nextFilter }, () => {
327
+ this.fetchChartData();
328
+ });
329
+ }
330
+ }
331
+
332
+ @NeoEvent.dispatch
333
+ onActiveStageChange(eventData?: any) {}
334
+
335
+ // 点击阶段切换时,更新当前激活的销售阶段
336
+ handleStageClick(stageName: string) {
337
+ this.onActiveStageChange({
338
+ activeStage: stageName,
339
+ });
340
+ }
341
+
342
+ render() {
343
+ const {
344
+ title = 'Pipeline Funnel',
345
+ // showAiButton = false,
346
+ className,
347
+ style,
348
+ } = this.props;
349
+ const showAiButton = false;
350
+
351
+ const { loading, error, stages, totalAmountLabel } = this.state;
352
+ const chartHeight = Math.max(stages.length * 52, 200);
353
+
354
+ return (
355
+ <div
356
+ className={`pipelineFunnel__c ${className || ''}`}
357
+ style={style}
358
+ data-time="2026.4.15 01"
359
+ >
360
+ <Spin spinning={loading}>
361
+ <div className="funnel-header">
362
+ <h3 className="funnel-title">{title}</h3>
363
+ {showAiButton && (
364
+ <span
365
+ className="ai-btn"
366
+ onClick={() => console.log('AI Analysis clicked')}
367
+ title="AI Analysis"
368
+ >
369
+
370
+ </span>
371
+ )}
372
+ </div>
373
+
374
+ {error ? (
375
+ <div
376
+ className="funnel-error"
377
+ style={{ color: '#cf1322', fontSize: 12, marginBottom: 8 }}
378
+ >
379
+ {error}
380
+ </div>
381
+ ) : null}
382
+
383
+ <div className="funnel-total">
384
+ <div className="funnel-total-label">Sales Amount</div>
385
+ <div className="funnel-total-value">{totalAmountLabel}</div>
386
+ </div>
387
+
388
+ <div className="funnel-body">
389
+ <div className="funnel-chart-wrap" style={{ height: chartHeight }}>
390
+ <div
391
+ ref={this.chartRef}
392
+ className="funnel-chart"
393
+ style={{ width: '100%', height: chartHeight }}
394
+ />
395
+ </div>
396
+ </div>
397
+
398
+ <div className="funnel-legend">
399
+ {stages.map((stage, index) => (
400
+ <span key={index} className="legend-item">
401
+ <span
402
+ className="legend-dot"
403
+ style={{ backgroundColor: stage.color }}
404
+ />
405
+ {stage.name}
406
+ </span>
407
+ ))}
408
+ </div>
409
+ </Spin>
410
+ </div>
411
+ );
412
+ }
413
+ }
414
+
415
+ export default PipelineFunnel;
@@ -0,0 +1,79 @@
1
+ export class PipelineFunnelModel {
2
+ label: string = 'Pipeline漏斗图';
3
+ description: string = '展示销售管道的漏斗转化情况,直观显示各阶段金额和数量';
4
+ iconUrl: string = 'https://custom-widgets.bj.bcebos.com/funnel.svg';
5
+ targetPage: string[] = ['all'];
6
+ targetDevice: string = 'all';
7
+
8
+ defaultComProps = {
9
+ title: 'Pipeline Funnel',
10
+ viewId: '4264464770007375',
11
+ viewType: 'sync',
12
+ showAiButton: true,
13
+ /** 为 true 时层宽按阶段顺序递减(经典形状),层高度按各阶段金额占比;默认 false,层宽按真实金额 */
14
+ useClassicFunnelShape: false,
15
+ };
16
+
17
+ events = [
18
+ {
19
+ apiKey: 'onActiveStageChange',
20
+ label: '点击阶段触发',
21
+ helpText:
22
+ '点击漏斗阶段触发;事件参数含 activeStage(当前激活阶段)',
23
+ eventParams:
24
+ '[{"apiKey":"eventParam","children":[{"apiKey":"activeStage","label":"当前激活阶段","type":"String"}],"label":"事件入参","type":"Object"}]',
25
+ },
26
+ ];
27
+
28
+ functions = [
29
+ {
30
+ apiKey: 'refreshData',
31
+ label: '刷新数据',
32
+ helpTextKey: '重新请求 queryDataTask 刷新漏斗图数据',
33
+ },
34
+ {
35
+ apiKey: 'setFilter',
36
+ label: '设置过滤条件',
37
+ helpTextKey: '设置报表组件的 filter 并重新拉取数据',
38
+ funcInParams: [
39
+ {
40
+ apiKey: 'filter',
41
+ label: '过滤条件',
42
+ type: 'Object',
43
+ required: false,
44
+ },
45
+ ],
46
+ },
47
+ ];
48
+
49
+ propsSchema = [
50
+ {
51
+ type: 'panelInput',
52
+ name: 'title',
53
+ label: '标题',
54
+ },
55
+ {
56
+ type: 'panelInput',
57
+ name: 'viewId',
58
+ label: '视图 ID',
59
+ },
60
+ {
61
+ type: 'panelInput',
62
+ name: 'viewType',
63
+ label: '视图类型(请求 type)',
64
+ },
65
+ {
66
+ type: 'panelSelect',
67
+ name: 'useClassicFunnelShape',
68
+ label: '漏斗形状',
69
+ value: false,
70
+ options: [
71
+ { label: '按真实金额设置漏斗形状', value: false },
72
+ { label: '经典漏斗(层高按数值大小)', value: true },
73
+ ],
74
+ clearable: false,
75
+ },
76
+ ];
77
+ }
78
+
79
+ export default PipelineFunnelModel;