neo-cmp-cli 1.13.17 → 1.13.19
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.
- package/README.md +23 -3
- package/dist/index2.js +1 -1
- package/dist/neo/env.js +1 -1
- package/dist/neo/pushCmp.js +1 -1
- package/dist/package.json.js +1 -1
- package/package.json +3 -2
- package/template/asset-manage-template/docs/README.md +1 -232
- package/template/echarts-custom-cmp-template/package.json +1 -1
- package/template/neo-bi-cmps/package.json +1 -1
- package/template/neo-bi-cmps/src/components/filterBar__c/common.scss +1 -1
- package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +18 -10
- package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +8 -2
- package/template/neo-bi-cmps/src/components/targetNumber__c/model.ts +1 -1
- package/template/neo-bi-cmps/src/utils/common.ts +18 -20
- package/template/neo-bi-cmps/src/utils/filter2chartFilter.ts +4 -6
- package/template/neo-bi-cmps/src/utils/pipelineFunnel.ts +4 -2
- package/template/neo-bi-cmps/src/utils/simpleTable.tsx +21 -16
- package/template/neo-custom-cmp-template/docs/README.md +0 -231
- package/template/neo-custom-cmp-template/package.json +1 -1
- package/template/neo-h5-cmps/src/components/entityList__c/index.tsx +1 -2
- package/template/neo-h5-cmps/src/components/entityTabs__c/index.tsx +1 -1
- package/template/neo-h5-cmps/src/components/globalSearchInput__c/index.tsx +1 -1
- package/template/neo-h5-cmps/src/components/openChatPageBtn__c/index.tsx +1 -2
- package/template/neo-pipeline-cmps/neo.config.js +11 -0
- package/template/neo-pipeline-cmps/src/assets/css/common.scss +16 -16
- package/template/neo-pipeline-cmps/src/assets/css/mixin.scss +5 -5
- package/template/neo-pipeline-cmps/src/components/filterBar__c/README.md +9 -9
- package/template/neo-pipeline-cmps/src/components/filterBar__c/common.scss +5 -5
- package/template/neo-pipeline-cmps/src/components/filterBar__c/index.tsx +47 -46
- package/template/neo-pipeline-cmps/src/components/filterBar__c/model.ts +22 -12
- package/template/neo-pipeline-cmps/src/components/filterBar__c/style.scss +1 -1
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/README.md +17 -17
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/index.tsx +24 -22
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +31 -18
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/reset.scss +4 -0
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/index.tsx +33 -26
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/model.ts +9 -9
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/README.md +53 -54
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/common.scss +5 -5
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/index.tsx +70 -68
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/model.ts +41 -41
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/style.scss +2 -3
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/README.md +15 -15
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/index.tsx +35 -33
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/model.ts +29 -16
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/README.md +18 -18
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/index.tsx +20 -20
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/model.ts +34 -19
- package/template/neo-pipeline-cmps/src/utils/common.ts +14 -14
- package/template/neo-pipeline-cmps/src/utils/filter2chartFilter.ts +21 -23
- package/template/neo-pipeline-cmps/src/utils/filterBar.ts +14 -14
- package/template/neo-pipeline-cmps/src/utils/pipelineFunnel.ts +5 -5
- package/template/neo-pipeline-cmps/src/utils/queryByCustomSQL.ts +26 -22
- package/template/neo-pipeline-cmps/src/utils/requestDebounce.ts +3 -3
- package/template/neo-pipeline-cmps/src/utils/simpleTable.tsx +31 -26
- package/template/neo-pipeline-cmps/src/utils/stageSwitch.ts +1 -1
- package/template/neo-pipeline-cmps/src/utils/stageTimeChart.ts +5 -5
- package/template/neo-pipeline-cmps/src/utils/targetNumber.ts +2 -2
- package/template/neo-web-entity-grid/src/components/createForm__c/index.tsx +271 -259
- package/template/neo-web-entity-grid/src/components/createForm__c/model.ts +17 -3
- package/template/neo-web-entity-grid/src/components/createForm__c/resetAntd.scss +0 -1
- package/template/neo-web-entity-grid/src/components/createForm__c/style.scss +1 -1
- package/template/neo-web-entity-grid/src/components/entityGrid2__c/index.tsx +5 -1
- package/template/neo-web-entity-grid/src/components/entityGrid2__c/model.ts +4 -3
- package/template/neo-web-entity-grid/src/components/entityGrid3__c/index.tsx +1 -1
- package/template/neo-web-entity-grid/src/components/searchForm__c/index.tsx +4 -3
- package/template/neo-web-entity-grid/src/components/searchForm__c/model.ts +9 -4
- package/template/neo-web-entity-grid/src/components/searchForm__c/style.scss +2 -1
- package/template/neo-web-form/package.json +1 -1
- package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +179 -59
- package/template/neo-web-form/src/components/batchAddTable__c/model.ts +12 -14
- package/template/neo-web-form/src/components/listSummary__c/index.tsx +6 -5
- package/template/react-custom-cmp-template/package.json +1 -1
- package/template/asset-manage-template/src/utils/axiosFetcher.ts +0 -37
- package/template/asset-manage-template/src/utils/queryObjectData.ts +0 -112
- package/template/asset-manage-template/src/utils/xobjects.ts +0 -162
- package/template/neo-custom-cmp-template/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-custom-cmp-template/src/utils/queryObjectData.ts +0 -112
- package/template/neo-custom-cmp-template/src/utils/xobjects.ts +0 -162
- package/template/neo-h5-cmps/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-h5-cmps/src/utils/queryObjectData.ts +0 -112
- package/template/neo-h5-cmps/src/utils/xobjects.ts +0 -167
- package/template/neo-order-cmps/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-order-cmps/src/utils/queryObjectData.ts +0 -112
- package/template/neo-order-cmps/src/utils/xobjects.ts +0 -162
- package/template/neo-web-entity-grid/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-web-entity-grid/src/utils/queryObjectData.ts +0 -112
- package/template/neo-web-entity-grid/src/utils/xobjects.ts +0 -167
- package/template/neo-web-form/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-web-form/src/utils/queryObjectData.ts +0 -112
- package/template/neo-web-form/src/utils/xobjects.ts +0 -167
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file
|
|
3
|
-
* @description
|
|
2
|
+
* @file Filter Bar Component
|
|
3
|
+
* @description Supports multi-dimensional filtering by date range, owner (user entity), business type, etc.
|
|
4
4
|
*/
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { DatePicker, Select, Spin } from 'antd';
|
|
@@ -33,7 +33,7 @@ const { RangePicker } = DatePicker;
|
|
|
33
33
|
|
|
34
34
|
const BUSI_TYPE_URL = '/rest/data/v2.0/xobjects/opportunity/busiType';
|
|
35
35
|
|
|
36
|
-
/** Opportunity Owner
|
|
36
|
+
/** Opportunity Owner personnel list: platform user entity */
|
|
37
37
|
const USER_ENTITY_API_KEY = 'user';
|
|
38
38
|
const USER_QUERY_FIELDS = [
|
|
39
39
|
'id',
|
|
@@ -50,18 +50,18 @@ interface FilterBarEventData {
|
|
|
50
50
|
export interface FilterBarChangePayload {
|
|
51
51
|
closeDate: number | string;
|
|
52
52
|
closeDateCustomRange?: TimestampRange;
|
|
53
|
-
/** Opportunity Owner
|
|
53
|
+
/** Opportunity Owner multi-select: personnel id list, empty array means none selected */
|
|
54
54
|
opportunityOwner: (number | string)[];
|
|
55
55
|
businessType: string | number;
|
|
56
|
-
/**
|
|
56
|
+
/** Current business type display name (corresponding to businessType) */
|
|
57
57
|
businessTypeLabel?: string;
|
|
58
58
|
businessTypeApiKey?: string;
|
|
59
59
|
changesSince: string | number;
|
|
60
|
-
/** Changes Since
|
|
60
|
+
/** Changes Since Custom: selected date's midnight Unix millisecond timestamp */
|
|
61
61
|
changesSinceCustomTime?: number;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
/**
|
|
64
|
+
/** Designer / page-injected current logged-in user (consistent with __NeoCurrentUser) */
|
|
65
65
|
interface NeoCurrentUser {
|
|
66
66
|
id: number;
|
|
67
67
|
name: string;
|
|
@@ -74,17 +74,17 @@ interface NeoCurrentUser {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
interface FilterBarProps {
|
|
77
|
-
/** Close Date
|
|
77
|
+
/** Close Date dropdown options (passed in from page / designer configuration) */
|
|
78
78
|
closeDateOptions?: FilterOption[];
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
80
|
+
* Default business type: when matching the apiKey of an item returned by the API, use that item's id (or value) as the initial Business Type selection
|
|
81
81
|
*/
|
|
82
82
|
defaultBusiType?: string;
|
|
83
83
|
className?: string;
|
|
84
84
|
style?: React.CSSProperties;
|
|
85
|
-
/**
|
|
85
|
+
/** Coexists with NeoEvent: allows parent component to listen directly */
|
|
86
86
|
onValuesChange?: (payload: FilterBarChangePayload) => void;
|
|
87
|
-
/**
|
|
87
|
+
/** Page data: contains __NeoCurrentUser, used to insert current user at the top of the owner list */
|
|
88
88
|
data?: { __NeoCurrentUser?: NeoCurrentUser };
|
|
89
89
|
}
|
|
90
90
|
|
|
@@ -100,18 +100,18 @@ interface FilterBarState {
|
|
|
100
100
|
opportunityOwnerOptions: FilterOption[];
|
|
101
101
|
businessTypeOptions: FilterOption[];
|
|
102
102
|
values: FilterValues;
|
|
103
|
-
/** Opportunity Owner
|
|
103
|
+
/** Opportunity Owner selected personnel ids (multi-select) */
|
|
104
104
|
opportunityOwner: (number | string)[];
|
|
105
|
-
/** Close Date
|
|
105
|
+
/** Custom date range timestamps when Close Date is 'custom' */
|
|
106
106
|
closeDateCustomRange: TimestampRange | null;
|
|
107
|
-
/** Changes Since
|
|
107
|
+
/** Single time point when Changes Since is Custom (midnight, Unix ms) */
|
|
108
108
|
changesSinceCustomTime: number | null;
|
|
109
109
|
ownerLoading: boolean;
|
|
110
110
|
businessTypeLoading: boolean;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
114
|
-
/**
|
|
114
|
+
/** Initialize filter state and Close Date options */
|
|
115
115
|
constructor(props: FilterBarProps) {
|
|
116
116
|
super(props);
|
|
117
117
|
const closeOpts = normalizeOptions(
|
|
@@ -120,7 +120,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
120
120
|
: FilterBar.defaultCloseDateOptions(),
|
|
121
121
|
);
|
|
122
122
|
const csOpts = defaultChangesSinceOptions();
|
|
123
|
-
const initialClose = closeOpts[2]?.value ?? 401; //
|
|
123
|
+
const initialClose = closeOpts[2]?.value ?? 401; // Default: this quarter
|
|
124
124
|
const initialRelativeRange = relativeCloseDateRangeFromCode(initialClose);
|
|
125
125
|
this.state = {
|
|
126
126
|
closeDateOptions: closeOpts,
|
|
@@ -139,7 +139,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
139
139
|
businessTypeLoading: false,
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
-
//
|
|
142
|
+
// Bind methods to this
|
|
143
143
|
this.syncDefaultBusinessTypeFromProps =
|
|
144
144
|
this.syncDefaultBusinessTypeFromProps.bind(this);
|
|
145
145
|
this.syncCloseDateOptionsFromProps =
|
|
@@ -167,7 +167,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
167
167
|
this.renderChangesSinceBlock = this.renderChangesSinceBlock.bind(this);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
/** Close Date
|
|
170
|
+
/** Close Date built-in default options */
|
|
171
171
|
static defaultCloseDateOptions(): FilterOption[] {
|
|
172
172
|
return [
|
|
173
173
|
{ value: 201, label: 'This Week' },
|
|
@@ -177,14 +177,14 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
177
177
|
];
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
/**
|
|
180
|
+
/** Sync props options and fetch owner and business type data */
|
|
181
181
|
componentDidMount() {
|
|
182
182
|
this.syncCloseDateOptionsFromProps(this.props);
|
|
183
183
|
this.loadOpportunityOwnerOptions();
|
|
184
184
|
this.loadBusinessTypeOptions();
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
/** props.closeDateOptions
|
|
187
|
+
/** Sync local options when props.closeDateOptions changes */
|
|
188
188
|
componentWillReceiveProps(nextProps: FilterBarProps) {
|
|
189
189
|
if (nextProps.closeDateOptions !== this.props.closeDateOptions) {
|
|
190
190
|
this.syncCloseDateOptionsFromProps(nextProps);
|
|
@@ -206,7 +206,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
/** defaultBusiType
|
|
209
|
+
/** Re-match default business type from loaded dropdown data when defaultBusiType config changes */
|
|
210
210
|
syncDefaultBusinessTypeFromProps(props: FilterBarProps) {
|
|
211
211
|
const { businessTypeOptions } = this.state;
|
|
212
212
|
if (!businessTypeOptions.length) return;
|
|
@@ -222,7 +222,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
222
222
|
);
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
/**
|
|
225
|
+
/** Update Close Date options from props and correct current selected value */
|
|
226
226
|
syncCloseDateOptionsFromProps(props: FilterBarProps) {
|
|
227
227
|
const next = normalizeOptions(
|
|
228
228
|
props.closeDateOptions && props.closeDateOptions.length > 0
|
|
@@ -252,7 +252,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
252
252
|
});
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
/**
|
|
255
|
+
/** Build payload from state for external events / callbacks */
|
|
256
256
|
buildPayload(): FilterBarChangePayload {
|
|
257
257
|
const {
|
|
258
258
|
values,
|
|
@@ -290,7 +290,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
290
290
|
};
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
/**
|
|
293
|
+
/** Log, trigger onValuesChange and designer event */
|
|
294
294
|
emitChange() {
|
|
295
295
|
const payload = this.buildPayload();
|
|
296
296
|
console.log('[FilterBar__c] filters change', payload);
|
|
@@ -303,20 +303,20 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
303
303
|
});
|
|
304
304
|
|
|
305
305
|
/*
|
|
306
|
-
console.log('
|
|
306
|
+
console.log('Triggered a broadcast event updateFilterData:', payload);
|
|
307
307
|
NeoEvent.broadcast('updateFilterData', payload);
|
|
308
308
|
*/
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
/**
|
|
311
|
+
/** Designer event: filter conditions changed (including custom date range) */
|
|
312
312
|
@NeoEvent.dispatch
|
|
313
313
|
onFiltersChange(eventData?: FilterBarEventData) {}
|
|
314
314
|
|
|
315
|
-
/**
|
|
315
|
+
/** Fetch user entity to populate Opportunity Owner dropdown */
|
|
316
316
|
async loadOpportunityOwnerOptions() {
|
|
317
317
|
this.setState({ ownerLoading: true });
|
|
318
318
|
const currentUser = Object.assign({}, this.props.data?.__NeoCurrentUser);
|
|
319
|
-
//
|
|
319
|
+
// Add marker to current user
|
|
320
320
|
if (
|
|
321
321
|
currentUser &&
|
|
322
322
|
currentUser.name &&
|
|
@@ -348,7 +348,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
348
348
|
]);
|
|
349
349
|
} else {
|
|
350
350
|
console.warn(
|
|
351
|
-
'FilterBar xObject.query(user)
|
|
351
|
+
'FilterBar xObject.query(user) not successful:',
|
|
352
352
|
result?.msg ?? result,
|
|
353
353
|
);
|
|
354
354
|
opportunityOwnerOptions =
|
|
@@ -375,7 +375,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
375
375
|
},
|
|
376
376
|
);
|
|
377
377
|
} catch (e) {
|
|
378
|
-
console.error('FilterBar
|
|
378
|
+
console.error('FilterBar failed to load owner (user):', e);
|
|
379
379
|
const fallbackOptions =
|
|
380
380
|
parseUserRecordsToOwnerOptions(currentUserRecords);
|
|
381
381
|
const defaultOwnerIds = getDefaultOpportunityOwnerIds(this.props);
|
|
@@ -400,7 +400,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
-
/**
|
|
403
|
+
/** Fetch opportunity business type API to populate Business Type dropdown */
|
|
404
404
|
async loadBusinessTypeOptions() {
|
|
405
405
|
this.setState({ businessTypeLoading: true });
|
|
406
406
|
try {
|
|
@@ -428,12 +428,12 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
428
428
|
},
|
|
429
429
|
);
|
|
430
430
|
} catch (e) {
|
|
431
|
-
console.error('FilterBar
|
|
431
|
+
console.error('FilterBar failed to load business types:', e);
|
|
432
432
|
this.setState({ businessTypeLoading: false, businessTypeOptions: [] });
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
-
/**
|
|
436
|
+
/** Merge values subset and notify change */
|
|
437
437
|
patchValues(patch: Partial<FilterValues>) {
|
|
438
438
|
this.setState(
|
|
439
439
|
(prev) =>
|
|
@@ -444,7 +444,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
444
444
|
);
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
/** Close Date
|
|
447
|
+
/** Close Date dropdown change; for non-custom, write relative period start/end timestamps and start to changesSinceCustomTime */
|
|
448
448
|
handleCloseDateChange(value: number | string) {
|
|
449
449
|
if (String(value).toLowerCase() !== 'custom') {
|
|
450
450
|
const relativeRange = relativeCloseDateRangeFromCode(value);
|
|
@@ -478,7 +478,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
478
478
|
);
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
-
/** Changes Since
|
|
481
|
+
/** Changes Since dropdown change; for non-Custom with relative Close Date period, sync start timestamp */
|
|
482
482
|
handleChangesSinceChange(value: string | number) {
|
|
483
483
|
if (value !== 'Custom') {
|
|
484
484
|
const closeDate = this.state.values.closeDate;
|
|
@@ -505,20 +505,20 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
505
505
|
);
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
-
/** Close Date
|
|
508
|
+
/** Close Date custom RangePicker: write timestamp range */
|
|
509
509
|
handleCloseDateRangeChange(dates: any) {
|
|
510
510
|
const closeDateCustomRange = momentRangeToTimestamps(dates);
|
|
511
511
|
this.setState({ closeDateCustomRange }, () => this.emitChange());
|
|
512
512
|
}
|
|
513
513
|
|
|
514
|
-
/** Changes Since
|
|
514
|
+
/** Changes Since custom DatePicker: write midnight timestamp */
|
|
515
515
|
handleChangesSinceTimeChange(date: Moment | null) {
|
|
516
516
|
const changesSinceCustomTime =
|
|
517
517
|
date != null ? moment(date).startOf('day').valueOf() : null;
|
|
518
518
|
this.setState({ changesSinceCustomTime }, () => this.emitChange());
|
|
519
519
|
}
|
|
520
520
|
|
|
521
|
-
/** state.closeDateCustomRange
|
|
521
|
+
/** state.closeDateCustomRange -> RangePicker controlled value */
|
|
522
522
|
closeDateRangePickerValue(): [Moment | null, Moment | null] | null {
|
|
523
523
|
const r = this.state.closeDateCustomRange;
|
|
524
524
|
if (!r || (r.start == null && r.end == null)) return null;
|
|
@@ -528,20 +528,20 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
528
528
|
];
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
-
/** state.changesSinceCustomTime
|
|
531
|
+
/** state.changesSinceCustomTime -> DatePicker controlled value */
|
|
532
532
|
changesSinceDatePickerValue(): Moment | null {
|
|
533
533
|
const t = this.state.changesSinceCustomTime;
|
|
534
534
|
if (t == null) return null;
|
|
535
535
|
return moment(t);
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
-
/**
|
|
538
|
+
/** Action flow / external call: return current filter snapshot */
|
|
539
539
|
@NeoEvent.function
|
|
540
540
|
getFilters(): FilterBarChangePayload {
|
|
541
541
|
return this.buildPayload();
|
|
542
542
|
}
|
|
543
543
|
|
|
544
|
-
/**
|
|
544
|
+
/** Action flow: reset to defaults and clear custom ranges, then notify */
|
|
545
545
|
@NeoEvent.function
|
|
546
546
|
resetFilters() {
|
|
547
547
|
const { closeDateOptions, changesSinceOptions } = this.state;
|
|
@@ -565,7 +565,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
565
565
|
);
|
|
566
566
|
}
|
|
567
567
|
|
|
568
|
-
/** Close Date +
|
|
568
|
+
/** Close Date + custom date range */
|
|
569
569
|
renderCloseDateBlock() {
|
|
570
570
|
const { closeDateOptions, values } = this.state;
|
|
571
571
|
const showCustom = String(values.closeDate).toLowerCase() === 'custom';
|
|
@@ -666,7 +666,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
666
666
|
);
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
-
/** Changes Since + Custom
|
|
669
|
+
/** Changes Since + single date when Custom */
|
|
670
670
|
renderChangesSinceBlock() {
|
|
671
671
|
const { changesSinceOptions, values } = this.state;
|
|
672
672
|
const showCustom = values.changesSince === 'Custom';
|
|
@@ -679,7 +679,8 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
679
679
|
<span className="help-tip">
|
|
680
680
|
?
|
|
681
681
|
<span className="help-tip-text">
|
|
682
|
-
|
|
682
|
+
Changes Since custom: start time relative to the report period;
|
|
683
|
+
select a date when Custom
|
|
683
684
|
</span>
|
|
684
685
|
</span>
|
|
685
686
|
</label>
|
|
@@ -707,7 +708,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
707
708
|
);
|
|
708
709
|
}
|
|
709
710
|
|
|
710
|
-
/**
|
|
711
|
+
/** Filter bar root layout */
|
|
711
712
|
render() {
|
|
712
713
|
const { className, style } = this.props;
|
|
713
714
|
console.log('[FilterBar__c] render', this.props);
|
|
@@ -716,7 +717,7 @@ class FilterBar extends BaseCmp<FilterBarProps, FilterBarState> {
|
|
|
716
717
|
<div
|
|
717
718
|
className={`filterBar__c ${className || ''}`}
|
|
718
719
|
style={style}
|
|
719
|
-
data-time="2026.4.
|
|
720
|
+
data-time="2026.4.17 01"
|
|
720
721
|
>
|
|
721
722
|
{this.renderCloseDateBlock()}
|
|
722
723
|
{this.renderOwnerBlock()}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class FilterBarModel {
|
|
2
|
-
label: string = '
|
|
3
|
-
description: string =
|
|
2
|
+
label: string = 'Filter Bar';
|
|
3
|
+
description: string =
|
|
4
|
+
'Supports multi-dimensional filtering by date range, owner, business type, etc.';
|
|
4
5
|
iconUrl: string = 'https://custom-widgets.bj.bcebos.com/filter.svg';
|
|
5
6
|
targetPage: string[] = ['all'];
|
|
6
7
|
targetDevice: string = 'all';
|
|
@@ -12,35 +13,44 @@ export class FilterBarModel {
|
|
|
12
13
|
{ value: 401, label: 'This Quarter' },
|
|
13
14
|
{ value: 'custom', label: 'Custom' },
|
|
14
15
|
],
|
|
15
|
-
/**
|
|
16
|
+
/** When matching the apiKey of an item returned by the business type API, use it as the default Business Type selection */
|
|
16
17
|
defaultBusiType: 'defaultBusiType_1', // New Business
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
|
-
*
|
|
21
|
+
* Declare all events that the current component will trigger (matching @NeoEvent.dispatch method names in the component)
|
|
21
22
|
*/
|
|
22
23
|
events = [
|
|
23
24
|
{
|
|
24
25
|
apiKey: 'onFiltersChange',
|
|
25
|
-
label: '
|
|
26
|
+
label: 'After filter conditions change',
|
|
26
27
|
helpText:
|
|
27
|
-
'
|
|
28
|
-
eventParams:
|
|
29
|
-
|
|
28
|
+
'Triggered when any filter option or custom time range changes; event params include closeDate, closeDateCustomRange (for non-custom: current relative period start/end Unix ms timestamps; for custom: RangePicker start/end), opportunityOwner (owner multi-select id array), businessType, businessTypeLabel (business type display name), changesSince, changesSinceCustomTime (for Custom Changes Since: selected date midnight; otherwise for non-custom Close Date: period start midnight)',
|
|
29
|
+
eventParams: [
|
|
30
|
+
{
|
|
31
|
+
apiKey: 'eventParam',
|
|
32
|
+
children: [
|
|
33
|
+
{ apiKey: 'data', label: 'Current filter data', type: 'Object' },
|
|
34
|
+
],
|
|
35
|
+
label: 'Event parameters',
|
|
36
|
+
type: 'Object',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
30
39
|
},
|
|
31
40
|
];
|
|
32
41
|
|
|
33
42
|
functions = [
|
|
34
43
|
{
|
|
35
44
|
apiKey: 'resetFilters',
|
|
36
|
-
label: '
|
|
37
|
-
helpTextKey:
|
|
45
|
+
label: 'Reset filters',
|
|
46
|
+
helpTextKey:
|
|
47
|
+
'Reset all filter conditions to defaults and trigger the "After filter conditions change" event',
|
|
38
48
|
},
|
|
39
49
|
{
|
|
40
50
|
apiKey: 'getFilters',
|
|
41
|
-
label: '
|
|
51
|
+
label: 'Get filters',
|
|
42
52
|
helpTextKey:
|
|
43
|
-
'
|
|
53
|
+
'Return current filter snapshot object; when closeDate is not custom, closeDateCustomRange is the relative period start/end timestamps; when custom, it is the custom range; businessTypeLabel is the business type display name; changesSinceCustomTime is the selected date midnight when Changes Since is Custom, otherwise the period start midnight when Close Date is not custom',
|
|
44
54
|
},
|
|
45
55
|
];
|
|
46
56
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# PipelineFunnel
|
|
1
|
+
# PipelineFunnel Component
|
|
2
2
|
|
|
3
|
-
Pipeline
|
|
3
|
+
Pipeline funnel chart component that displays the funnel conversion of the sales pipeline.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Usage
|
|
6
6
|
|
|
7
7
|
```tsx
|
|
8
8
|
import PipelineFunnel from './components/pipelineFunnel__c';
|
|
@@ -20,20 +20,20 @@ import PipelineFunnel from './components/pipelineFunnel__c';
|
|
|
20
20
|
|
|
21
21
|
## Props
|
|
22
22
|
|
|
23
|
-
|
|
|
24
|
-
|
|
25
|
-
| title |
|
|
26
|
-
| totalAmount |
|
|
27
|
-
| stages |
|
|
28
|
-
| showAiButton |
|
|
29
|
-
| onStageClick |
|
|
23
|
+
| Property | Description | Type | Default |
|
|
24
|
+
|----------|-------------|------|---------|
|
|
25
|
+
| title | Title | string | 'Pipeline Funnel' |
|
|
26
|
+
| totalAmount | Total amount | string | '$11.1M' |
|
|
27
|
+
| stages | Funnel stage data | FunnelStage[] | [] |
|
|
28
|
+
| showAiButton | Show AI button | boolean | true |
|
|
29
|
+
| onStageClick | Stage click callback | (stageName: string) => void | - |
|
|
30
30
|
|
|
31
31
|
## FunnelStage
|
|
32
32
|
|
|
33
|
-
|
|
|
34
|
-
|
|
35
|
-
| name |
|
|
36
|
-
| amount |
|
|
37
|
-
| count |
|
|
38
|
-
| conversionRate |
|
|
39
|
-
| color |
|
|
33
|
+
| Property | Description | Type |
|
|
34
|
+
|----------|-------------|------|
|
|
35
|
+
| name | Stage name | string |
|
|
36
|
+
| amount | Amount | string |
|
|
37
|
+
| count | Count | number |
|
|
38
|
+
| conversionRate | Conversion rate | string |
|
|
39
|
+
| color | Color | string |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file Pipeline
|
|
3
|
-
* @description
|
|
2
|
+
* @file Pipeline Funnel Chart Component
|
|
3
|
+
* @description Displays the funnel conversion of the sales pipeline, data from NeoBI queryDataTask
|
|
4
4
|
*/
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import * as echarts from 'echarts';
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
type FunnelStage,
|
|
26
26
|
} from '../../utils/pipelineFunnel';
|
|
27
27
|
|
|
28
|
+
import './reset.scss';
|
|
28
29
|
import './style.scss';
|
|
29
30
|
|
|
30
31
|
const QUERY_DATA_TASK_URL = '/rest/neobi/v2.0/bestpractices/queryDataTask';
|
|
@@ -33,15 +34,15 @@ const FORM_URLENCODED_UTF8 = 'application/x-www-form-urlencoded;charset=UTF-8';
|
|
|
33
34
|
|
|
34
35
|
interface PipelineFunnelProps {
|
|
35
36
|
title?: string;
|
|
36
|
-
/**
|
|
37
|
+
/** View ID, used in queryDataTask */
|
|
37
38
|
viewId?: string;
|
|
38
|
-
/**
|
|
39
|
+
/** View type, corresponding to request parameter type */
|
|
39
40
|
viewType?: string;
|
|
40
41
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
42
|
+
* When true, layer width decreases with stage order (classic funnel), layer height proportional to each stage's amount;
|
|
43
|
+
* When false (default), layer width is proportional to each stage's actual amount */
|
|
43
44
|
useClassicFunnelShape?: boolean;
|
|
44
|
-
/** Neo
|
|
45
|
+
/** Neo injected, contains current user */
|
|
45
46
|
data?: {
|
|
46
47
|
__NeoCurrentUser?: { id?: string | number };
|
|
47
48
|
};
|
|
@@ -55,11 +56,11 @@ interface PipelineFunnelState {
|
|
|
55
56
|
loading: boolean;
|
|
56
57
|
error: string | null;
|
|
57
58
|
stages: FunnelStage[];
|
|
58
|
-
/**
|
|
59
|
+
/** Sum of amount values across all stages, used for percentage calculation */
|
|
59
60
|
totalAmountNum: number;
|
|
60
|
-
/**
|
|
61
|
+
/** Total amount label for top display */
|
|
61
62
|
totalAmountLabel: string;
|
|
62
|
-
/**
|
|
63
|
+
/** Filter for request body, can be updated by component action setFilter */
|
|
63
64
|
filter: any;
|
|
64
65
|
}
|
|
65
66
|
|
|
@@ -97,9 +98,9 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
97
98
|
this.bindResize();
|
|
98
99
|
|
|
99
100
|
/*
|
|
100
|
-
//
|
|
101
|
+
// Listen to a broadcast event
|
|
101
102
|
NeoEvent.listen('updateFilterData', (filterData: any) => {
|
|
102
|
-
console.log('PipelineFunnel
|
|
103
|
+
console.log('PipelineFunnel received broadcast event updateFilterData: ', filterData);
|
|
103
104
|
this.setFilter(filterData);
|
|
104
105
|
});
|
|
105
106
|
*/
|
|
@@ -196,6 +197,7 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
196
197
|
chartViewHeightPx: classicShape ? chartViewHeightPx : undefined,
|
|
197
198
|
funnelGap: 2,
|
|
198
199
|
});
|
|
200
|
+
console.log('[PipelineFunnel__c] updateChart option:', option);
|
|
199
201
|
this.chartInstance.setOption(option, true);
|
|
200
202
|
this.chartInstance.off('click');
|
|
201
203
|
this.chartInstance.on(
|
|
@@ -224,7 +226,7 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
224
226
|
}
|
|
225
227
|
requestAnimationFrame(() => this.chartInstance?.resize());
|
|
226
228
|
} catch (e) {
|
|
227
|
-
console.error('PipelineFunnel ECharts
|
|
229
|
+
console.error('PipelineFunnel ECharts update failed:', e);
|
|
228
230
|
}
|
|
229
231
|
}
|
|
230
232
|
|
|
@@ -235,7 +237,7 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
235
237
|
if (viewId == null || viewId === '' || userId == null || userId === '') {
|
|
236
238
|
this.setState({
|
|
237
239
|
loading: false,
|
|
238
|
-
error: '
|
|
240
|
+
error: 'Missing viewId or current user id (data.__NeoCurrentUser.id)',
|
|
239
241
|
stages: [],
|
|
240
242
|
totalAmountNum: 0,
|
|
241
243
|
totalAmountLabel: formatAmountDisplay(0),
|
|
@@ -273,7 +275,7 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
273
275
|
if (status !== 0 || !Array.isArray(table)) {
|
|
274
276
|
this.setState({
|
|
275
277
|
loading: false,
|
|
276
|
-
error: res?.message || res?.msg || '
|
|
278
|
+
error: res?.message || res?.msg || 'Failed to query chart data',
|
|
277
279
|
stages: [],
|
|
278
280
|
totalAmountNum: 0,
|
|
279
281
|
totalAmountLabel: formatAmountDisplay(0),
|
|
@@ -292,10 +294,10 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
292
294
|
totalAmountLabel: formatAmountDisplay(totalAmountNum),
|
|
293
295
|
});
|
|
294
296
|
} catch (e: any) {
|
|
295
|
-
console.error('PipelineFunnel queryDataTask
|
|
297
|
+
console.error('PipelineFunnel queryDataTask failed:', e);
|
|
296
298
|
this.setState({
|
|
297
299
|
loading: false,
|
|
298
|
-
error: e?.message || '
|
|
300
|
+
error: e?.message || 'Network request failed',
|
|
299
301
|
stages: [],
|
|
300
302
|
totalAmountNum: 0,
|
|
301
303
|
totalAmountLabel: formatAmountDisplay(0),
|
|
@@ -304,7 +306,7 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
304
306
|
}
|
|
305
307
|
|
|
306
308
|
/**
|
|
307
|
-
*
|
|
309
|
+
* Refresh funnel chart data (bindable in designer)
|
|
308
310
|
*/
|
|
309
311
|
@NeoEvent.function
|
|
310
312
|
async refreshData() {
|
|
@@ -312,8 +314,8 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
312
314
|
}
|
|
313
315
|
|
|
314
316
|
/**
|
|
315
|
-
*
|
|
316
|
-
* @param filter
|
|
317
|
+
* Set filter conditions and re-fetch data (bindable in designer)
|
|
318
|
+
* @param filter Filter object, used as the filter field of queryDataTask
|
|
317
319
|
*/
|
|
318
320
|
@NeoEvent.function
|
|
319
321
|
setFilter(filter?: any) {
|
|
@@ -332,7 +334,7 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
332
334
|
@NeoEvent.dispatch
|
|
333
335
|
onActiveStageChange(eventData?: any) {}
|
|
334
336
|
|
|
335
|
-
//
|
|
337
|
+
// Update the current active sales stage when clicking a stage
|
|
336
338
|
handleStageClick(stageName: string) {
|
|
337
339
|
this.onActiveStageChange({
|
|
338
340
|
activeStage: stageName,
|
|
@@ -355,7 +357,7 @@ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
|
|
|
355
357
|
<div
|
|
356
358
|
className={`pipelineFunnel__c ${className || ''}`}
|
|
357
359
|
style={style}
|
|
358
|
-
data-time="2026.4.
|
|
360
|
+
data-time="2026.4.17 01"
|
|
359
361
|
>
|
|
360
362
|
<Spin spinning={loading}>
|
|
361
363
|
<div className="funnel-header">
|