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,229 @@
1
+ /**
2
+ * 多组件共用的纯工具方法(筛选 where、阶段名、金额展示、NeoBI 请求体等)
3
+ */
4
+
5
+ export interface PropsContext {
6
+ data?: {
7
+ __NeoCurrentUser?: {
8
+ id?: number;
9
+ name?: string | null;
10
+ };
11
+ };
12
+ }
13
+ /** 筛选项转有限数字,无效返回 null(与 filter2chartFilter 等一致) */
14
+ export function toFiniteNumber(v: unknown): number | null {
15
+ if (v == null || v === '') return null;
16
+ if (typeof v === 'number' && Number.isFinite(v)) return v;
17
+ const n = Number(v);
18
+ return Number.isFinite(n) ? n : null;
19
+ }
20
+
21
+ /** 时间区间起止:均为有效数字时返回闭区间 [start, end](毫秒时间戳,与 FilterBar 一致) */
22
+ export function closeRangeFromFilter(
23
+ range: unknown,
24
+ ): { start: number; end: number } | null {
25
+ if (!range || typeof range !== 'object') return null;
26
+ const r = range as { start?: unknown; end?: unknown };
27
+ const start = toFiniteNumber(r.start);
28
+ const end = toFiniteNumber(r.end);
29
+ if (start == null || end == null) return null;
30
+ return { start, end };
31
+ }
32
+
33
+ /** opportunityOwner → 用于 where 的用户 id 列表(字符串避免大整数精度丢失) */
34
+ export function normalizeOwnerIdsForWhere(owner: unknown): string[] {
35
+ if (owner == null || owner === '') return [];
36
+ const raw = Array.isArray(owner) ? owner : [owner];
37
+ const out: string[] = [];
38
+ for (const item of raw) {
39
+ if (item == null || item === '') continue;
40
+ const s = String(item).trim().replace(/,/g, '');
41
+ if (s === '' || !/^-?\d+$/.test(s)) continue;
42
+ out.push(s);
43
+ }
44
+ return out;
45
+ }
46
+
47
+ export function entityTypeIdForWhere(businessType: unknown): string | null {
48
+ if (businessType == null || businessType === '') return null;
49
+ const s = String(businessType).trim();
50
+ if (s === '' || !/^-?\d+$/.test(s.replace(/,/g, ''))) return null;
51
+ return s.replace(/,/g, '');
52
+ }
53
+
54
+ /** 从阶段展示名取「第一个 . 之后」的片段,用于匹配 customItem1__c 等 */
55
+ export function extractStageKeyFromStageName(stageName: string): string {
56
+ const s = String(stageName ?? '').trim();
57
+ const i = s.indexOf('.');
58
+ if (i < 0) return s;
59
+ return s.slice(i + 1).trim();
60
+ }
61
+
62
+ /** 商机行等:空或无法解析时为 0 */
63
+ export function parseNumberOrZero(raw: unknown): number {
64
+ if (raw === null || raw === undefined || raw === '') return 0;
65
+ const n =
66
+ typeof raw === 'number'
67
+ ? raw
68
+ : parseFloat(String(raw).replace(/,/g, '').trim());
69
+ return Number.isFinite(n) ? n : 0;
70
+ }
71
+
72
+ /** 阈值配置等:空或无法解析时为 NaN */
73
+ export function parseNumberOrNaN(raw: unknown): number {
74
+ if (raw === null || raw === undefined || raw === '') return NaN;
75
+ const n =
76
+ typeof raw === 'number'
77
+ ? raw
78
+ : parseFloat(String(raw).replace(/,/g, '').trim());
79
+ return Number.isFinite(n) ? n : NaN;
80
+ }
81
+
82
+ /** 展示用金额:$ 前缀;≥1e3 时使用 K / M / B / T 缩写 */
83
+ export function formatAmountDisplay(n: number): string {
84
+ if (!Number.isFinite(n)) return '-';
85
+ const sign = n < 0 ? '-' : '';
86
+ const abs = Math.abs(n);
87
+ if (abs === 0) return '$0';
88
+
89
+ if (abs < 1000) {
90
+ const part = Number.isInteger(abs)
91
+ ? abs.toLocaleString('en-US')
92
+ : abs.toLocaleString('en-US', {
93
+ minimumFractionDigits: 0,
94
+ maximumFractionDigits: 2,
95
+ });
96
+ return `${sign}$${part}`;
97
+ }
98
+
99
+ const tier = Math.min(Math.floor(Math.log10(abs) / 3), 4);
100
+ const suffixes = ['', 'K', 'M', 'B', 'T'] as const;
101
+ const suffix = suffixes[tier];
102
+ const divisor = 1000 ** tier;
103
+ const scaled = abs / divisor;
104
+
105
+ let body: string;
106
+ if (scaled >= 100) body = String(Math.round(scaled));
107
+ else if (scaled >= 10) body = scaled.toFixed(1).replace(/\.0$/, '');
108
+ else body = scaled.toFixed(2).replace(/\.?0+$/, '');
109
+
110
+ return `${sign}$${body}${suffix}`;
111
+ }
112
+
113
+ /** queryDataTask:x-www-form-urlencoded 表单体(filter 以 JSON 字符串传递) */
114
+ export function buildQueryDataTaskFormBody(params: {
115
+ viewId: string;
116
+ userId: string | number;
117
+ type: string;
118
+ filter: Record<string, unknown>;
119
+ }): string {
120
+ const form = new URLSearchParams();
121
+ form.set('viewId', String(params.viewId));
122
+ form.set('userId', String(params.userId));
123
+ form.set('type', params.type);
124
+ form.set('filter', JSON.stringify(params.filter));
125
+ return form.toString();
126
+ }
127
+
128
+ /** 负责人多选默认值:当前登录用户 id(与 __NeoCurrentUser 一致) */
129
+ export function getDefaultOpportunityOwnerIds(
130
+ props: PropsContext,
131
+ ): (number | string)[] {
132
+ // return [1246045]; // 默认用户改成 (Alice)
133
+ const curUser = props.data?.__NeoCurrentUser;
134
+ if (
135
+ curUser?.id == null ||
136
+ curUser.name == null ||
137
+ String(curUser.name).trim() === ''
138
+ ) {
139
+ return [];
140
+ }
141
+ return [curUser.id];
142
+ }
143
+
144
+ /** 负责人多选默认值:当前登录用户 id(与 __NeoCurrentUser 一致) */
145
+ export function getDefaultFilterWhereByProps(props: any = {}): any {
146
+ let userId = 1246045; // 默认用户(Alice)
147
+ const curUser = props.data?.__NeoCurrentUser;
148
+ if (curUser?.id) {
149
+ userId = curUser.id;
150
+ }
151
+ const defaultFilter = {
152
+ relation: '1 and 2 and 3',
153
+ filter: [
154
+ {
155
+ modelKey: 'opportunity',
156
+ serialNo: 1,
157
+ fieldKey: 'closeDate',
158
+ type: 'date',
159
+ entityOnlyFilter: 0,
160
+ timeType: 'relative',
161
+ operate: 'eq',
162
+ value: [
163
+ {
164
+ val: 401,
165
+ baseType: 'system',
166
+ },
167
+ ],
168
+ seq: 1,
169
+ },
170
+ {
171
+ modelKey: 'opportunity',
172
+ serialNo: 1,
173
+ fieldKey: 'ownerId',
174
+ type: 'refer',
175
+ referKey: 'user',
176
+ optionType: 'user',
177
+ entityOnlyFilter: 0,
178
+ operate: 'in',
179
+ value: [
180
+ {
181
+ val: userId,
182
+ sub: false,
183
+ source: 'internal',
184
+ specialType: 0,
185
+ },
186
+ ],
187
+ includeNullValue: 0,
188
+ seq: 2,
189
+ },
190
+ {
191
+ modelKey: 'opportunity',
192
+ serialNo: 1,
193
+ fieldKey: 'entityType',
194
+ type: 'refer',
195
+ entityOnlyFilter: 0,
196
+ operate: 'in',
197
+ value: [
198
+ {
199
+ val: 8150459,
200
+ },
201
+ ],
202
+ seq: 3,
203
+ },
204
+ ],
205
+ };
206
+ return defaultFilter;
207
+ }
208
+
209
+ export function getDefaultFilterByProps(props: any = {}): any {
210
+ let userId = 1246045; // 默认用户(Alice)
211
+ const curUser = props.data?.__NeoCurrentUser;
212
+ if (curUser?.id) {
213
+ userId = curUser.id;
214
+ }
215
+ const defaultFilter = {
216
+ closeDate: 401,
217
+ closeDateCustomRange: {
218
+ start: 1775059200000,
219
+ end: 1782835199999,
220
+ },
221
+ opportunityOwner: [userId],
222
+ businessType: 8150459,
223
+ businessTypeLabel: 'New Business',
224
+ businessTypeApiKey: 'defaultBusiType_1',
225
+ changesSince: 'Start of the Period',
226
+ changesSinceCustomTime: 1775059200000,
227
+ };
228
+ return defaultFilter;
229
+ }
@@ -0,0 +1,268 @@
1
+ /**
2
+ * 过滤条件格式化
3
+ * 将筛选栏组件得到的过滤条件格式化为接口需要的格式。
4
+ *
5
+ * 数据转换规则:
6
+ 1. closeDate
7
+
8
+ 非 custom:用 toFiniteNumber 把选项值(如 201 / "201")转成数字,timeType: 'relative',value: [{ val, baseType: 'system' }]。
9
+ custom:读取 closeDateCustomRange.start / end,二者均为有效数字时,timeType: 'absolute',value 为两条 { val, close: 1 };缺区间则不生成该项。
10
+
11
+ 2. opportunityOwner → ownerId
12
+ 支持单值(字符串/数字)或多选数组;逐项 toFiniteNumber,无效项丢弃。至少一个有效 id 时生成 refer 条件(referKey: 'user'、optionType: 'user'、operate: 'in'、includeNullValue: 0)。
13
+ value 为数组,每个用户为 { val, sub: false, source: 'internal', specialType: 0 }。
14
+
15
+ 3. businessType → entityType
16
+ 有值且能转成数字时,生成 refer + value: [{ val }];空字符串不传 entityType。
17
+
18
+ 4. relation
19
+ 按最终条目的 seq 拼成 "1 and 2 and 3";没有任何条件时为 '',filter 为 []。
20
+
21
+ *
22
+ * 例如:
23
+ * 筛选栏组件得到的过滤条件:
24
+ {
25
+ "closeDate": "custom",
26
+ "closeDateCustomRange": {
27
+ "start": 1775577600000,
28
+ "end": 1777996799999
29
+ },
30
+ "opportunityOwner": ["1255807", 2088134315786370],
31
+ "businessType": "8150459",
32
+ "changesSince": "Custom",
33
+ "changesSinceCustomTime": 1776268800000
34
+ }
35
+
36
+ 接口需要的格式:
37
+ {
38
+ "relation": "1 and 2 and 3",
39
+ "filter": [
40
+ {
41
+ "seq": 1,
42
+ "fieldKey": "closeDate",
43
+ "modelKey": "opportunity",
44
+ "serialNo": 1,
45
+ "type": "date",
46
+ "entityOnlyFilter": 0,
47
+ "timeType": "relative",
48
+ "operate": "eq",
49
+ "value": [
50
+ {
51
+ "val": 101,
52
+ "baseType": "system"
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "seq": 2,
58
+ "fieldKey": "ownerId",
59
+ "modelKey": "opportunity",
60
+ "serialNo": 1,
61
+ "type": "refer",
62
+ "referKey": "user",
63
+ "optionType": "user",
64
+ "entityOnlyFilter": 0,
65
+ "operate": "in",
66
+ "value": [
67
+ {
68
+ "val": 1255807,
69
+ "sub": false,
70
+ "source": "internal",
71
+ "specialType": 0
72
+ },
73
+ {
74
+ "val": 2088134315786370,
75
+ "sub": false,
76
+ "source": "internal",
77
+ "specialType": 0
78
+ }
79
+ ],
80
+ "includeNullValue": 0
81
+ },
82
+ {
83
+ "seq": 3,
84
+ "fieldKey": "entityType",
85
+ "modelKey": "opportunity",
86
+ "serialNo": 1,
87
+ "type": "refer",
88
+ "entityOnlyFilter": 0,
89
+ "operate": "in",
90
+ "value": [
91
+ {
92
+ "val": 8150459
93
+ }
94
+ ]
95
+ }
96
+ ]
97
+ }
98
+
99
+ 如果是 closeDate 为 custom,则需要将 closeDateCustomRange 格式化为接口需要的格式
100
+ {
101
+ "seq": 3,
102
+ "fieldKey": "closeDate",
103
+ "modelKey": "opportunity",
104
+ "serialNo": 1,
105
+ "type": "date",
106
+ "entityOnlyFilter": 0,
107
+ "timeType": "absolute",
108
+ "operate": "eq",
109
+ "value": [
110
+ {
111
+ "val": 1777305600000,
112
+ "close": 1
113
+ },
114
+ {
115
+ "val": 1780156799999,
116
+ "close": 1
117
+ }
118
+ ]
119
+ }
120
+ */
121
+
122
+ import { toFiniteNumber } from './common';
123
+
124
+ const MODEL_KEY = 'opportunity';
125
+ const SERIAL_NO = 1;
126
+
127
+ type ChartFilterItem = {
128
+ seq: number;
129
+ fieldKey: string;
130
+ modelKey: string;
131
+ serialNo: number;
132
+ type: string;
133
+ entityOnlyFilter: number;
134
+ operate: string;
135
+ value: unknown[];
136
+ timeType?: string;
137
+ referKey?: string;
138
+ optionType?: string;
139
+ includeNullValue?: number;
140
+ };
141
+
142
+ export interface ChartFilterPayload {
143
+ relation: string;
144
+ filter: ChartFilterItem[];
145
+ }
146
+
147
+ function closeDateChunk(
148
+ filter: Record<string, unknown>,
149
+ ): Omit<ChartFilterItem, 'seq'> | null {
150
+ const raw = filter.closeDate;
151
+ const isCustom = String(raw ?? '').toLowerCase() === 'custom';
152
+
153
+ if (isCustom) {
154
+ const range = filter.closeDateCustomRange as
155
+ | { start?: unknown; end?: unknown }
156
+ | undefined;
157
+ const start = toFiniteNumber(range?.start);
158
+ const end = toFiniteNumber(range?.end);
159
+ if (start == null || end == null) return null;
160
+ return {
161
+ modelKey: MODEL_KEY,
162
+ serialNo: SERIAL_NO,
163
+ fieldKey: 'closeDate',
164
+ type: 'date',
165
+ entityOnlyFilter: 0,
166
+ timeType: 'absolute',
167
+ operate: 'eq',
168
+ value: [
169
+ { val: start, close: 1 },
170
+ { val: end, close: 1 },
171
+ ],
172
+ };
173
+ }
174
+
175
+ const n = toFiniteNumber(raw);
176
+ if (n == null) return null;
177
+ return {
178
+ modelKey: MODEL_KEY,
179
+ serialNo: SERIAL_NO,
180
+ fieldKey: 'closeDate',
181
+ type: 'date',
182
+ entityOnlyFilter: 0,
183
+ timeType: 'relative',
184
+ operate: 'eq',
185
+ value: [{ val: n, baseType: 'system' }],
186
+ };
187
+ }
188
+
189
+ const ownerUserValueItem = (val: number) => ({
190
+ val,
191
+ sub: false as const,
192
+ source: 'internal' as const,
193
+ specialType: 0,
194
+ });
195
+
196
+ /** 从 opportunityOwner(单值或 id 数组)解析出有效的用户 id 列表 */
197
+ function normalizeOwnerIds(owner: unknown): number[] {
198
+ if (owner == null || owner === '') return [];
199
+ const raw = Array.isArray(owner) ? owner : [owner];
200
+ const out: number[] = [];
201
+ for (const item of raw) {
202
+ if (item == null || item === '') continue;
203
+ const n = toFiniteNumber(item);
204
+ if (n != null) out.push(n);
205
+ }
206
+ return out;
207
+ }
208
+
209
+ function ownerChunk(
210
+ filter: Record<string, unknown>,
211
+ ): Omit<ChartFilterItem, 'seq'> | null {
212
+ const ids = normalizeOwnerIds(filter.opportunityOwner);
213
+ if (ids.length === 0) return null;
214
+ return {
215
+ modelKey: MODEL_KEY,
216
+ serialNo: SERIAL_NO,
217
+ fieldKey: 'ownerId',
218
+ type: 'refer',
219
+ referKey: 'user',
220
+ optionType: 'user',
221
+ entityOnlyFilter: 0,
222
+ operate: 'in',
223
+ value: ids.map(ownerUserValueItem),
224
+ includeNullValue: 0,
225
+ };
226
+ }
227
+
228
+ function entityTypeChunk(
229
+ filter: Record<string, unknown>,
230
+ ): Omit<ChartFilterItem, 'seq'> | null {
231
+ const busi = filter.businessType;
232
+ if (busi == null || busi === '') return null;
233
+ const val = toFiniteNumber(busi);
234
+ if (val == null) return null;
235
+ return {
236
+ modelKey: MODEL_KEY,
237
+ serialNo: SERIAL_NO,
238
+ fieldKey: 'entityType',
239
+ type: 'refer',
240
+ entityOnlyFilter: 0,
241
+ operate: 'in',
242
+ value: [{ val }],
243
+ };
244
+ }
245
+
246
+ /**
247
+ * 将筛选栏 payload 转为图表/接口 filter 结构;仅映射 closeDate、ownerId、entityType。
248
+ */
249
+ export const filter2chartFilter = (filter: any): ChartFilterPayload => {
250
+ const f = filter && typeof filter === 'object' ? filter : {};
251
+ const parts = [
252
+ closeDateChunk(f),
253
+ ownerChunk(f),
254
+ entityTypeChunk(f),
255
+ ].filter(Boolean) as Omit<ChartFilterItem, 'seq'>[];
256
+
257
+ const filterOut: ChartFilterItem[] = parts.map((p, i) => ({
258
+ ...p,
259
+ seq: i + 1,
260
+ }));
261
+
262
+ const relation =
263
+ filterOut.length === 0
264
+ ? ''
265
+ : filterOut.map((x) => String(x.seq)).join(' and ');
266
+
267
+ return { relation, filter: filterOut };
268
+ };
@@ -0,0 +1,140 @@
1
+ /**
2
+ * 筛选栏 filterBar 专用工具方法
3
+ */
4
+ import moment from 'moment';
5
+
6
+ export interface FilterOption {
7
+ value: string | number;
8
+ label: string;
9
+ /** 业务类型等接口项上的 apiKey,用于与 defaultBusiType 配置对齐 */
10
+ apiKey?: string;
11
+ }
12
+
13
+ /** 自定义日期区间:起止为 Unix 毫秒时间戳(日为本地 startOfDay / endOfDay) */
14
+ export interface TimestampRange {
15
+ start?: number;
16
+ end?: number;
17
+ }
18
+
19
+ /** 规范化下拉项的 value / label */
20
+ export function normalizeOptions(list?: FilterOption[]): FilterOption[] {
21
+ if (!Array.isArray(list) || list.length === 0) return [];
22
+ return list.map((o) => ({
23
+ value: o.value,
24
+ label: String(o.label ?? o.value),
25
+ }));
26
+ }
27
+
28
+ /** Changes Since 下拉默认项 */
29
+ export function defaultChangesSinceOptions(): FilterOption[] {
30
+ return [
31
+ { value: 'Start of the Period', label: 'Start of the Period' },
32
+ { value: 'Custom', label: 'Custom' },
33
+ ];
34
+ }
35
+
36
+ /** RangePicker 选中的两天转为起止时间戳(含当日) */
37
+ export function momentRangeToTimestamps(dates: any): TimestampRange | null {
38
+ const m0 = dates?.[0];
39
+ const m1 = dates?.[1];
40
+ if (!m0 || !m1) return null;
41
+ return {
42
+ start: moment(m0).startOf('day').valueOf(),
43
+ end: moment(m1).endOf('day').valueOf(),
44
+ };
45
+ }
46
+
47
+ /**
48
+ * 将 Close Date 相对周期编码转为本地自然日的起止时间戳(起点 0 点,终点 23:59:59.999)。
49
+ * 约定:百位 2=本周(iso 周)、3=本月、4=本季度(如 201/301/401 及同段其它编码)。
50
+ */
51
+ export function relativeCloseDateRangeFromCode(
52
+ encoded: number | string,
53
+ ): TimestampRange | null {
54
+ const curEncoded = typeof encoded === 'number' ? encoded : Number(encoded);
55
+ if (!Number.isFinite(curEncoded)) return null;
56
+ const now = moment();
57
+ if (curEncoded === 201) {
58
+ // This Week
59
+ return {
60
+ start: now.clone().startOf('isoWeek').startOf('day').valueOf(),
61
+ end: now.clone().endOf('isoWeek').endOf('day').valueOf(),
62
+ };
63
+ }
64
+ if (curEncoded === 301) {
65
+ // This Month(起点为月初次日 0 点)
66
+ return {
67
+ start: now
68
+ .clone()
69
+ .startOf('month')
70
+ .startOf('day')
71
+ .add(1, 'day')
72
+ .valueOf(),
73
+ end: now.clone().endOf('month').endOf('day').valueOf(),
74
+ };
75
+ }
76
+ if (curEncoded === 401) {
77
+ // This Quarter(起点为季首次日 0 点)
78
+ return {
79
+ start: now
80
+ .clone()
81
+ .startOf('quarter')
82
+ .startOf('day')
83
+ .add(1, 'day')
84
+ .valueOf(),
85
+ end: now.clone().endOf('quarter').endOf('day').valueOf(),
86
+ };
87
+ }
88
+ return null;
89
+ }
90
+
91
+ /** 将 user 实体查询结果转为负责人下拉项(按 id 去重) */
92
+ export function parseUserRecordsToOwnerOptions(records: any): FilterOption[] {
93
+ const arr = Array.isArray(records) ? records : [];
94
+ const seen = new Set<string>();
95
+ const out: FilterOption[] = [];
96
+ for (const row of arr) {
97
+ if (row == null || typeof row !== 'object') continue;
98
+ const id = row.id;
99
+ const name = row.name != null ? String(row.name) : '';
100
+ if (!id || !name || seen.has(id)) continue;
101
+ seen.add(id);
102
+ out.push({ value: id, label: name });
103
+ }
104
+ return out;
105
+ }
106
+
107
+ /** 从业务类型接口响应中取出列表数组 */
108
+ export function getBusinessTypeRawList(res: any): any[] {
109
+ const raw = res?.data !== undefined ? res.data : res;
110
+ const arr = Array.isArray(raw)
111
+ ? raw
112
+ : raw?.records || raw?.items || raw?.list || raw?.data || [];
113
+ return Array.isArray(arr) ? arr : [];
114
+ }
115
+
116
+ /** 解析业务类型接口结果为下拉选项(保留 apiKey 供默认项匹配) */
117
+ export function parseBusinessTypes(res: any): FilterOption[] {
118
+ const arr = getBusinessTypeRawList(res);
119
+ return arr
120
+ .map((x: any) => ({
121
+ value: x.id ?? x.apiKey ?? x.value,
122
+ label: String(x.label ?? x.name ?? x.title ?? x.apiKey ?? ''),
123
+ apiKey: x.apiKey != null ? String(x.apiKey) : undefined,
124
+ }))
125
+ .filter((o) => o.value != null && o.value !== '' && o.label);
126
+ }
127
+
128
+ /**
129
+ * 在业务类型选项中按 apiKey === defaultBusiType 解析默认选中值(与下拉 value 一致,一般为 id)
130
+ */
131
+ export function resolveDefaultBusinessTypeValue(
132
+ options: FilterOption[],
133
+ defaultBusiTypeKey?: string,
134
+ ): string | number {
135
+ if (defaultBusiTypeKey == null || defaultBusiTypeKey === '') return '';
136
+ const key = String(defaultBusiTypeKey);
137
+ const hit = options.find((o) => o.apiKey != null && o.apiKey === key);
138
+ if (!hit) return '';
139
+ return hit.value;
140
+ }