neo-cmp-cli 1.13.17 → 1.13.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/targetNumber__c/model.ts +1 -1
- 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 +13 -11
- 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 +23 -22
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +18 -17
- 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 +16 -15
- 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 +21 -18
- 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-form/package.json +1 -1
- package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +161 -41
- package/template/neo-web-form/src/components/batchAddTable__c/model.ts +4 -2
- 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 Stage Time Component
|
|
3
|
+
* @description Displays the average time spent in each sales stage; chart data from NeoBI queryDataTask, target/limit from XObject customItem3/4 (days)
|
|
4
4
|
*/
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
import { Spin } from 'antd';
|
|
@@ -52,7 +52,7 @@ interface StageTimeChartProps {
|
|
|
52
52
|
style?: React.CSSProperties;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/**
|
|
55
|
+
/** Threshold row: only normalize customItem3/4 to number, keep other fields as-is */
|
|
56
56
|
interface ThresholdRecordRow extends Record<string, unknown> {
|
|
57
57
|
customItem3__c: number;
|
|
58
58
|
customItem4__c: number;
|
|
@@ -63,14 +63,14 @@ interface StageTimeChartState {
|
|
|
63
63
|
error: string | null;
|
|
64
64
|
items: StageTimeItem[];
|
|
65
65
|
filter: any;
|
|
66
|
-
/**
|
|
66
|
+
/** Matches threshold row customItem2__c, default New Business (can be updated by setFilter) */
|
|
67
67
|
oppType: string;
|
|
68
|
-
/** fetchThresholdRecords
|
|
68
|
+
/** Full list fetched by fetchThresholdRecords (customItem3/4 converted to number) */
|
|
69
69
|
thresholdRecords: ThresholdRecordRow[];
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
73
|
-
/**
|
|
73
|
+
/** Initialize state */
|
|
74
74
|
constructor(props: StageTimeChartProps) {
|
|
75
75
|
super(props);
|
|
76
76
|
const initialOppType =
|
|
@@ -94,20 +94,20 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
94
94
|
this.setFilter = this.setFilter.bind(this);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
/**
|
|
97
|
+
/** Fetch thresholds and chart data after mount */
|
|
98
98
|
componentDidMount() {
|
|
99
99
|
this.fetchAllData();
|
|
100
100
|
|
|
101
101
|
/*
|
|
102
|
-
//
|
|
102
|
+
// Listen to a broadcast event
|
|
103
103
|
NeoEvent.listen('updateFilterData', (filterData: any) => {
|
|
104
|
-
console.log('StageTimeChart
|
|
104
|
+
console.log('StageTimeChart received broadcast event updateFilterData: ', filterData);
|
|
105
105
|
this.setFilter(filterData);
|
|
106
106
|
});
|
|
107
107
|
*/
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
/**
|
|
110
|
+
/** Re-fetch data when key props change */
|
|
111
111
|
componentDidUpdate(
|
|
112
112
|
prevProps: StageTimeChartProps,
|
|
113
113
|
prevState: StageTimeChartState,
|
|
@@ -134,7 +134,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
/**
|
|
137
|
+
/** Find a single row in the list by customItem1__c / customItem2__c; customItem2 defaults to state.oppType */
|
|
138
138
|
getThresholdRecord(
|
|
139
139
|
customItem1__c: string = String(this.state.oppType ?? 'New Business'),
|
|
140
140
|
customItem2__c: string,
|
|
@@ -150,7 +150,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
150
150
|
});
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
/**
|
|
153
|
+
/** Fetch threshold list, only convert customItem3/4 to number, and write to state.thresholdRecords */
|
|
154
154
|
async fetchThresholdRecords(): Promise<ThresholdRecordRow[]> {
|
|
155
155
|
const { thresholdsXObjectApi } = this.props;
|
|
156
156
|
const apiKey = thresholdsXObjectApi?.xObjectApiKey;
|
|
@@ -180,7 +180,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
180
180
|
|
|
181
181
|
if (!result?.status) {
|
|
182
182
|
console.warn(
|
|
183
|
-
'StageTimeChart xObject.query(thresholds)
|
|
183
|
+
'StageTimeChart xObject.query(thresholds) not successful:',
|
|
184
184
|
result?.msg ?? result,
|
|
185
185
|
);
|
|
186
186
|
this.setState({ thresholdRecords: [] });
|
|
@@ -199,13 +199,13 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
199
199
|
this.setState({ thresholdRecords });
|
|
200
200
|
return thresholdRecords;
|
|
201
201
|
} catch (e) {
|
|
202
|
-
console.error('StageTimeChart
|
|
202
|
+
console.error('StageTimeChart failed to load threshold data:', e);
|
|
203
203
|
this.setState({ thresholdRecords: [] });
|
|
204
204
|
return [];
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
/**
|
|
208
|
+
/** Request queryDataTask, parse table rows and attach target/limit for each row (from threshold row customItem3/4) */
|
|
209
209
|
async fetchChartRows(
|
|
210
210
|
thresholdRecords: ThresholdRecordRow[],
|
|
211
211
|
): Promise<{ rows: StageTimeRow[]; error: string | null }> {
|
|
@@ -216,7 +216,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
216
216
|
if (viewId == null || viewId === '' || userId == null || userId === '') {
|
|
217
217
|
return {
|
|
218
218
|
rows: [],
|
|
219
|
-
error: '
|
|
219
|
+
error: 'Missing viewId or current user id (data.__NeoCurrentUser.id)',
|
|
220
220
|
};
|
|
221
221
|
}
|
|
222
222
|
|
|
@@ -248,7 +248,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
248
248
|
if (status !== 0 || !Array.isArray(table)) {
|
|
249
249
|
return {
|
|
250
250
|
rows: [],
|
|
251
|
-
error: res?.message || res?.msg || '
|
|
251
|
+
error: res?.message || res?.msg || 'Failed to query chart data',
|
|
252
252
|
};
|
|
253
253
|
}
|
|
254
254
|
|
|
@@ -286,10 +286,10 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
286
286
|
|
|
287
287
|
return { rows, error: null };
|
|
288
288
|
} catch (e: any) {
|
|
289
|
-
console.error('StageTimeChart queryDataTask
|
|
289
|
+
console.error('StageTimeChart queryDataTask failed:', e);
|
|
290
290
|
return {
|
|
291
291
|
rows: [],
|
|
292
|
-
error: e?.message || '
|
|
292
|
+
error: e?.message || 'Network request failed',
|
|
293
293
|
};
|
|
294
294
|
}
|
|
295
295
|
}
|
|
@@ -370,7 +370,7 @@ class StageTimeChart extends BaseCmp<StageTimeChartProps, StageTimeChartState> {
|
|
|
370
370
|
<div
|
|
371
371
|
className={`stageTimeChart__c ${className || ''}`}
|
|
372
372
|
style={style}
|
|
373
|
-
data-time="2026.4.
|
|
373
|
+
data-time="2026.4.17 01"
|
|
374
374
|
>
|
|
375
375
|
<Spin spinning={loading}>
|
|
376
376
|
<div className="chart-header">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export class StageTimeChartModel {
|
|
2
|
-
label: string = '
|
|
2
|
+
label: string = 'Stage Time';
|
|
3
3
|
description: string =
|
|
4
|
-
'
|
|
4
|
+
'Displays the average time spent in each sales stage; chart data from NeoBI queryDataTask, target/limit from XObject customItem3/4 (days)';
|
|
5
5
|
iconUrl: string = 'https://custom-widgets.bj.bcebos.com/barChart.svg';
|
|
6
6
|
targetPage: string[] = ['all'];
|
|
7
7
|
targetDevice: string = 'all';
|
|
@@ -11,9 +11,9 @@ export class StageTimeChartModel {
|
|
|
11
11
|
viewId: '4264466340118875',
|
|
12
12
|
viewType: 'sync',
|
|
13
13
|
showAiButton: true,
|
|
14
|
-
/**
|
|
14
|
+
/** Opportunity type matching threshold record customItem2__c, default New Business */
|
|
15
15
|
oppType: 'New Business',
|
|
16
|
-
/**
|
|
16
|
+
/** Thresholds: customItem1__c stage key, customItem2__c opportunity type, customItem3__c/4 target and limit (days) */
|
|
17
17
|
thresholdsXObjectApi: {
|
|
18
18
|
xObjectApiKey: 'customEntity105__c',
|
|
19
19
|
fields: [
|
|
@@ -30,28 +30,29 @@ export class StageTimeChartModel {
|
|
|
30
30
|
events = [
|
|
31
31
|
{
|
|
32
32
|
apiKey: 'onActiveStageChange',
|
|
33
|
-
label: '
|
|
33
|
+
label: 'Triggered on stage bar click',
|
|
34
34
|
helpText:
|
|
35
|
-
'
|
|
35
|
+
'Triggered when clicking a stage bar; event params include activeStage (current active stage)',
|
|
36
36
|
eventParams:
|
|
37
|
-
'[{"apiKey":"eventParam","children":[{"apiKey":"activeStage","label":"
|
|
37
|
+
'[{"apiKey":"eventParam","children":[{"apiKey":"activeStage","label":"Current active stage","type":"String"}],"label":"Event parameters","type":"Object"}]',
|
|
38
38
|
},
|
|
39
39
|
];
|
|
40
40
|
|
|
41
41
|
functions = [
|
|
42
42
|
{
|
|
43
43
|
apiKey: 'refreshData',
|
|
44
|
-
label: '
|
|
45
|
-
helpTextKey:
|
|
44
|
+
label: 'Refresh data',
|
|
45
|
+
helpTextKey:
|
|
46
|
+
'Re-request queryDataTask and threshold config to refresh stage time',
|
|
46
47
|
},
|
|
47
48
|
{
|
|
48
49
|
apiKey: 'setFilter',
|
|
49
|
-
label: '
|
|
50
|
-
helpTextKey: '
|
|
50
|
+
label: 'Set filter conditions',
|
|
51
|
+
helpTextKey: 'Set the queryDataTask filter and re-fetch chart data',
|
|
51
52
|
funcInParams: [
|
|
52
53
|
{
|
|
53
54
|
apiKey: 'filter',
|
|
54
|
-
label: '
|
|
55
|
+
label: 'Filter conditions',
|
|
55
56
|
type: 'Object',
|
|
56
57
|
required: false,
|
|
57
58
|
},
|
|
@@ -63,27 +64,28 @@ export class StageTimeChartModel {
|
|
|
63
64
|
{
|
|
64
65
|
type: 'panelInput',
|
|
65
66
|
name: 'title',
|
|
66
|
-
label: '
|
|
67
|
+
label: 'Title',
|
|
67
68
|
},
|
|
68
69
|
{
|
|
69
70
|
type: 'panelInput',
|
|
70
71
|
name: 'viewId',
|
|
71
|
-
label: '
|
|
72
|
+
label: 'View ID',
|
|
72
73
|
},
|
|
73
74
|
{
|
|
74
75
|
type: 'panelInput',
|
|
75
76
|
name: 'viewType',
|
|
76
|
-
label: '
|
|
77
|
+
label: 'View type (request type)',
|
|
77
78
|
},
|
|
78
79
|
{
|
|
79
80
|
type: 'panelInput',
|
|
80
81
|
name: 'oppType',
|
|
81
|
-
label: '
|
|
82
|
+
label: 'Opportunity type (matching customItem2__c)',
|
|
82
83
|
},
|
|
84
|
+
/*
|
|
83
85
|
{
|
|
84
86
|
type: 'xObjectDataApi',
|
|
85
87
|
name: 'thresholdsXObjectApi',
|
|
86
|
-
label: '
|
|
88
|
+
label: 'Stage target/limit data source',
|
|
87
89
|
value: {
|
|
88
90
|
xObjectApiKey: '',
|
|
89
91
|
fields: [
|
|
@@ -94,9 +96,10 @@ export class StageTimeChartModel {
|
|
|
94
96
|
],
|
|
95
97
|
},
|
|
96
98
|
placeholder:
|
|
97
|
-
'
|
|
99
|
+
'Entity must contain customItem1__c (stage), customItem2__c (opportunity type), customItem3/4 (target/limit days)',
|
|
98
100
|
custom: true,
|
|
99
101
|
},
|
|
102
|
+
*/
|
|
100
103
|
];
|
|
101
104
|
}
|
|
102
105
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Shared pure utility methods across multiple components (filter where, stage name, amount display, NeoBI request body, etc.)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export interface PropsContext {
|
|
@@ -10,7 +10,7 @@ export interface PropsContext {
|
|
|
10
10
|
};
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
13
|
+
/** Convert filter option to finite number, returns null if invalid (consistent with filter2chartFilter, etc.) */
|
|
14
14
|
export function toFiniteNumber(v: unknown): number | null {
|
|
15
15
|
if (v == null || v === '') return null;
|
|
16
16
|
if (typeof v === 'number' && Number.isFinite(v)) return v;
|
|
@@ -18,7 +18,7 @@ export function toFiniteNumber(v: unknown): number | null {
|
|
|
18
18
|
return Number.isFinite(n) ? n : null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/**
|
|
21
|
+
/** Time range start/end: returns closed interval [start, end] when both are valid numbers (millisecond timestamps, consistent with FilterBar) */
|
|
22
22
|
export function closeRangeFromFilter(
|
|
23
23
|
range: unknown,
|
|
24
24
|
): { start: number; end: number } | null {
|
|
@@ -30,7 +30,7 @@ export function closeRangeFromFilter(
|
|
|
30
30
|
return { start, end };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
/** opportunityOwner
|
|
33
|
+
/** opportunityOwner -> user id list for where clause (string to avoid big integer precision loss) */
|
|
34
34
|
export function normalizeOwnerIdsForWhere(owner: unknown): string[] {
|
|
35
35
|
if (owner == null || owner === '') return [];
|
|
36
36
|
const raw = Array.isArray(owner) ? owner : [owner];
|
|
@@ -51,7 +51,7 @@ export function entityTypeIdForWhere(businessType: unknown): string | null {
|
|
|
51
51
|
return s.replace(/,/g, '');
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/**
|
|
54
|
+
/** Extract the segment after the first '.' from stage display name, used for matching customItem1__c, etc. */
|
|
55
55
|
export function extractStageKeyFromStageName(stageName: string): string {
|
|
56
56
|
const s = String(stageName ?? '').trim();
|
|
57
57
|
const i = s.indexOf('.');
|
|
@@ -59,7 +59,7 @@ export function extractStageKeyFromStageName(stageName: string): string {
|
|
|
59
59
|
return s.slice(i + 1).trim();
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/**
|
|
62
|
+
/** Opportunity rows, etc.: returns 0 when empty or unparseable */
|
|
63
63
|
export function parseNumberOrZero(raw: unknown): number {
|
|
64
64
|
if (raw === null || raw === undefined || raw === '') return 0;
|
|
65
65
|
const n =
|
|
@@ -69,7 +69,7 @@ export function parseNumberOrZero(raw: unknown): number {
|
|
|
69
69
|
return Number.isFinite(n) ? n : 0;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
/**
|
|
72
|
+
/** Threshold config, etc.: returns NaN when empty or unparseable */
|
|
73
73
|
export function parseNumberOrNaN(raw: unknown): number {
|
|
74
74
|
if (raw === null || raw === undefined || raw === '') return NaN;
|
|
75
75
|
const n =
|
|
@@ -79,7 +79,7 @@ export function parseNumberOrNaN(raw: unknown): number {
|
|
|
79
79
|
return Number.isFinite(n) ? n : NaN;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/**
|
|
82
|
+
/** Display amount: $ prefix; uses K / M / B / T abbreviation when >= 1e3 */
|
|
83
83
|
export function formatAmountDisplay(n: number): string {
|
|
84
84
|
if (!Number.isFinite(n)) return '-';
|
|
85
85
|
const sign = n < 0 ? '-' : '';
|
|
@@ -110,7 +110,7 @@ export function formatAmountDisplay(n: number): string {
|
|
|
110
110
|
return `${sign}$${body}${suffix}`;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
/** queryDataTask
|
|
113
|
+
/** queryDataTask: x-www-form-urlencoded form body (filter passed as JSON string) */
|
|
114
114
|
export function buildQueryDataTaskFormBody(params: {
|
|
115
115
|
viewId: string;
|
|
116
116
|
userId: string | number;
|
|
@@ -125,11 +125,11 @@ export function buildQueryDataTaskFormBody(params: {
|
|
|
125
125
|
return form.toString();
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
/**
|
|
128
|
+
/** Owner multi-select default: current logged-in user id (consistent with __NeoCurrentUser) */
|
|
129
129
|
export function getDefaultOpportunityOwnerIds(
|
|
130
130
|
props: PropsContext,
|
|
131
131
|
): (number | string)[] {
|
|
132
|
-
// return [1246045]; //
|
|
132
|
+
// return [1246045]; // Default user changed to (Alice)
|
|
133
133
|
const curUser = props.data?.__NeoCurrentUser;
|
|
134
134
|
if (
|
|
135
135
|
curUser?.id == null ||
|
|
@@ -141,9 +141,9 @@ export function getDefaultOpportunityOwnerIds(
|
|
|
141
141
|
return [curUser.id];
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
/**
|
|
144
|
+
/** Owner multi-select default: current logged-in user id (consistent with __NeoCurrentUser) */
|
|
145
145
|
export function getDefaultFilterWhereByProps(props: any = {}): any {
|
|
146
|
-
let userId = 1246045; //
|
|
146
|
+
let userId = 1246045; // Default user (Alice)
|
|
147
147
|
const curUser = props.data?.__NeoCurrentUser;
|
|
148
148
|
if (curUser?.id) {
|
|
149
149
|
userId = curUser.id;
|
|
@@ -207,7 +207,7 @@ export function getDefaultFilterWhereByProps(props: any = {}): any {
|
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
export function getDefaultFilterByProps(props: any = {}): any {
|
|
210
|
-
let userId = 1246045; //
|
|
210
|
+
let userId = 1246045; // Default user (Alice)
|
|
211
211
|
const curUser = props.data?.__NeoCurrentUser;
|
|
212
212
|
if (curUser?.id) {
|
|
213
213
|
userId = curUser.id;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Filter condition formatting
|
|
3
|
+
* Formats the filter conditions from the filter bar component into the format required by the API.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Data conversion rules:
|
|
6
6
|
1. closeDate
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
custom
|
|
8
|
+
Non-custom: Use toFiniteNumber to convert the option value (e.g. 201 / "201") to a number, timeType: 'relative', value: [{ val, baseType: 'system' }].
|
|
9
|
+
custom: Read closeDateCustomRange.start / end; when both are valid numbers, timeType: 'absolute', value is two { val, close: 1 } entries; if range is missing, this item is not generated.
|
|
10
10
|
|
|
11
|
-
2. opportunityOwner
|
|
12
|
-
|
|
13
|
-
value
|
|
11
|
+
2. opportunityOwner -> ownerId
|
|
12
|
+
Supports single value (string/number) or multi-select array; toFiniteNumber each item, invalid items are discarded. When at least one valid id exists, generates a refer condition (referKey: 'user', optionType: 'user', operate: 'in', includeNullValue: 0).
|
|
13
|
+
value is an array, each user is { val, sub: false, source: 'internal', specialType: 0 }.
|
|
14
14
|
|
|
15
|
-
3. businessType
|
|
16
|
-
|
|
15
|
+
3. businessType -> entityType
|
|
16
|
+
When a value exists and can be converted to a number, generates refer + value: [{ val }]; empty string does not pass entityType.
|
|
17
17
|
|
|
18
18
|
4. relation
|
|
19
|
-
|
|
19
|
+
Concatenated as "1 and 2 and 3" based on the seq of final items; when there are no conditions, it is '', filter is [].
|
|
20
20
|
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* Example:
|
|
23
|
+
* Filter conditions from the filter bar component:
|
|
24
24
|
{
|
|
25
25
|
"closeDate": "custom",
|
|
26
26
|
"closeDateCustomRange": {
|
|
@@ -33,7 +33,7 @@ value 为数组,每个用户为 { val, sub: false, source: 'internal', special
|
|
|
33
33
|
"changesSinceCustomTime": 1776268800000
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Format required by the API:
|
|
37
37
|
{
|
|
38
38
|
"relation": "1 and 2 and 3",
|
|
39
39
|
"filter": [
|
|
@@ -96,7 +96,7 @@ value 为数组,每个用户为 { val, sub: false, source: 'internal', special
|
|
|
96
96
|
]
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
If closeDate is custom, the closeDateCustomRange needs to be formatted into the API-required format
|
|
100
100
|
{
|
|
101
101
|
"seq": 3,
|
|
102
102
|
"fieldKey": "closeDate",
|
|
@@ -193,7 +193,7 @@ const ownerUserValueItem = (val: number) => ({
|
|
|
193
193
|
specialType: 0,
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
-
/**
|
|
196
|
+
/** Parse valid user id list from opportunityOwner (single value or id array) */
|
|
197
197
|
function normalizeOwnerIds(owner: unknown): number[] {
|
|
198
198
|
if (owner == null || owner === '') return [];
|
|
199
199
|
const raw = Array.isArray(owner) ? owner : [owner];
|
|
@@ -244,15 +244,13 @@ function entityTypeChunk(
|
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
/**
|
|
247
|
-
*
|
|
247
|
+
* Convert filter bar payload to chart/API filter structure; only maps closeDate, ownerId, entityType.
|
|
248
248
|
*/
|
|
249
249
|
export const filter2chartFilter = (filter: any): ChartFilterPayload => {
|
|
250
250
|
const f = filter && typeof filter === 'object' ? filter : {};
|
|
251
|
-
const parts = [
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
entityTypeChunk(f),
|
|
255
|
-
].filter(Boolean) as Omit<ChartFilterItem, 'seq'>[];
|
|
251
|
+
const parts = [closeDateChunk(f), ownerChunk(f), entityTypeChunk(f)].filter(
|
|
252
|
+
Boolean,
|
|
253
|
+
) as Omit<ChartFilterItem, 'seq'>[];
|
|
256
254
|
|
|
257
255
|
const filterOut: ChartFilterItem[] = parts.map((p, i) => ({
|
|
258
256
|
...p,
|
|
@@ -265,4 +263,4 @@ export const filter2chartFilter = (filter: any): ChartFilterPayload => {
|
|
|
265
263
|
: filterOut.map((x) => String(x.seq)).join(' and ');
|
|
266
264
|
|
|
267
265
|
return { relation, filter: filterOut };
|
|
268
|
-
};
|
|
266
|
+
};
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Filter bar filterBar specific utility methods
|
|
3
3
|
*/
|
|
4
4
|
import moment from 'moment';
|
|
5
5
|
|
|
6
6
|
export interface FilterOption {
|
|
7
7
|
value: string | number;
|
|
8
8
|
label: string;
|
|
9
|
-
/**
|
|
9
|
+
/** apiKey on API items like business type, used to align with defaultBusiType config */
|
|
10
10
|
apiKey?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/** Custom date range: start/end are Unix millisecond timestamps (day is local startOfDay / endOfDay) */
|
|
14
14
|
export interface TimestampRange {
|
|
15
15
|
start?: number;
|
|
16
16
|
end?: number;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
/**
|
|
19
|
+
/** Normalize dropdown item value / label */
|
|
20
20
|
export function normalizeOptions(list?: FilterOption[]): FilterOption[] {
|
|
21
21
|
if (!Array.isArray(list) || list.length === 0) return [];
|
|
22
22
|
return list.map((o) => ({
|
|
@@ -25,7 +25,7 @@ export function normalizeOptions(list?: FilterOption[]): FilterOption[] {
|
|
|
25
25
|
}));
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/** Changes Since
|
|
28
|
+
/** Changes Since dropdown default options */
|
|
29
29
|
export function defaultChangesSinceOptions(): FilterOption[] {
|
|
30
30
|
return [
|
|
31
31
|
{ value: 'Start of the Period', label: 'Start of the Period' },
|
|
@@ -33,7 +33,7 @@ export function defaultChangesSinceOptions(): FilterOption[] {
|
|
|
33
33
|
];
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/** RangePicker
|
|
36
|
+
/** Convert two selected days from RangePicker to start/end timestamps (inclusive of the day) */
|
|
37
37
|
export function momentRangeToTimestamps(dates: any): TimestampRange | null {
|
|
38
38
|
const m0 = dates?.[0];
|
|
39
39
|
const m1 = dates?.[1];
|
|
@@ -45,8 +45,8 @@ export function momentRangeToTimestamps(dates: any): TimestampRange | null {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* Convert Close Date relative period code to local calendar day start/end timestamps (start at midnight, end at 23:59:59.999).
|
|
49
|
+
* Convention: hundreds digit 2=this week (ISO week), 3=this month, 4=this quarter (e.g. 201/301/401 and other codes in the same range).
|
|
50
50
|
*/
|
|
51
51
|
export function relativeCloseDateRangeFromCode(
|
|
52
52
|
encoded: number | string,
|
|
@@ -62,7 +62,7 @@ export function relativeCloseDateRangeFromCode(
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
if (curEncoded === 301) {
|
|
65
|
-
// This Month
|
|
65
|
+
// This Month (start is the second day of the month at midnight)
|
|
66
66
|
return {
|
|
67
67
|
start: now
|
|
68
68
|
.clone()
|
|
@@ -74,7 +74,7 @@ export function relativeCloseDateRangeFromCode(
|
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
if (curEncoded === 401) {
|
|
77
|
-
// This Quarter
|
|
77
|
+
// This Quarter (start is the second day of the quarter at midnight)
|
|
78
78
|
return {
|
|
79
79
|
start: now
|
|
80
80
|
.clone()
|
|
@@ -88,7 +88,7 @@ export function relativeCloseDateRangeFromCode(
|
|
|
88
88
|
return null;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/**
|
|
91
|
+
/** Convert user entity query results to owner dropdown options (deduplicated by id) */
|
|
92
92
|
export function parseUserRecordsToOwnerOptions(records: any): FilterOption[] {
|
|
93
93
|
const arr = Array.isArray(records) ? records : [];
|
|
94
94
|
const seen = new Set<string>();
|
|
@@ -104,7 +104,7 @@ export function parseUserRecordsToOwnerOptions(records: any): FilterOption[] {
|
|
|
104
104
|
return out;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
/**
|
|
107
|
+
/** Extract list array from business type API response */
|
|
108
108
|
export function getBusinessTypeRawList(res: any): any[] {
|
|
109
109
|
const raw = res?.data !== undefined ? res.data : res;
|
|
110
110
|
const arr = Array.isArray(raw)
|
|
@@ -113,7 +113,7 @@ export function getBusinessTypeRawList(res: any): any[] {
|
|
|
113
113
|
return Array.isArray(arr) ? arr : [];
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
/**
|
|
116
|
+
/** Parse business type API results to dropdown options (preserving apiKey for default item matching) */
|
|
117
117
|
export function parseBusinessTypes(res: any): FilterOption[] {
|
|
118
118
|
const arr = getBusinessTypeRawList(res);
|
|
119
119
|
return arr
|
|
@@ -126,7 +126,7 @@ export function parseBusinessTypes(res: any): FilterOption[] {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
|
-
*
|
|
129
|
+
* Resolve default selected value in business type options by apiKey === defaultBusiType (consistent with dropdown value, usually id)
|
|
130
130
|
*/
|
|
131
131
|
export function resolveDefaultBusinessTypeValue(
|
|
132
132
|
options: FilterOption[],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pipeline
|
|
2
|
+
* Pipeline funnel pipelineFunnel specific (parse table rows, ECharts config)
|
|
3
3
|
*/
|
|
4
4
|
import * as echarts from 'echarts';
|
|
5
5
|
|
|
@@ -41,7 +41,7 @@ export function isTruthyProp(v: unknown): boolean {
|
|
|
41
41
|
return v === true || v === 'true' || v === 1 || v === '1';
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/**
|
|
44
|
+
/** Ignore case and consecutive whitespace when comparing with backend stage names */
|
|
45
45
|
export function isClosedLostStageName(raw: unknown): boolean {
|
|
46
46
|
const n = String(raw ?? '')
|
|
47
47
|
.trim()
|
|
@@ -50,7 +50,7 @@ export function isClosedLostStageName(raw: unknown): boolean {
|
|
|
50
50
|
return n === 'closed lost';
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
/**
|
|
53
|
+
/** Remove Closed Lost stage rows, keep header; must be called before parseRowsToStages to avoid counting in total and percentage */
|
|
54
54
|
export function filterOutClosedLostRows(table: unknown[][]): unknown[][] {
|
|
55
55
|
if (!Array.isArray(table) || table.length < 2) return table;
|
|
56
56
|
const header = table[0];
|
|
@@ -122,7 +122,7 @@ export function funnelValueForStage(
|
|
|
122
122
|
return 0;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
/**
|
|
125
|
+
/** Classic funnel: width still controlled by value (order); layer height allocated by amount proportion (consistent with series.gap, unit is px) */
|
|
126
126
|
export function classicFunnelLayerHeightPercents(
|
|
127
127
|
amountNums: number[],
|
|
128
128
|
viewHeightPx: number,
|
|
@@ -144,7 +144,7 @@ export function classicFunnelLayerHeightPercents(
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
export interface BuildFunnelChartOptionParams {
|
|
147
|
-
/**
|
|
147
|
+
/** Container height (px), used in classic mode to calculate layer height percentages; should re-setOption when changed */
|
|
148
148
|
chartViewHeightPx?: number;
|
|
149
149
|
funnelGap?: number;
|
|
150
150
|
}
|