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,730 @@
1
+ /**
2
+ * @file 筛选栏组件
3
+ * @description 支持日期范围、负责人(user 实体)、业务类型等多维度筛选
4
+ */
5
+ import * as React from 'react';
6
+ import { DatePicker, Select, Spin } from 'antd';
7
+ import moment, { Moment } from 'moment';
8
+ // @ts-ignore
9
+ import { request, xObject } from 'neo-open-api';
10
+ // @ts-ignore
11
+ import { BaseCmp, NeoEvent } from 'neo-ui-common';
12
+
13
+ import { getDefaultOpportunityOwnerIds } from '../../utils/common';
14
+
15
+ import {
16
+ defaultChangesSinceOptions,
17
+ momentRangeToTimestamps,
18
+ normalizeOptions,
19
+ parseBusinessTypes,
20
+ parseUserRecordsToOwnerOptions,
21
+ relativeCloseDateRangeFromCode,
22
+ resolveDefaultBusinessTypeValue,
23
+ type FilterOption,
24
+ type TimestampRange,
25
+ } from '../../utils/filterBar';
26
+
27
+ export type { FilterOption, TimestampRange } from '../../utils/filterBar';
28
+
29
+ import './common.scss';
30
+ import './style.scss';
31
+
32
+ const { RangePicker } = DatePicker;
33
+
34
+ const BUSI_TYPE_URL = '/rest/data/v2.0/xobjects/opportunity/busiType';
35
+
36
+ /** Opportunity Owner 人员列表:平台 user 实体 */
37
+ const USER_ENTITY_API_KEY = 'user';
38
+ const USER_QUERY_FIELDS = [
39
+ 'id',
40
+ 'name',
41
+ 'nickName',
42
+ 'personalEmail',
43
+ 'telephone',
44
+ ] as const;
45
+
46
+ interface FilterBarEventData {
47
+ data: FilterBarChangePayload;
48
+ }
49
+
50
+ export interface FilterBarChangePayload {
51
+ closeDate: number | string;
52
+ closeDateCustomRange?: TimestampRange;
53
+ /** Opportunity Owner 多选:人员 id 列表,空数组表示未选 */
54
+ opportunityOwner: (number | string)[];
55
+ businessType: string | number;
56
+ /** 当前选中的业务类型展示名(与 businessType 对应) */
57
+ businessTypeLabel?: string;
58
+ businessTypeApiKey?: string;
59
+ changesSince: string | number;
60
+ /** Changes Since 为 Custom 时:所选日期的当日 0 点 Unix 毫秒时间戳 */
61
+ changesSinceCustomTime?: number;
62
+ }
63
+
64
+ /** 设计器 / 页面注入的当前登录用户(与 __NeoCurrentUser 一致) */
65
+ interface NeoCurrentUser {
66
+ id: number;
67
+ name: string;
68
+ icon?: string;
69
+ departId?: number;
70
+ localCode?: string;
71
+ type?: number;
72
+ languageCode?: string;
73
+ currencyApiKey?: string;
74
+ }
75
+
76
+ interface FilterBarProps {
77
+ /** Close Date 下拉选项(由页面 / 设计器配置传入) */
78
+ closeDateOptions?: FilterOption[];
79
+ /**
80
+ * 默认业务类型:与接口返回项的 apiKey 相等时,将该项的 id(或 value)作为 Business Type 初始选中值
81
+ */
82
+ defaultBusiType?: string;
83
+ className?: string;
84
+ style?: React.CSSProperties;
85
+ /** 与 NeoEvent 并存:便于父组件直接监听 */
86
+ onValuesChange?: (payload: FilterBarChangePayload) => void;
87
+ /** 页面数据:含 __NeoCurrentUser,用于负责人列表首位插入当前用户 */
88
+ data?: { __NeoCurrentUser?: NeoCurrentUser };
89
+ }
90
+
91
+ interface FilterValues {
92
+ closeDate: number | string;
93
+ businessType: string | number;
94
+ changesSince: string | number;
95
+ }
96
+
97
+ interface FilterBarState {
98
+ closeDateOptions: FilterOption[];
99
+ changesSinceOptions: FilterOption[];
100
+ opportunityOwnerOptions: FilterOption[];
101
+ businessTypeOptions: FilterOption[];
102
+ values: FilterValues;
103
+ /** Opportunity Owner 选中的人员 id(多选) */
104
+ opportunityOwner: (number | string)[];
105
+ /** Close Date 为 custom 时的自定义区间时间戳 */
106
+ closeDateCustomRange: TimestampRange | null;
107
+ /** Changes Since 为 Custom 时的单个时间点(当日 0 点,Unix 毫秒) */
108
+ changesSinceCustomTime: number | null;
109
+ ownerLoading: boolean;
110
+ businessTypeLoading: boolean;
111
+ }
112
+
113
+ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
114
+ /** 初始化筛选 state 与 Close Date 选项 */
115
+ constructor(props: FilterBarProps) {
116
+ super(props);
117
+ const closeOpts = normalizeOptions(
118
+ props.closeDateOptions && props.closeDateOptions.length > 0
119
+ ? props.closeDateOptions
120
+ : FilterBar.defaultCloseDateOptions(),
121
+ );
122
+ const csOpts = defaultChangesSinceOptions();
123
+ const initialClose = closeOpts[2]?.value ?? 401; // 默认本季度
124
+ const initialRelativeRange = relativeCloseDateRangeFromCode(initialClose);
125
+ this.state = {
126
+ closeDateOptions: closeOpts,
127
+ changesSinceOptions: csOpts,
128
+ opportunityOwnerOptions: [],
129
+ businessTypeOptions: [],
130
+ values: {
131
+ closeDate: initialClose,
132
+ businessType: '',
133
+ changesSince: csOpts[0]?.value ?? '',
134
+ },
135
+ opportunityOwner: getDefaultOpportunityOwnerIds(props),
136
+ closeDateCustomRange: initialRelativeRange,
137
+ changesSinceCustomTime: initialRelativeRange?.start ?? null,
138
+ ownerLoading: false,
139
+ businessTypeLoading: false,
140
+ };
141
+
142
+ // 绑定方法到 this
143
+ this.syncDefaultBusinessTypeFromProps =
144
+ this.syncDefaultBusinessTypeFromProps.bind(this);
145
+ this.syncCloseDateOptionsFromProps =
146
+ this.syncCloseDateOptionsFromProps.bind(this);
147
+ this.buildPayload = this.buildPayload.bind(this);
148
+ this.emitChange = this.emitChange.bind(this);
149
+ this.loadOpportunityOwnerOptions =
150
+ this.loadOpportunityOwnerOptions.bind(this);
151
+ this.loadBusinessTypeOptions = this.loadBusinessTypeOptions.bind(this);
152
+ this.patchValues = this.patchValues.bind(this);
153
+ this.handleCloseDateChange = this.handleCloseDateChange.bind(this);
154
+ this.handleChangesSinceChange = this.handleChangesSinceChange.bind(this);
155
+ this.handleCloseDateRangeChange =
156
+ this.handleCloseDateRangeChange.bind(this);
157
+ this.handleChangesSinceTimeChange =
158
+ this.handleChangesSinceTimeChange.bind(this);
159
+ this.closeDateRangePickerValue = this.closeDateRangePickerValue.bind(this);
160
+ this.changesSinceDatePickerValue =
161
+ this.changesSinceDatePickerValue.bind(this);
162
+ this.getFilters = this.getFilters.bind(this);
163
+ this.resetFilters = this.resetFilters.bind(this);
164
+ this.renderCloseDateBlock = this.renderCloseDateBlock.bind(this);
165
+ this.renderOwnerBlock = this.renderOwnerBlock.bind(this);
166
+ this.renderBusinessTypeBlock = this.renderBusinessTypeBlock.bind(this);
167
+ this.renderChangesSinceBlock = this.renderChangesSinceBlock.bind(this);
168
+ }
169
+
170
+ /** Close Date 内置默认选项 */
171
+ static defaultCloseDateOptions(): FilterOption[] {
172
+ return [
173
+ { value: 201, label: 'This Week' },
174
+ { value: 301, label: 'This Month' },
175
+ { value: 401, label: 'This Quarter' },
176
+ { value: 'custom', label: 'Custom' },
177
+ ];
178
+ }
179
+
180
+ /** 同步 props 选项并拉取负责人、业务类型 */
181
+ componentDidMount() {
182
+ this.syncCloseDateOptionsFromProps(this.props);
183
+ this.loadOpportunityOwnerOptions();
184
+ this.loadBusinessTypeOptions();
185
+ }
186
+
187
+ /** props.closeDateOptions 变化时同步本地选项 */
188
+ componentWillReceiveProps(nextProps: FilterBarProps) {
189
+ if (nextProps.closeDateOptions !== this.props.closeDateOptions) {
190
+ this.syncCloseDateOptionsFromProps(nextProps);
191
+ }
192
+ if (nextProps.defaultBusiType !== this.props.defaultBusiType) {
193
+ this.syncDefaultBusinessTypeFromProps(nextProps);
194
+ }
195
+ const nextOwnerIds = getDefaultOpportunityOwnerIds(nextProps);
196
+ const prevUserId = this.props.data?.__NeoCurrentUser?.id;
197
+ const nextUserId = nextProps.data?.__NeoCurrentUser?.id;
198
+ if (
199
+ nextOwnerIds.length > 0 &&
200
+ prevUserId !== nextUserId &&
201
+ this.state.opportunityOwner.length === 0
202
+ ) {
203
+ this.setState({ opportunityOwner: nextOwnerIds }, () =>
204
+ this.emitChange(),
205
+ );
206
+ }
207
+ }
208
+
209
+ /** defaultBusiType 配置变更时,在已加载的下拉数据中重新匹配默认业务类型 */
210
+ syncDefaultBusinessTypeFromProps(props: FilterBarProps) {
211
+ const { businessTypeOptions } = this.state;
212
+ if (!businessTypeOptions.length) return;
213
+ const nextVal = resolveDefaultBusinessTypeValue(
214
+ businessTypeOptions,
215
+ props.defaultBusiType,
216
+ );
217
+ this.setState(
218
+ (prev) => ({
219
+ values: { ...prev.values, businessType: nextVal },
220
+ }),
221
+ () => this.emitChange(),
222
+ );
223
+ }
224
+
225
+ /** 用 props 更新 Close Date 选项并校正当前选中值 */
226
+ syncCloseDateOptionsFromProps(props: FilterBarProps) {
227
+ const next = normalizeOptions(
228
+ props.closeDateOptions && props.closeDateOptions.length > 0
229
+ ? props.closeDateOptions
230
+ : FilterBar.defaultCloseDateOptions(),
231
+ );
232
+ this.setState((prev) => {
233
+ const stillValid = next.some((o) => o.value === prev.values.closeDate);
234
+ const closeDate = stillValid
235
+ ? prev.values.closeDate
236
+ : next[0]?.value ?? '';
237
+ const isCustom = String(closeDate).toLowerCase() === 'custom';
238
+ const relativeRange = !isCustom
239
+ ? relativeCloseDateRangeFromCode(closeDate)
240
+ : null;
241
+ return {
242
+ closeDateOptions: next,
243
+ values: { ...prev.values, closeDate },
244
+ closeDateCustomRange: isCustom
245
+ ? prev.closeDateCustomRange
246
+ : relativeRange,
247
+ changesSinceCustomTime:
248
+ prev.values.changesSince === 'Custom'
249
+ ? prev.changesSinceCustomTime
250
+ : relativeRange?.start ?? null,
251
+ };
252
+ });
253
+ }
254
+
255
+ /** 由 state 组装对外事件 / 回调用的 payload */
256
+ buildPayload(): FilterBarChangePayload {
257
+ const {
258
+ values,
259
+ opportunityOwner,
260
+ closeDateCustomRange,
261
+ changesSinceCustomTime,
262
+ businessTypeOptions,
263
+ } = this.state;
264
+ const closeCustom = String(values.closeDate).toLowerCase() === 'custom';
265
+ const changesCustom = values.changesSince === 'Custom';
266
+
267
+ const rangeResolved = closeCustom
268
+ ? closeDateCustomRange
269
+ : closeDateCustomRange ??
270
+ relativeCloseDateRangeFromCode(values.closeDate);
271
+
272
+ const curBusinessType =
273
+ values.businessType === '' || values.businessType == null
274
+ ? undefined
275
+ : businessTypeOptions.find((o) => o.value === values.businessType);
276
+
277
+ return {
278
+ closeDate: values.closeDate,
279
+ closeDateCustomRange: rangeResolved ?? undefined,
280
+ opportunityOwner,
281
+ businessType: values.businessType,
282
+ businessTypeLabel: curBusinessType?.label,
283
+ businessTypeApiKey: curBusinessType?.apiKey,
284
+ changesSince: values.changesSince,
285
+ changesSinceCustomTime: changesCustom
286
+ ? changesSinceCustomTime ?? undefined
287
+ : rangeResolved?.start != null
288
+ ? rangeResolved.start
289
+ : undefined,
290
+ };
291
+ }
292
+
293
+ /** 打日志、触发 onValuesChange 与设计器事件 */
294
+ emitChange() {
295
+ const payload = this.buildPayload();
296
+ console.log('[FilterBar__c] filters change', payload);
297
+ const { onValuesChange } = this.props;
298
+ if (onValuesChange) {
299
+ onValuesChange(payload);
300
+ }
301
+ this.onFiltersChange({
302
+ data: payload,
303
+ });
304
+
305
+ /*
306
+ console.log('触发了一个广播事件 updateFilterData:', payload);
307
+ NeoEvent.broadcast('updateFilterData', payload);
308
+ */
309
+ }
310
+
311
+ /** 设计器事件:筛选条件变化(含自定义区间) */
312
+ @NeoEvent.dispatch
313
+ onFiltersChange(eventData?: FilterBarEventData) {}
314
+
315
+ /** 请求 user 实体,填充 Opportunity Owner 下拉 */
316
+ async loadOpportunityOwnerOptions() {
317
+ this.setState({ ownerLoading: true });
318
+ const currentUser = Object.assign({}, this.props.data?.__NeoCurrentUser);
319
+ // 给当前用户增加标记
320
+ if (
321
+ currentUser &&
322
+ currentUser.name &&
323
+ currentUser.name.indexOf('(CurrentUser)') === -1
324
+ ) {
325
+ currentUser.name = currentUser.name + '(CurrentUser)';
326
+ }
327
+ const currentUserRecords =
328
+ currentUser?.id != null &&
329
+ currentUser.name != null &&
330
+ String(currentUser.name).trim() !== ''
331
+ ? [currentUser]
332
+ : [];
333
+
334
+ try {
335
+ const result = await xObject.query({
336
+ xObjectApiKey: USER_ENTITY_API_KEY,
337
+ fields: [...USER_QUERY_FIELDS],
338
+ page: 1,
339
+ pageSize: 500,
340
+ });
341
+
342
+ let opportunityOwnerOptions: FilterOption[] = [];
343
+ if (result?.status) {
344
+ const records = Array.isArray(result.data) ? result.data : [];
345
+ opportunityOwnerOptions = parseUserRecordsToOwnerOptions([
346
+ ...currentUserRecords,
347
+ ...records,
348
+ ]);
349
+ } else {
350
+ console.warn(
351
+ 'FilterBar xObject.query(user) 非成功:',
352
+ result?.msg ?? result,
353
+ );
354
+ opportunityOwnerOptions =
355
+ parseUserRecordsToOwnerOptions(currentUserRecords);
356
+ }
357
+
358
+ const defaultOwnerIds = getDefaultOpportunityOwnerIds(this.props);
359
+ let appliedOwnerDefault = false;
360
+ this.setState(
361
+ (prev) => {
362
+ const shouldApply =
363
+ defaultOwnerIds.length > 0 && prev.opportunityOwner.length === 0;
364
+ if (shouldApply) appliedOwnerDefault = true;
365
+ return {
366
+ ownerLoading: false,
367
+ opportunityOwnerOptions,
368
+ opportunityOwner: shouldApply
369
+ ? defaultOwnerIds
370
+ : prev.opportunityOwner,
371
+ };
372
+ },
373
+ () => {
374
+ if (appliedOwnerDefault) this.emitChange();
375
+ },
376
+ );
377
+ } catch (e) {
378
+ console.error('FilterBar 加载负责人(user)失败:', e);
379
+ const fallbackOptions =
380
+ parseUserRecordsToOwnerOptions(currentUserRecords);
381
+ const defaultOwnerIds = getDefaultOpportunityOwnerIds(this.props);
382
+ let appliedOwnerDefault = false;
383
+ this.setState(
384
+ (prev) => {
385
+ const shouldApply =
386
+ defaultOwnerIds.length > 0 && prev.opportunityOwner.length === 0;
387
+ if (shouldApply) appliedOwnerDefault = true;
388
+ return {
389
+ ownerLoading: false,
390
+ opportunityOwnerOptions: fallbackOptions,
391
+ opportunityOwner: shouldApply
392
+ ? defaultOwnerIds
393
+ : prev.opportunityOwner,
394
+ };
395
+ },
396
+ () => {
397
+ if (appliedOwnerDefault) this.emitChange();
398
+ },
399
+ );
400
+ }
401
+ }
402
+
403
+ /** 请求商机业务类型接口,填充 Business Type 下拉 */
404
+ async loadBusinessTypeOptions() {
405
+ this.setState({ businessTypeLoading: true });
406
+ try {
407
+ const res = await request({
408
+ url: BUSI_TYPE_URL,
409
+ method: 'GET',
410
+ data: {},
411
+ });
412
+ const businessTypeOptions = parseBusinessTypes(res);
413
+ const defaultBt = resolveDefaultBusinessTypeValue(
414
+ businessTypeOptions,
415
+ this.props.defaultBusiType,
416
+ );
417
+ this.setState(
418
+ (prev) => ({
419
+ businessTypeLoading: false,
420
+ businessTypeOptions,
421
+ values: {
422
+ ...prev.values,
423
+ businessType: defaultBt,
424
+ },
425
+ }),
426
+ () => {
427
+ if (defaultBt) this.emitChange();
428
+ },
429
+ );
430
+ } catch (e) {
431
+ console.error('FilterBar 加载业务类型失败:', e);
432
+ this.setState({ businessTypeLoading: false, businessTypeOptions: [] });
433
+ }
434
+ }
435
+
436
+ /** 合并 values 子集并通知变更 */
437
+ patchValues(patch: Partial<FilterValues>) {
438
+ this.setState(
439
+ (prev) =>
440
+ ({
441
+ values: { ...prev.values, ...patch },
442
+ } as Pick<FilterBarState, 'values'>),
443
+ () => this.emitChange(),
444
+ );
445
+ }
446
+
447
+ /** Close Date 下拉变更;非 custom 时按编码写入相对周期的起止时间戳及起点到 changesSinceCustomTime */
448
+ handleCloseDateChange(value: number | string) {
449
+ if (String(value).toLowerCase() !== 'custom') {
450
+ const relativeRange = relativeCloseDateRangeFromCode(value);
451
+ this.setState(
452
+ (prev) =>
453
+ ({
454
+ values: { ...prev.values, closeDate: value },
455
+ closeDateCustomRange: relativeRange,
456
+ changesSinceCustomTime:
457
+ prev.values.changesSince === 'Custom'
458
+ ? prev.changesSinceCustomTime
459
+ : relativeRange?.start ?? null,
460
+ } as Pick<
461
+ FilterBarState,
462
+ 'values' | 'closeDateCustomRange' | 'changesSinceCustomTime'
463
+ >),
464
+ () => this.emitChange(),
465
+ );
466
+ return;
467
+ }
468
+ this.setState(
469
+ (prev) => ({
470
+ values: { ...prev.values, closeDate: value },
471
+ closeDateCustomRange: null,
472
+ changesSinceCustomTime:
473
+ prev.values.changesSince === 'Custom'
474
+ ? prev.changesSinceCustomTime
475
+ : null,
476
+ }),
477
+ () => this.emitChange(),
478
+ );
479
+ }
480
+
481
+ /** Changes Since 下拉变更;非 Custom 且 Close Date 为相对周期时同步起点时间戳 */
482
+ handleChangesSinceChange(value: string | number) {
483
+ if (value !== 'Custom') {
484
+ const closeDate = this.state.values.closeDate;
485
+ const closeCustom = String(closeDate).toLowerCase() === 'custom';
486
+ const relativeRange = !closeCustom
487
+ ? relativeCloseDateRangeFromCode(closeDate)
488
+ : null;
489
+ this.setState(
490
+ (prev) =>
491
+ ({
492
+ values: { ...prev.values, changesSince: value },
493
+ changesSinceCustomTime: relativeRange?.start ?? null,
494
+ } as Pick<FilterBarState, 'values' | 'changesSinceCustomTime'>),
495
+ () => this.emitChange(),
496
+ );
497
+ return;
498
+ }
499
+ this.setState(
500
+ (prev) => ({
501
+ values: { ...prev.values, changesSince: value },
502
+ changesSinceCustomTime: null,
503
+ }),
504
+ () => this.emitChange(),
505
+ );
506
+ }
507
+
508
+ /** Close Date 自定义 RangePicker:写入时间戳区间 */
509
+ handleCloseDateRangeChange(dates: any) {
510
+ const closeDateCustomRange = momentRangeToTimestamps(dates);
511
+ this.setState({ closeDateCustomRange }, () => this.emitChange());
512
+ }
513
+
514
+ /** Changes Since 自定义 DatePicker:写入当日 0 点时间戳 */
515
+ handleChangesSinceTimeChange(date: Moment | null) {
516
+ const changesSinceCustomTime =
517
+ date != null ? moment(date).startOf('day').valueOf() : null;
518
+ this.setState({ changesSinceCustomTime }, () => this.emitChange());
519
+ }
520
+
521
+ /** state.closeDateCustomRange → RangePicker 受控值 */
522
+ closeDateRangePickerValue(): [Moment | null, Moment | null] | null {
523
+ const r = this.state.closeDateCustomRange;
524
+ if (!r || (r.start == null && r.end == null)) return null;
525
+ return [
526
+ r.start != null ? moment(r.start) : null,
527
+ r.end != null ? moment(r.end) : null,
528
+ ];
529
+ }
530
+
531
+ /** state.changesSinceCustomTime → DatePicker 受控值 */
532
+ changesSinceDatePickerValue(): Moment | null {
533
+ const t = this.state.changesSinceCustomTime;
534
+ if (t == null) return null;
535
+ return moment(t);
536
+ }
537
+
538
+ /** 动作流 / 外部调用:返回当前筛选快照 */
539
+ @NeoEvent.function
540
+ getFilters(): FilterBarChangePayload {
541
+ return this.buildPayload();
542
+ }
543
+
544
+ /** 动作流:恢复默认并清空自定义区间后通知 */
545
+ @NeoEvent.function
546
+ resetFilters() {
547
+ const { closeDateOptions, changesSinceOptions } = this.state;
548
+ const resetClose = closeDateOptions[0]?.value ?? '';
549
+ const resetCustom = String(resetClose).toLowerCase() === 'custom';
550
+ const resetRange = !resetCustom
551
+ ? relativeCloseDateRangeFromCode(resetClose)
552
+ : null;
553
+ this.setState(
554
+ {
555
+ values: {
556
+ closeDate: resetClose,
557
+ businessType: '',
558
+ changesSince: changesSinceOptions[0]?.value ?? '',
559
+ },
560
+ opportunityOwner: getDefaultOpportunityOwnerIds(this.props),
561
+ closeDateCustomRange: resetRange,
562
+ changesSinceCustomTime: resetRange?.start ?? null,
563
+ },
564
+ () => this.emitChange(),
565
+ );
566
+ }
567
+
568
+ /** Close Date + 自定义日期区间 */
569
+ renderCloseDateBlock() {
570
+ const { closeDateOptions, values } = this.state;
571
+ const showCustom = String(values.closeDate).toLowerCase() === 'custom';
572
+
573
+ return (
574
+ <div className="filter-block" key="closeDate">
575
+ <div className="filter-field">
576
+ <label>Close Date</label>
577
+ <Select
578
+ className="filter-select"
579
+ value={values.closeDate || undefined}
580
+ onChange={(v) => this.handleCloseDateChange(v)}
581
+ options={closeDateOptions.map((o) => ({
582
+ value: o.value,
583
+ label: o.label,
584
+ }))}
585
+ />
586
+ </div>
587
+ {showCustom && (
588
+ <div className="filter-field filter-field-range">
589
+ <RangePicker
590
+ value={this.closeDateRangePickerValue()}
591
+ onChange={this.handleCloseDateRangeChange}
592
+ format="YYYY-MM-DD"
593
+ />
594
+ </div>
595
+ )}
596
+ </div>
597
+ );
598
+ }
599
+
600
+ /** Opportunity Owner */
601
+ renderOwnerBlock() {
602
+ const { opportunityOwnerOptions, ownerLoading, opportunityOwner } =
603
+ this.state;
604
+
605
+ return (
606
+ <div className="filter-block" key="owner">
607
+ <div className="filter-field">
608
+ <label>Opportunity Owner</label>
609
+ <Spin spinning={ownerLoading}>
610
+ <div className="filter-select-spin-wrap">
611
+ <Select
612
+ className="filter-select filter-select-wide"
613
+ mode="multiple"
614
+ allowClear
615
+ showSearch
616
+ placeholder="Please select the owner"
617
+ value={opportunityOwner}
618
+ optionFilterProp="label"
619
+ maxTagCount={2}
620
+ maxTagPlaceholder={(omitted) => `+${omitted.length}`}
621
+ onChange={(v) =>
622
+ this.setState({ opportunityOwner: v ?? [] }, () =>
623
+ this.emitChange(),
624
+ )
625
+ }
626
+ dropdownMatchSelectWidth={320}
627
+ options={opportunityOwnerOptions.map((o) => ({
628
+ value: o.value,
629
+ label: o.label,
630
+ }))}
631
+ />
632
+ </div>
633
+ </Spin>
634
+ </div>
635
+ </div>
636
+ );
637
+ }
638
+
639
+ /** Business Type */
640
+ renderBusinessTypeBlock() {
641
+ const { businessTypeOptions, businessTypeLoading, values } = this.state;
642
+
643
+ return (
644
+ <div className="filter-block" key="businessType">
645
+ <div className="filter-field">
646
+ <label>Business Type</label>
647
+ <Spin spinning={businessTypeLoading}>
648
+ <div className="filter-select-spin-wrap">
649
+ <Select
650
+ className="filter-select"
651
+ allowClear
652
+ showSearch
653
+ placeholder="Please select the business type"
654
+ value={values.businessType || undefined}
655
+ optionFilterProp="label"
656
+ onChange={(v) => this.patchValues({ businessType: v || '' })}
657
+ options={businessTypeOptions.map((o) => ({
658
+ value: o.value,
659
+ label: o.label,
660
+ }))}
661
+ />
662
+ </div>
663
+ </Spin>
664
+ </div>
665
+ </div>
666
+ );
667
+ }
668
+
669
+ /** Changes Since + Custom 时单个日期 */
670
+ renderChangesSinceBlock() {
671
+ const { changesSinceOptions, values } = this.state;
672
+ const showCustom = values.changesSince === 'Custom';
673
+
674
+ return (
675
+ <div className="filter-block" key="changesSince">
676
+ <div className="filter-field">
677
+ <label>
678
+ Changes Since
679
+ <span className="help-tip">
680
+ ?
681
+ <span className="help-tip-text">
682
+ 相对报表周期的变更起始时间;Custom 时可自选日期
683
+ </span>
684
+ </span>
685
+ </label>
686
+ <Select
687
+ className="filter-select"
688
+ value={values.changesSince || undefined}
689
+ onChange={(v) => this.handleChangesSinceChange(v)}
690
+ options={changesSinceOptions.map((o) => ({
691
+ value: o.value,
692
+ label: o.label,
693
+ }))}
694
+ />
695
+ </div>
696
+ {showCustom && (
697
+ <div className="filter-field filter-field-range">
698
+ <DatePicker
699
+ value={this.changesSinceDatePickerValue()}
700
+ onChange={this.handleChangesSinceTimeChange}
701
+ format="YYYY-MM-DD"
702
+ placeholder="Select date"
703
+ />
704
+ </div>
705
+ )}
706
+ </div>
707
+ );
708
+ }
709
+
710
+ /** 筛选栏根布局 */
711
+ render() {
712
+ const { className, style } = this.props;
713
+ console.log('[FilterBar__c] render', this.props);
714
+
715
+ return (
716
+ <div
717
+ className={`filterBar__c ${className || ''}`}
718
+ style={style}
719
+ data-time="2026.4.15 01"
720
+ >
721
+ {this.renderCloseDateBlock()}
722
+ {this.renderOwnerBlock()}
723
+ {this.renderBusinessTypeBlock()}
724
+ {this.renderChangesSinceBlock()}
725
+ </div>
726
+ );
727
+ }
728
+ }
729
+
730
+ export default FilterBar;