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,343 @@
1
+ /**
2
+ * Pipeline funnel pipelineFunnel specific (parse table rows, ECharts config)
3
+ */
4
+ import * as echarts from 'echarts';
5
+
6
+ import { formatAmountDisplay } from './common';
7
+
8
+ export const FUNNEL_COLORS = [
9
+ '#3b82f6',
10
+ '#22c55e',
11
+ '#f59e0b',
12
+ '#8b5cf6',
13
+ '#ec4899',
14
+ '#06b6d4',
15
+ ];
16
+
17
+ export interface FunnelStage {
18
+ name: string;
19
+ amountNum: number;
20
+ amount: string;
21
+ count: number;
22
+ conversionRate?: string;
23
+ color: string;
24
+ }
25
+
26
+ export function parseAmountToNumber(raw: unknown): number {
27
+ if (raw === null || raw === undefined) return 0;
28
+ const s = String(raw)
29
+ .replace(/,/g, '')
30
+ .replace(/[^\d.-]/g, '');
31
+ const n = parseFloat(s);
32
+ return Number.isFinite(n) ? n : 0;
33
+ }
34
+
35
+ export function formatSharePercent(amount: number, total: number): string {
36
+ if (!total || !Number.isFinite(amount)) return '—';
37
+ return `${((amount / total) * 100).toFixed(1)}%`;
38
+ }
39
+
40
+ export function isTruthyProp(v: unknown): boolean {
41
+ return v === true || v === 'true' || v === 1 || v === '1';
42
+ }
43
+
44
+ /** Ignore case and consecutive whitespace when comparing with backend stage names */
45
+ export function isClosedLostStageName(raw: unknown): boolean {
46
+ const n = String(raw ?? '')
47
+ .trim()
48
+ .toLowerCase()
49
+ .replace(/\s+/g, ' ');
50
+ return n === 'closed lost';
51
+ }
52
+
53
+ /** Remove Closed Lost stage rows, keep header; must be called before parseRowsToStages to avoid counting in total and percentage */
54
+ export function filterOutClosedLostRows(table: unknown[][]): unknown[][] {
55
+ if (!Array.isArray(table) || table.length < 2) return table;
56
+ const header = table[0];
57
+ const rest = table
58
+ .slice(1)
59
+ .filter((row) => !(Array.isArray(row) && isClosedLostStageName(row[0])));
60
+ return [header, ...rest];
61
+ }
62
+
63
+ export function parseRowsToStages(rows: unknown[][]): {
64
+ stages: FunnelStage[];
65
+ totalAmountNum: number;
66
+ } {
67
+ if (!Array.isArray(rows) || rows.length < 2) {
68
+ return { stages: [], totalAmountNum: 0 };
69
+ }
70
+ const dataRows = rows.slice(1);
71
+ const stages: FunnelStage[] = [];
72
+ const amountNums: number[] = [];
73
+ let totalAmountNum = 0;
74
+
75
+ for (let i = 0; i < dataRows.length; i++) {
76
+ const row = dataRows[i];
77
+ if (!Array.isArray(row) || row.length < 4) continue;
78
+
79
+ const name = String(row[0] ?? '');
80
+ const countRaw = row[2];
81
+ const amountNum = parseAmountToNumber(row[3]);
82
+ totalAmountNum += amountNum;
83
+ amountNums.push(amountNum);
84
+
85
+ const countNum = parseInt(String(countRaw).replace(/\D/g, ''), 10);
86
+ const count = Number.isFinite(countNum) ? countNum : 0;
87
+
88
+ stages.push({
89
+ name,
90
+ amountNum,
91
+ amount: formatAmountDisplay(amountNum),
92
+ count,
93
+ color: FUNNEL_COLORS[i % FUNNEL_COLORS.length],
94
+ });
95
+ }
96
+
97
+ for (let j = 0; j < stages.length; j++) {
98
+ stages[j].conversionRate = formatSharePercent(
99
+ amountNums[j],
100
+ totalAmountNum,
101
+ );
102
+ }
103
+
104
+ return { stages, totalAmountNum };
105
+ }
106
+
107
+ export function funnelLayerValue(index: number, total: number): number {
108
+ return Math.max(total - index, 1);
109
+ }
110
+
111
+ export function funnelValueForStage(
112
+ stage: FunnelStage,
113
+ index: number,
114
+ stageCount: number,
115
+ useClassicFunnelShape: boolean,
116
+ ): number {
117
+ if (useClassicFunnelShape) {
118
+ return funnelLayerValue(index, stageCount);
119
+ }
120
+ const v = stage.amountNum;
121
+ if (Number.isFinite(v) && v > 0) return v;
122
+ return 0;
123
+ }
124
+
125
+ /** Classic funnel: width still controlled by value (order); layer height allocated by amount proportion (consistent with series.gap, unit is px) */
126
+ export function classicFunnelLayerHeightPercents(
127
+ amountNums: number[],
128
+ viewHeightPx: number,
129
+ gapPx: number,
130
+ ): string[] {
131
+ const n = amountNums.length;
132
+ if (n === 0) return [];
133
+ const h = Math.max(viewHeightPx, 1);
134
+ const factor = (h - gapPx * Math.max(n - 1, 0)) / h;
135
+ const positives = amountNums.map((v) =>
136
+ Number.isFinite(v) && v > 0 ? v : 0,
137
+ );
138
+ const sum = positives.reduce((a, v) => a + v, 0);
139
+ if (sum <= 0) {
140
+ const even = (100 / n) * factor;
141
+ return Array(n).fill(`${even}%`);
142
+ }
143
+ return positives.map((v) => `${100 * (v / sum) * factor}%`);
144
+ }
145
+
146
+ export interface BuildFunnelChartOptionParams {
147
+ /** Container height (px), used in classic mode to calculate layer height percentages; should re-setOption when changed */
148
+ chartViewHeightPx?: number;
149
+ funnelGap?: number;
150
+ }
151
+
152
+ export function buildFunnelChartOption(
153
+ stages: FunnelStage[],
154
+ useClassicFunnelShape: boolean,
155
+ params?: BuildFunnelChartOptionParams,
156
+ ): echarts.EChartsOption {
157
+ const n = stages.length;
158
+ const values = stages.map((s, i) =>
159
+ funnelValueForStage(s, i, n, useClassicFunnelShape),
160
+ );
161
+ const funnelMax = useClassicFunnelShape
162
+ ? Math.max(n, 1)
163
+ : Math.max(...values, 1);
164
+
165
+ const gap = params?.funnelGap ?? 2;
166
+ const viewH = params?.chartViewHeightPx;
167
+ const classicHeights =
168
+ useClassicFunnelShape && viewH != null && viewH > 0
169
+ ? classicFunnelLayerHeightPercents(
170
+ stages.map((s) => s.amountNum),
171
+ viewH,
172
+ gap,
173
+ )
174
+ : null;
175
+
176
+ const coloredData = stages.map((s, i) => ({
177
+ name: s.name,
178
+ value: values[i],
179
+ itemStyle: {
180
+ color: s.color,
181
+ ...(classicHeights?.[i] ? { height: classicHeights[i] } : {}),
182
+ },
183
+ }));
184
+
185
+ const alignData = stages.map((s, i) => ({
186
+ name: s.name,
187
+ value: values[i],
188
+ ...(classicHeights?.[i]
189
+ ? { itemStyle: { height: classicHeights[i] } }
190
+ : {}),
191
+ }));
192
+
193
+ const labelLineStyle = {
194
+ length: 28,
195
+ lineStyle: {
196
+ width: 1,
197
+ color: '#d0d0d0',
198
+ type: 'solid' as const,
199
+ },
200
+ };
201
+
202
+ const labelLayoutLeft = () => ({
203
+ x: 0,
204
+ align: 'left' as const,
205
+ dx: 4,
206
+ });
207
+
208
+ const labelLayoutRight = () => ({
209
+ x: '100%',
210
+ align: 'right' as const,
211
+ dx: -6,
212
+ });
213
+
214
+ const funnelLayout = {
215
+ left: '12%',
216
+ top: 4,
217
+ bottom: 4,
218
+ width: '76%',
219
+ min: 0,
220
+ max: funnelMax,
221
+ minSize: '0%',
222
+ maxSize: '100%',
223
+ sort: 'none' as const,
224
+ gap: 1,
225
+ };
226
+
227
+ return {
228
+ backgroundColor: 'transparent',
229
+ legend: { show: false },
230
+ tooltip: {
231
+ trigger: 'item',
232
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
233
+ borderColor: '#333',
234
+ textStyle: { color: '#fff' },
235
+ formatter: (params: unknown) => {
236
+ const p = params as { dataIndex?: number; seriesName?: string };
237
+ const idx = p.dataIndex ?? 0;
238
+ const st = stages[idx];
239
+ if (!st) return '';
240
+ return [
241
+ `${st.name}`,
242
+ `Percentage: ${st.conversionRate ?? '—'}`,
243
+ `Amount: ${st.amount}`,
244
+ `Quantity: ${st.count}`,
245
+ ].join('<br/>');
246
+ },
247
+ },
248
+ series: [
249
+ {
250
+ name: 'Pipeline',
251
+ type: 'funnel',
252
+ z: 1,
253
+ ...funnelLayout,
254
+ itemStyle: {
255
+ borderColor: '#fff',
256
+ borderWidth: 1,
257
+ },
258
+ emphasis: {
259
+ itemStyle: {
260
+ shadowBlur: 10,
261
+ shadowColor: 'rgba(0, 0, 0, 0.15)',
262
+ },
263
+ },
264
+ label: {
265
+ show: true,
266
+ position: 'left',
267
+ color: '#888',
268
+ fontSize: 11,
269
+ formatter: (p: { dataIndex?: number }) => {
270
+ const idx = typeof p.dataIndex === 'number' ? p.dataIndex : 0;
271
+ return stages[idx]?.conversionRate ?? '—';
272
+ },
273
+ },
274
+ labelLine: {
275
+ show: true,
276
+ ...labelLineStyle,
277
+ },
278
+ labelLayout: labelLayoutLeft,
279
+ data: coloredData,
280
+ },
281
+ {
282
+ name: 'PipelineLabels',
283
+ type: 'funnel',
284
+ z: 2,
285
+ animation: false,
286
+ animationDurationUpdate: 0,
287
+ ...funnelLayout,
288
+ itemStyle: {
289
+ color: 'rgba(0,0,0,0)',
290
+ borderWidth: 0,
291
+ },
292
+ emphasis: { disabled: true },
293
+ label: {
294
+ show: true,
295
+ position: 'right',
296
+ color: '#555',
297
+ fontSize: 11,
298
+ formatter: (p: { dataIndex?: number }) => {
299
+ const idx = typeof p.dataIndex === 'number' ? p.dataIndex : 0;
300
+ const st = stages[idx];
301
+ return st ? `${st.amount} / ${st.count}` : '';
302
+ },
303
+ },
304
+ labelLine: {
305
+ show: true,
306
+ ...labelLineStyle,
307
+ },
308
+ labelLayout: labelLayoutRight,
309
+ data: alignData,
310
+ },
311
+ {
312
+ name: 'PipelineName',
313
+ type: 'funnel',
314
+ z: 3,
315
+ silent: true,
316
+ animation: false,
317
+ animationDurationUpdate: 0,
318
+ tooltip: { show: false },
319
+ ...funnelLayout,
320
+ itemStyle: {
321
+ color: 'rgba(0,0,0,0)',
322
+ borderWidth: 0,
323
+ },
324
+ emphasis: { disabled: true },
325
+ label: {
326
+ show: true,
327
+ position: 'inside',
328
+ color: '#fff',
329
+ fontSize: 12,
330
+ fontWeight: 600,
331
+ textBorderColor: 'rgba(0,0,0,0.4)',
332
+ textBorderWidth: 1,
333
+ formatter: (p: { name?: string; dataIndex?: number }) => {
334
+ const idx = typeof p.dataIndex === 'number' ? p.dataIndex : 0;
335
+ return stages[idx]?.name ?? p.name ?? '';
336
+ },
337
+ },
338
+ labelLine: { show: false },
339
+ data: alignData,
340
+ },
341
+ ],
342
+ };
343
+ }
@@ -0,0 +1,121 @@
1
+ // @ts-nocheck
2
+ import { request as axiosFetcher } from 'neo-open-api';
3
+
4
+ /**
5
+ * Common query Open APIs are stored here
6
+ */
7
+
8
+ /** Normalize where to SQL fragment: string is used directly; array items are joined with 'and' */
9
+ function normalizeWhere(where: unknown): string {
10
+ if (where == null || where === '') {
11
+ return '';
12
+ }
13
+ if (typeof where === 'string') {
14
+ return where.trim();
15
+ }
16
+ if (Array.isArray(where)) {
17
+ return where
18
+ .map((part) => (part == null ? '' : String(part).trim()))
19
+ .filter(Boolean)
20
+ .join(' and ');
21
+ }
22
+ return '';
23
+ }
24
+
25
+ // Query BI data by SQL
26
+ export const queryByCustomSQL = async (options?: any) => {
27
+ const apiUrl = '/rest/neobi/v2.0/query/queryByCustomSQL';
28
+ const curOptions = options || {};
29
+ const xObjectApiKey = curOptions.xObjectApiKey || '';
30
+ const fields = Object.assign([], curOptions.fields || []);
31
+ const page = curOptions.page || 1;
32
+ const pageSize = curOptions.pageSize || 10;
33
+
34
+ // Automatically add objectId field
35
+ if (!fields.includes('id')) {
36
+ fields.push('id');
37
+ }
38
+
39
+ // Calculate pagination offset
40
+ const offset = (page - 1) * pageSize;
41
+
42
+ // Build SQL query
43
+ let querySql = `select ${fields.join(',')} from ${xObjectApiKey}`;
44
+
45
+ // Add sort conditions (if any)
46
+ if (curOptions.orderBy) {
47
+ querySql += ` order by ${curOptions.orderBy}`;
48
+ }
49
+
50
+ /**
51
+ * Add filter conditions (if any)
52
+ * Supported operators: =, !=, like, not like, not in, is not null, is null, >, <, <>, >=, <=, in, between ... and ...
53
+ * Notes for =, like and in:
54
+ * "=" as a string condition means exact match. For example, city = 'Beijing' returns all records where city is strictly equal to "Beijing".
55
+ * "like" as a string condition requires "%" wildcard for fuzzy matching. For example, city like 'Beijing%' returns all records where city starts with "Beijing".
56
+ * Currently only supports placing "%" after the known content; e.g., city like '% Beijing' is not supported.
57
+ * When the SQL query contains special characters like "%", URL encoding is required.
58
+ * Supports "in", but not subqueries.
59
+ * Supported logical operators: and, or.
60
+ *
61
+ * `where` can be a string, or a string array (multiple items joined with 'and' by default, equivalent to `a and b`).
62
+ */
63
+ const whereClause = normalizeWhere(curOptions.where);
64
+
65
+ if (whereClause) {
66
+ querySql += ` where ${whereClause}`;
67
+ }
68
+
69
+ if (curOptions.page || curOptions.pageSize) {
70
+ // Add pagination limit
71
+ querySql += ` limit ${offset},${pageSize}`;
72
+ }
73
+
74
+ const querySqlForm = new URLSearchParams();
75
+ querySqlForm.set('sqlObject', querySql);
76
+
77
+ try {
78
+ const config = {
79
+ url: apiUrl,
80
+ method: 'POST',
81
+ data: querySqlForm.toString(),
82
+ headers: {
83
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
84
+ },
85
+ };
86
+
87
+ const resultData = await axiosFetcher(config);
88
+
89
+ if (resultData.code === 200) {
90
+ const records = resultData.data || [];
91
+ return {
92
+ status: true,
93
+ code: resultData.code,
94
+ msg:
95
+ resultData.msg || 'Successfully retrieved business object data list',
96
+ totalSize: records.length,
97
+ data: records || [],
98
+ };
99
+ }
100
+
101
+ return {
102
+ status: false,
103
+ code: resultData.code,
104
+ msg: resultData.msg || 'Failed to retrieve business object data list',
105
+ data: [],
106
+ };
107
+ } catch (error) {
108
+ console.error('Failed to retrieve business object data list:', error);
109
+
110
+ return {
111
+ status: false,
112
+ msg:
113
+ error.msg ||
114
+ error.message ||
115
+ 'Failed to retrieve business object data list',
116
+ data: [],
117
+ };
118
+ }
119
+ };
120
+
121
+ export default queryByCustomSQL;
@@ -0,0 +1,22 @@
1
+ // @ts-nocheck
2
+ import debounce from 'lodash/debounce';
3
+
4
+ /** Data request debounce interval in milliseconds for components, used to merge repeated triggers within a short time */
5
+ export const REQUEST_DEBOUNCE_MS = 280;
6
+
7
+ /** lodash debounce instance (avoids dependency on @types/lodash) */
8
+ export type DebouncedRequestFn = {
9
+ (): void;
10
+ cancel(): void;
11
+ flush(): void;
12
+ };
13
+
14
+ /**
15
+ * Trailing debounce: executes only once after a pause during continuous triggers, suitable for filter / props rapid update scenarios.
16
+ */
17
+ export function createRequestDebounce(
18
+ fn: () => void,
19
+ wait: number = REQUEST_DEBOUNCE_MS,
20
+ ): DebouncedRequestFn {
21
+ return debounce(fn, wait) as DebouncedRequestFn;
22
+ }