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,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
|
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import { request as axiosFetcher } from 'neo-open-api';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Common query Open APIs are stored here
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/** Normalize where to SQL fragment: string is used directly; array items are joined with 'and' */
|
|
9
9
|
function normalizeWhere(where: unknown): string {
|
|
10
10
|
if (where == null || where === '') {
|
|
11
11
|
return '';
|
|
@@ -22,7 +22,7 @@ function normalizeWhere(where: unknown): string {
|
|
|
22
22
|
return '';
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// Query BI data by SQL
|
|
26
26
|
export const queryByCustomSQL = async (options?: any) => {
|
|
27
27
|
const apiUrl = '/rest/neobi/v2.0/query/queryByCustomSQL';
|
|
28
28
|
const curOptions = options || {};
|
|
@@ -31,34 +31,34 @@ export const queryByCustomSQL = async (options?: any) => {
|
|
|
31
31
|
const page = curOptions.page || 1;
|
|
32
32
|
const pageSize = curOptions.pageSize || 10;
|
|
33
33
|
|
|
34
|
-
//
|
|
34
|
+
// Automatically add objectId field
|
|
35
35
|
if (!fields.includes('id')) {
|
|
36
36
|
fields.push('id');
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
//
|
|
39
|
+
// Calculate pagination offset
|
|
40
40
|
const offset = (page - 1) * pageSize;
|
|
41
41
|
|
|
42
|
-
//
|
|
42
|
+
// Build SQL query
|
|
43
43
|
let querySql = `select ${fields.join(',')} from ${xObjectApiKey}`;
|
|
44
44
|
|
|
45
|
-
//
|
|
45
|
+
// Add sort conditions (if any)
|
|
46
46
|
if (curOptions.orderBy) {
|
|
47
47
|
querySql += ` order by ${curOptions.orderBy}`;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* "like"
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
51
|
+
* Add filter conditions (if any)
|
|
52
|
+
* Supported operators: =, !=, like, not like, not in, is not null, is null, >, <, <>, >=, <=, in, between ... and ...
|
|
53
|
+
* Notes for =, like and in:
|
|
54
|
+
* "=" as a string condition means exact match. For example, city = 'Beijing' returns all records where city is strictly equal to "Beijing".
|
|
55
|
+
* "like" as a string condition requires "%" wildcard for fuzzy matching. For example, city like 'Beijing%' returns all records where city starts with "Beijing".
|
|
56
|
+
* Currently only supports placing "%" after the known content; e.g., city like '% Beijing' is not supported.
|
|
57
|
+
* When the SQL query contains special characters like "%", URL encoding is required.
|
|
58
|
+
* Supports "in", but not subqueries.
|
|
59
|
+
* Supported logical operators: and, or.
|
|
60
60
|
*
|
|
61
|
-
* `where`
|
|
61
|
+
* `where` can be a string, or a string array (multiple items joined with 'and' by default, equivalent to `a and b`).
|
|
62
62
|
*/
|
|
63
63
|
const whereClause = normalizeWhere(curOptions.where);
|
|
64
64
|
|
|
@@ -67,7 +67,7 @@ export const queryByCustomSQL = async (options?: any) => {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
if (curOptions.page || curOptions.pageSize) {
|
|
70
|
-
//
|
|
70
|
+
// Add pagination limit
|
|
71
71
|
querySql += ` limit ${offset},${pageSize}`;
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -91,7 +91,8 @@ export const queryByCustomSQL = async (options?: any) => {
|
|
|
91
91
|
return {
|
|
92
92
|
status: true,
|
|
93
93
|
code: resultData.code,
|
|
94
|
-
msg:
|
|
94
|
+
msg:
|
|
95
|
+
resultData.msg || 'Successfully retrieved business object data list',
|
|
95
96
|
totalSize: records.length,
|
|
96
97
|
data: records || [],
|
|
97
98
|
};
|
|
@@ -100,15 +101,18 @@ export const queryByCustomSQL = async (options?: any) => {
|
|
|
100
101
|
return {
|
|
101
102
|
status: false,
|
|
102
103
|
code: resultData.code,
|
|
103
|
-
msg: resultData.msg || '
|
|
104
|
+
msg: resultData.msg || 'Failed to retrieve business object data list',
|
|
104
105
|
data: [],
|
|
105
106
|
};
|
|
106
107
|
} catch (error) {
|
|
107
|
-
console.error('
|
|
108
|
+
console.error('Failed to retrieve business object data list:', error);
|
|
108
109
|
|
|
109
110
|
return {
|
|
110
111
|
status: false,
|
|
111
|
-
msg:
|
|
112
|
+
msg:
|
|
113
|
+
error.msg ||
|
|
114
|
+
error.message ||
|
|
115
|
+
'Failed to retrieve business object data list',
|
|
112
116
|
data: [],
|
|
113
117
|
};
|
|
114
118
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import debounce from 'lodash/debounce';
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/** Data request debounce interval in milliseconds for components, used to merge repeated triggers within a short time */
|
|
5
5
|
export const REQUEST_DEBOUNCE_MS = 280;
|
|
6
6
|
|
|
7
|
-
/** lodash debounce
|
|
7
|
+
/** lodash debounce instance (avoids dependency on @types/lodash) */
|
|
8
8
|
export type DebouncedRequestFn = {
|
|
9
9
|
(): void;
|
|
10
10
|
cancel(): void;
|
|
@@ -12,7 +12,7 @@ export type DebouncedRequestFn = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Trailing debounce: executes only once after a pause during continuous triggers, suitable for filter / props rapid update scenarios.
|
|
16
16
|
*/
|
|
17
17
|
export function createRequestDebounce(
|
|
18
18
|
fn: () => void,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Table simpleTable specific: formatting, history snapshots, trends and cell rendering
|
|
3
3
|
*/
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { Card, Popover } from 'antd';
|
|
@@ -7,7 +7,7 @@ import moment from 'moment';
|
|
|
7
7
|
|
|
8
8
|
import { toFiniteNumber } from './common';
|
|
9
9
|
|
|
10
|
-
/**
|
|
10
|
+
/** Period-start opportunity snapshot (associated with current row id) */
|
|
11
11
|
export interface HistoryOppSnap {
|
|
12
12
|
id: string;
|
|
13
13
|
closeDate: unknown;
|
|
@@ -67,7 +67,7 @@ function closeDateTrend(
|
|
|
67
67
|
return 'same';
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
70
|
+
/** Custom SQL return row: array column order matches HISTORY_QUERY_FIELDS; or object fields */
|
|
71
71
|
export function historyRowToSnap(row: unknown): HistoryOppSnap | null {
|
|
72
72
|
if (row == null) return null;
|
|
73
73
|
if (Array.isArray(row)) {
|
|
@@ -91,9 +91,7 @@ export function historyRowToSnap(row: unknown): HistoryOppSnap | null {
|
|
|
91
91
|
opportunityName: String(
|
|
92
92
|
o.opportunity_1_opportunityName ?? o.opportunityName ?? '',
|
|
93
93
|
),
|
|
94
|
-
saleStageId: String(
|
|
95
|
-
o.opportunity_1_saleStageId ?? o.saleStageId ?? '',
|
|
96
|
-
),
|
|
94
|
+
saleStageId: String(o.opportunity_1_saleStageId ?? o.saleStageId ?? ''),
|
|
97
95
|
money: o.opportunity_1_money ?? o.money,
|
|
98
96
|
};
|
|
99
97
|
}
|
|
@@ -123,7 +121,7 @@ export function formatActivityDateTime(value: unknown): string {
|
|
|
123
121
|
return m.isValid() ? m.format('YYYY-MM-DD HH:mm:ss') : String(value);
|
|
124
122
|
}
|
|
125
123
|
|
|
126
|
-
/** customItem246/247
|
|
124
|
+
/** customItem246/247: change record time (ms/s timestamp or moment-parseable value) -> YYYY-MM-DD */
|
|
127
125
|
export function formatChangeRecordedAt(value: unknown): string {
|
|
128
126
|
if (value == null || value === '') return '—';
|
|
129
127
|
const n = toFiniteNumber(value);
|
|
@@ -136,7 +134,7 @@ export function formatChangeRecordedAt(value: unknown): string {
|
|
|
136
134
|
return m.isValid() ? m.format('YYYY-MM-DD') : String(value);
|
|
137
135
|
}
|
|
138
136
|
|
|
139
|
-
/**
|
|
137
|
+
/** Money column: $ prefix + thousands separator (no abbreviation) */
|
|
140
138
|
export function formatMoneyCell(value: unknown): string {
|
|
141
139
|
if (value == null || value === '') return '—';
|
|
142
140
|
const n = parseFloat(String(value).replace(/,/g, '').trim());
|
|
@@ -150,8 +148,10 @@ export function formatMoneyCell(value: unknown): string {
|
|
|
150
148
|
return `$${part}`;
|
|
151
149
|
}
|
|
152
150
|
|
|
153
|
-
/** oppHealthAssessmentLevel
|
|
154
|
-
export function renderOppHealthAssessmentLevel(
|
|
151
|
+
/** oppHealthAssessmentLevel: numeric value -> health label (with colored background) */
|
|
152
|
+
export function renderOppHealthAssessmentLevel(
|
|
153
|
+
value: unknown,
|
|
154
|
+
): React.ReactNode {
|
|
155
155
|
if (value == null || value === '') return '—';
|
|
156
156
|
const n = toFiniteNumber(value);
|
|
157
157
|
if (n === 5) {
|
|
@@ -160,7 +160,9 @@ export function renderOppHealthAssessmentLevel(value: unknown): React.ReactNode
|
|
|
160
160
|
);
|
|
161
161
|
}
|
|
162
162
|
if (n === 6) {
|
|
163
|
-
return
|
|
163
|
+
return (
|
|
164
|
+
<span className="opp-health-tag opp-health-tag--yellow">Health</span>
|
|
165
|
+
);
|
|
164
166
|
}
|
|
165
167
|
if (n === 7) {
|
|
166
168
|
return <span className="opp-health-tag opp-health-tag--red">Risk</span>;
|
|
@@ -172,7 +174,7 @@ export function renderOppHealthAssessmentLevel(value: unknown): React.ReactNode
|
|
|
172
174
|
}
|
|
173
175
|
|
|
174
176
|
/**
|
|
175
|
-
*
|
|
177
|
+
* Percentage column: appends %; API commonly returns 0-1 decimal or 0-100 numeric, both displayed in readable format
|
|
176
178
|
*/
|
|
177
179
|
export function formatPercentCell(value: unknown): string {
|
|
178
180
|
if (value == null || value === '') return '—';
|
|
@@ -205,7 +207,9 @@ export function moneyChangeTooltipTitle(
|
|
|
205
207
|
<div className="simpleTable-change-tip__detail">
|
|
206
208
|
{formatMoneyCell(h.money)}
|
|
207
209
|
{' → '}
|
|
208
|
-
<span className="simpleTable-change-tip__to">
|
|
210
|
+
<span className="simpleTable-change-tip__to">
|
|
211
|
+
{formatMoneyCell(current)}
|
|
212
|
+
</span>
|
|
209
213
|
</div>
|
|
210
214
|
<div className="simpleTable-change-tip__meta">
|
|
211
215
|
Changed on {formatChangeRecordedAt(preset)}
|
|
@@ -227,7 +231,9 @@ export function closeDateChangeTooltipTitle(
|
|
|
227
231
|
<div className="simpleTable-change-tip__detail">
|
|
228
232
|
{formatCloseDate(h.closeDate)}
|
|
229
233
|
{' → '}
|
|
230
|
-
<span className="simpleTable-change-tip__to">
|
|
234
|
+
<span className="simpleTable-change-tip__to">
|
|
235
|
+
{formatCloseDate(current)}
|
|
236
|
+
</span>
|
|
231
237
|
</div>
|
|
232
238
|
<div className="simpleTable-change-tip__meta">
|
|
233
239
|
Changed on {formatChangeRecordedAt(preset)}
|
|
@@ -268,22 +274,17 @@ export function renderTrendCell(args: {
|
|
|
268
274
|
);
|
|
269
275
|
}
|
|
270
276
|
|
|
271
|
-
/** demo.html winRateTooltip
|
|
277
|
+
/** demo.html winRateTooltip style */
|
|
272
278
|
export function renderWinRateHoverCard(record: any): React.ReactNode {
|
|
273
279
|
const baseline = record?.customItem244__c;
|
|
274
280
|
const positives = record?.customItem241__c;
|
|
275
281
|
const negatives = record?.customItem242__c;
|
|
276
282
|
const winRate = record?.customItem239__c;
|
|
277
283
|
|
|
278
|
-
const fmtLine = (v: unknown) =>
|
|
279
|
-
v == null || v === '' ? '—' : String(v);
|
|
284
|
+
const fmtLine = (v: unknown) => (v == null || v === '' ? '—' : String(v));
|
|
280
285
|
|
|
281
286
|
return (
|
|
282
|
-
<Card
|
|
283
|
-
size="small"
|
|
284
|
-
bordered={false}
|
|
285
|
-
className="simpleTable-winrate-card"
|
|
286
|
-
>
|
|
287
|
+
<Card size="small" bordered={false} className="simpleTable-winrate-card">
|
|
287
288
|
<div className="simpleTable-winrate-card__baseline">
|
|
288
289
|
Baseline Probability{' '}
|
|
289
290
|
<span className="simpleTable-winrate-card__baseline-val">
|
|
@@ -291,12 +292,16 @@ export function renderWinRateHoverCard(record: any): React.ReactNode {
|
|
|
291
292
|
</span>
|
|
292
293
|
</div>
|
|
293
294
|
<hr className="simpleTable-winrate-card__hr" />
|
|
294
|
-
<div className="simpleTable-winrate-card__section-title">
|
|
295
|
+
<div className="simpleTable-winrate-card__section-title">
|
|
296
|
+
Positive Factors
|
|
297
|
+
</div>
|
|
295
298
|
<div className="simpleTable-winrate-card__positives">
|
|
296
299
|
{fmtLine(positives)}
|
|
297
300
|
</div>
|
|
298
301
|
<hr className="simpleTable-winrate-card__hr" />
|
|
299
|
-
<div className="simpleTable-winrate-card__section-title">
|
|
302
|
+
<div className="simpleTable-winrate-card__section-title">
|
|
303
|
+
Negative Factors
|
|
304
|
+
</div>
|
|
300
305
|
<div className="simpleTable-winrate-card__negatives">
|
|
301
306
|
{fmtLine(negatives)}
|
|
302
307
|
</div>
|
|
@@ -311,7 +316,7 @@ export function renderWinRateHoverCard(record: any): React.ReactNode {
|
|
|
311
316
|
);
|
|
312
317
|
}
|
|
313
318
|
|
|
314
|
-
/**
|
|
319
|
+
/** Summary amount display (number format consistent with formatMoneyCell) */
|
|
315
320
|
export function formatMoneySummaryAmount(n: number): string {
|
|
316
321
|
const part = Number.isInteger(n)
|
|
317
322
|
? n.toLocaleString('en-US')
|
|
@@ -322,7 +327,7 @@ export function formatMoneySummaryAmount(n: number): string {
|
|
|
322
327
|
return `$${part}`;
|
|
323
328
|
}
|
|
324
329
|
|
|
325
|
-
/**
|
|
330
|
+
/** Sum row amounts by field apiKey (parsing rules consistent with money column) */
|
|
326
331
|
export function sumMoneyRows(rows: any[], moneyFieldKey: string): number {
|
|
327
332
|
let total = 0;
|
|
328
333
|
for (const row of rows) {
|