neo-cmp-cli 1.13.16 → 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/README.md +2 -1
- package/dist/index2.js +1 -1
- package/dist/main2.js +1 -1
- package/dist/neo/env.js +1 -1
- package/dist/neo/neoLogin.js +1 -1
- package/dist/neo/pushCmp.js +1 -1
- package/dist/package.json.js +1 -1
- package/package.json +3 -2
- package/template/antd-custom-cmp-template/package.json +1 -1
- package/template/asset-manage-template/docs/README.md +1 -232
- package/template/asset-manage-template/package.json +2 -2
- package/template/echarts-custom-cmp-template/package.json +1 -1
- package/template/empty-custom-cmp-template/package.json +2 -2
- package/template/map-custom-cmp-template/package.json +1 -1
- package/template/neo-bi-cmps/neo.config.js +7 -1
- package/template/neo-bi-cmps/package.json +8 -7
- package/template/neo-bi-cmps/public/403.html +77 -0
- package/template/neo-bi-cmps/public/demo.html +2453 -0
- package/template/neo-bi-cmps/src/assets/icon/barChart.svg +1 -0
- package/template/neo-bi-cmps/src/assets/icon/card.svg +1 -0
- package/template/neo-bi-cmps/src/assets/icon/filter.svg +1 -0
- package/template/neo-bi-cmps/src/assets/icon/funnel.svg +1 -0
- package/template/neo-bi-cmps/src/assets/icon/tab.svg +1 -0
- package/template/neo-bi-cmps/src/components/filterBar__c/README.md +3 -14
- package/template/neo-bi-cmps/src/components/filterBar__c/common.scss +29 -0
- package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +668 -146
- package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +26 -48
- package/template/neo-bi-cmps/src/components/filterBar__c/style.scss +46 -139
- package/template/neo-bi-cmps/src/components/targetNumber__c/customStyleConfig/index.tsx +11 -10
- package/template/neo-bi-cmps/src/components/targetNumber__c/index.tsx +9 -16
- package/template/neo-bi-cmps/src/components/targetNumber__c/model.ts +1 -1
- package/template/neo-bi-cmps/src/utils/common.ts +231 -0
- package/template/neo-bi-cmps/src/utils/filter2chartFilter.ts +268 -0
- package/template/neo-bi-cmps/src/utils/filterBar.ts +140 -0
- package/template/neo-bi-cmps/src/utils/pipelineFunnel.ts +341 -0
- package/template/{neo-h5-cmps/src/utils/queryObjectData.ts → neo-bi-cmps/src/utils/queryByCustomSQL.ts} +18 -13
- package/template/neo-bi-cmps/src/utils/requestDebounce.ts +22 -0
- package/template/neo-bi-cmps/src/utils/simpleTable.tsx +344 -0
- package/template/neo-bi-cmps/src/utils/stageSwitch.ts +15 -0
- package/template/neo-bi-cmps/src/utils/stageTimeChart.ts +90 -0
- package/template/neo-bi-cmps/src/utils/targetNumber.ts +12 -0
- package/template/neo-custom-cmp-template/docs/README.md +0 -231
- package/template/neo-custom-cmp-template/package.json +2 -2
- package/template/neo-h5-cmps/package.json +2 -2
- 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-order-cmps/package.json +2 -2
- package/template/neo-pipeline-cmps/.prettierrc.js +12 -0
- package/template/neo-pipeline-cmps/@types/neo-ui-common.d.ts +36 -0
- package/template/neo-pipeline-cmps/README.md +99 -0
- package/template/neo-pipeline-cmps/commitlint.config.js +59 -0
- package/template/neo-pipeline-cmps/neo.config.js +135 -0
- package/template/neo-pipeline-cmps/package.json +66 -0
- package/template/neo-pipeline-cmps/public/403.html +77 -0
- package/template/neo-pipeline-cmps/public/css/base.css +283 -0
- package/template/neo-pipeline-cmps/public/demo.html +2453 -0
- package/template/neo-pipeline-cmps/public/scripts/app/bluebird.js +6679 -0
- package/template/neo-pipeline-cmps/public/template.html +13 -0
- package/template/neo-pipeline-cmps/src/assets/css/common.scss +127 -0
- package/template/neo-pipeline-cmps/src/assets/css/mixin.scss +47 -0
- package/template/neo-pipeline-cmps/src/assets/icon/barChart.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/icon/card.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/icon/filter.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/icon/funnel.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/icon/tab.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/AIBtn.gif +0 -0
- package/template/neo-pipeline-cmps/src/assets/img/NeoCRM.jpg +0 -0
- package/template/neo-pipeline-cmps/src/assets/img/aiLogo.png +0 -0
- package/template/neo-pipeline-cmps/src/assets/img/card-list.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/contact-form.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/custom-form.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/custom-widget.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/data-list.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/detail.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/favicon.png +0 -0
- package/template/neo-pipeline-cmps/src/assets/img/map.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/search.svg +1 -0
- package/template/neo-pipeline-cmps/src/assets/img/table.svg +1 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/README.md +24 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/common.scss +29 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/index.tsx +731 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/model.ts +52 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/style.scss +119 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/README.md +39 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/index.tsx +416 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +80 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/style.scss +83 -0
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/index.tsx +470 -0
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/model.ts +45 -0
- package/template/neo-pipeline-cmps/src/components/showHealthResult__c/style.scss +137 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/README.md +89 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/common.scss +195 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/index.tsx +667 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/model.ts +124 -0
- package/template/neo-pipeline-cmps/src/components/simpleTable__c/style.scss +192 -0
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/README.md +36 -0
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/index.tsx +513 -0
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/model.ts +71 -0
- package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageSwitch__c/style.scss +4 -2
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/README.md +37 -0
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/index.tsx +455 -0
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/model.ts +106 -0
- package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageTimeChart__c/style.scss +3 -2
- package/template/neo-pipeline-cmps/src/utils/common.ts +229 -0
- package/template/neo-pipeline-cmps/src/utils/filter2chartFilter.ts +266 -0
- package/template/neo-pipeline-cmps/src/utils/filterBar.ts +140 -0
- package/template/neo-pipeline-cmps/src/utils/pipelineFunnel.ts +343 -0
- package/template/neo-pipeline-cmps/src/utils/queryByCustomSQL.ts +121 -0
- package/template/neo-pipeline-cmps/src/utils/requestDebounce.ts +22 -0
- package/template/neo-pipeline-cmps/src/utils/simpleTable.tsx +349 -0
- package/template/neo-pipeline-cmps/src/utils/stageSwitch.ts +15 -0
- package/template/neo-pipeline-cmps/src/utils/stageTimeChart.ts +90 -0
- package/template/neo-pipeline-cmps/src/utils/targetNumber.ts +12 -0
- package/template/neo-pipeline-cmps/tsconfig.json +40 -0
- package/template/neo-web-entity-grid/package.json +2 -2
- package/template/neo-web-form/package.json +2 -2
- 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/react-ts-custom-cmp-template/package.json +1 -1
- package/template/vue2-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-bi-cmps/.npmrc copy +0 -1
- package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/README.md +0 -52
- package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/index.tsx +0 -183
- package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/model.ts +0 -90
- package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/style.scss +0 -218
- package/template/neo-bi-cmps/src/components/forecastChart__c/README.md +0 -31
- package/template/neo-bi-cmps/src/components/forecastChart__c/index.tsx +0 -158
- package/template/neo-bi-cmps/src/components/forecastChart__c/model.ts +0 -40
- package/template/neo-bi-cmps/src/components/forecastChart__c/style.scss +0 -154
- package/template/neo-bi-cmps/src/components/forecastGrid__c/README.md +0 -36
- package/template/neo-bi-cmps/src/components/forecastGrid__c/index.tsx +0 -86
- package/template/neo-bi-cmps/src/components/forecastGrid__c/model.ts +0 -62
- package/template/neo-bi-cmps/src/components/forecastGrid__c/style.scss +0 -48
- package/template/neo-bi-cmps/src/components/gapCloser__c/README.md +0 -24
- package/template/neo-bi-cmps/src/components/gapCloser__c/index.tsx +0 -100
- package/template/neo-bi-cmps/src/components/gapCloser__c/model.ts +0 -46
- package/template/neo-bi-cmps/src/components/gapCloser__c/style.scss +0 -60
- package/template/neo-bi-cmps/src/components/kpiCards__c/README.md +0 -35
- package/template/neo-bi-cmps/src/components/kpiCards__c/index.tsx +0 -70
- package/template/neo-bi-cmps/src/components/kpiCards__c/model.ts +0 -50
- package/template/neo-bi-cmps/src/components/kpiCards__c/style.scss +0 -33
- package/template/neo-bi-cmps/src/components/oppList__c/README.md +0 -52
- package/template/neo-bi-cmps/src/components/oppList__c/index.tsx +0 -285
- package/template/neo-bi-cmps/src/components/oppList__c/model.ts +0 -86
- package/template/neo-bi-cmps/src/components/oppList__c/style.scss +0 -133
- package/template/neo-bi-cmps/src/components/pipelineFunnel__c/README.md +0 -39
- package/template/neo-bi-cmps/src/components/pipelineFunnel__c/index.tsx +0 -130
- package/template/neo-bi-cmps/src/components/pipelineFunnel__c/model.ts +0 -66
- package/template/neo-bi-cmps/src/components/pipelineFunnel__c/style.scss +0 -133
- package/template/neo-bi-cmps/src/components/stageSwitch__c/README.md +0 -36
- package/template/neo-bi-cmps/src/components/stageSwitch__c/index.tsx +0 -118
- package/template/neo-bi-cmps/src/components/stageSwitch__c/model.ts +0 -92
- package/template/neo-bi-cmps/src/components/stageTimeChart__c/README.md +0 -37
- package/template/neo-bi-cmps/src/components/stageTimeChart__c/index.tsx +0 -126
- package/template/neo-bi-cmps/src/components/stageTimeChart__c/model.ts +0 -57
- package/template/neo-bi-cmps/src/components/tabSwitch__c/README.md +0 -37
- package/template/neo-bi-cmps/src/components/tabSwitch__c/index.tsx +0 -80
- package/template/neo-bi-cmps/src/components/tabSwitch__c/model.ts +0 -45
- package/template/neo-bi-cmps/src/components/tabSwitch__c/style.scss +0 -37
- package/template/neo-bi-cmps/src/utils/axiosFetcher.ts +0 -37
- package/template/neo-bi-cmps/src/utils/queryObjectData.ts +0 -76
- package/template/neo-bi-cmps/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/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
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline funnel pipelineFunnel specific (parse table rows, ECharts config)
|
|
3
|
+
*/
|
|
4
|
+
import * as echarts from 'echarts';
|
|
5
|
+
|
|
6
|
+
import { formatAmountDisplay } from './common';
|
|
7
|
+
|
|
8
|
+
export const FUNNEL_COLORS = [
|
|
9
|
+
'#3b82f6',
|
|
10
|
+
'#22c55e',
|
|
11
|
+
'#f59e0b',
|
|
12
|
+
'#8b5cf6',
|
|
13
|
+
'#ec4899',
|
|
14
|
+
'#06b6d4',
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export interface FunnelStage {
|
|
18
|
+
name: string;
|
|
19
|
+
amountNum: number;
|
|
20
|
+
amount: string;
|
|
21
|
+
count: number;
|
|
22
|
+
conversionRate?: string;
|
|
23
|
+
color: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function parseAmountToNumber(raw: unknown): number {
|
|
27
|
+
if (raw === null || raw === undefined) return 0;
|
|
28
|
+
const s = String(raw)
|
|
29
|
+
.replace(/,/g, '')
|
|
30
|
+
.replace(/[^\d.-]/g, '');
|
|
31
|
+
const n = parseFloat(s);
|
|
32
|
+
return Number.isFinite(n) ? n : 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function formatSharePercent(amount: number, total: number): string {
|
|
36
|
+
if (!total || !Number.isFinite(amount)) return '—';
|
|
37
|
+
return `${((amount / total) * 100).toFixed(1)}%`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isTruthyProp(v: unknown): boolean {
|
|
41
|
+
return v === true || v === 'true' || v === 1 || v === '1';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Ignore case and consecutive whitespace when comparing with backend stage names */
|
|
45
|
+
export function isClosedLostStageName(raw: unknown): boolean {
|
|
46
|
+
const n = String(raw ?? '')
|
|
47
|
+
.trim()
|
|
48
|
+
.toLowerCase()
|
|
49
|
+
.replace(/\s+/g, ' ');
|
|
50
|
+
return n === 'closed lost';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Remove Closed Lost stage rows, keep header; must be called before parseRowsToStages to avoid counting in total and percentage */
|
|
54
|
+
export function filterOutClosedLostRows(table: unknown[][]): unknown[][] {
|
|
55
|
+
if (!Array.isArray(table) || table.length < 2) return table;
|
|
56
|
+
const header = table[0];
|
|
57
|
+
const rest = table
|
|
58
|
+
.slice(1)
|
|
59
|
+
.filter((row) => !(Array.isArray(row) && isClosedLostStageName(row[0])));
|
|
60
|
+
return [header, ...rest];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function parseRowsToStages(rows: unknown[][]): {
|
|
64
|
+
stages: FunnelStage[];
|
|
65
|
+
totalAmountNum: number;
|
|
66
|
+
} {
|
|
67
|
+
if (!Array.isArray(rows) || rows.length < 2) {
|
|
68
|
+
return { stages: [], totalAmountNum: 0 };
|
|
69
|
+
}
|
|
70
|
+
const dataRows = rows.slice(1);
|
|
71
|
+
const stages: FunnelStage[] = [];
|
|
72
|
+
const amountNums: number[] = [];
|
|
73
|
+
let totalAmountNum = 0;
|
|
74
|
+
|
|
75
|
+
for (let i = 0; i < dataRows.length; i++) {
|
|
76
|
+
const row = dataRows[i];
|
|
77
|
+
if (!Array.isArray(row) || row.length < 4) continue;
|
|
78
|
+
|
|
79
|
+
const name = String(row[0] ?? '');
|
|
80
|
+
const countRaw = row[2];
|
|
81
|
+
const amountNum = parseAmountToNumber(row[3]);
|
|
82
|
+
totalAmountNum += amountNum;
|
|
83
|
+
amountNums.push(amountNum);
|
|
84
|
+
|
|
85
|
+
const countNum = parseInt(String(countRaw).replace(/\D/g, ''), 10);
|
|
86
|
+
const count = Number.isFinite(countNum) ? countNum : 0;
|
|
87
|
+
|
|
88
|
+
stages.push({
|
|
89
|
+
name,
|
|
90
|
+
amountNum,
|
|
91
|
+
amount: formatAmountDisplay(amountNum),
|
|
92
|
+
count,
|
|
93
|
+
color: FUNNEL_COLORS[i % FUNNEL_COLORS.length],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (let j = 0; j < stages.length; j++) {
|
|
98
|
+
stages[j].conversionRate = formatSharePercent(
|
|
99
|
+
amountNums[j],
|
|
100
|
+
totalAmountNum,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { stages, totalAmountNum };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function funnelLayerValue(index: number, total: number): number {
|
|
108
|
+
return Math.max(total - index, 1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function funnelValueForStage(
|
|
112
|
+
stage: FunnelStage,
|
|
113
|
+
index: number,
|
|
114
|
+
stageCount: number,
|
|
115
|
+
useClassicFunnelShape: boolean,
|
|
116
|
+
): number {
|
|
117
|
+
if (useClassicFunnelShape) {
|
|
118
|
+
return funnelLayerValue(index, stageCount);
|
|
119
|
+
}
|
|
120
|
+
const v = stage.amountNum;
|
|
121
|
+
if (Number.isFinite(v) && v > 0) return v;
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Classic funnel: width still controlled by value (order); layer height allocated by amount proportion (consistent with series.gap, unit is px) */
|
|
126
|
+
export function classicFunnelLayerHeightPercents(
|
|
127
|
+
amountNums: number[],
|
|
128
|
+
viewHeightPx: number,
|
|
129
|
+
gapPx: number,
|
|
130
|
+
): string[] {
|
|
131
|
+
const n = amountNums.length;
|
|
132
|
+
if (n === 0) return [];
|
|
133
|
+
const h = Math.max(viewHeightPx, 1);
|
|
134
|
+
const factor = (h - gapPx * Math.max(n - 1, 0)) / h;
|
|
135
|
+
const positives = amountNums.map((v) =>
|
|
136
|
+
Number.isFinite(v) && v > 0 ? v : 0,
|
|
137
|
+
);
|
|
138
|
+
const sum = positives.reduce((a, v) => a + v, 0);
|
|
139
|
+
if (sum <= 0) {
|
|
140
|
+
const even = (100 / n) * factor;
|
|
141
|
+
return Array(n).fill(`${even}%`);
|
|
142
|
+
}
|
|
143
|
+
return positives.map((v) => `${100 * (v / sum) * factor}%`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface BuildFunnelChartOptionParams {
|
|
147
|
+
/** Container height (px), used in classic mode to calculate layer height percentages; should re-setOption when changed */
|
|
148
|
+
chartViewHeightPx?: number;
|
|
149
|
+
funnelGap?: number;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function buildFunnelChartOption(
|
|
153
|
+
stages: FunnelStage[],
|
|
154
|
+
useClassicFunnelShape: boolean,
|
|
155
|
+
params?: BuildFunnelChartOptionParams,
|
|
156
|
+
): echarts.EChartsOption {
|
|
157
|
+
const n = stages.length;
|
|
158
|
+
const values = stages.map((s, i) =>
|
|
159
|
+
funnelValueForStage(s, i, n, useClassicFunnelShape),
|
|
160
|
+
);
|
|
161
|
+
const funnelMax = useClassicFunnelShape
|
|
162
|
+
? Math.max(n, 1)
|
|
163
|
+
: Math.max(...values, 1);
|
|
164
|
+
|
|
165
|
+
const gap = params?.funnelGap ?? 2;
|
|
166
|
+
const viewH = params?.chartViewHeightPx;
|
|
167
|
+
const classicHeights =
|
|
168
|
+
useClassicFunnelShape && viewH != null && viewH > 0
|
|
169
|
+
? classicFunnelLayerHeightPercents(
|
|
170
|
+
stages.map((s) => s.amountNum),
|
|
171
|
+
viewH,
|
|
172
|
+
gap,
|
|
173
|
+
)
|
|
174
|
+
: null;
|
|
175
|
+
|
|
176
|
+
const coloredData = stages.map((s, i) => ({
|
|
177
|
+
name: s.name,
|
|
178
|
+
value: values[i],
|
|
179
|
+
itemStyle: {
|
|
180
|
+
color: s.color,
|
|
181
|
+
...(classicHeights?.[i] ? { height: classicHeights[i] } : {}),
|
|
182
|
+
},
|
|
183
|
+
}));
|
|
184
|
+
|
|
185
|
+
const alignData = stages.map((s, i) => ({
|
|
186
|
+
name: s.name,
|
|
187
|
+
value: values[i],
|
|
188
|
+
...(classicHeights?.[i]
|
|
189
|
+
? { itemStyle: { height: classicHeights[i] } }
|
|
190
|
+
: {}),
|
|
191
|
+
}));
|
|
192
|
+
|
|
193
|
+
const labelLineStyle = {
|
|
194
|
+
length: 28,
|
|
195
|
+
lineStyle: {
|
|
196
|
+
width: 1,
|
|
197
|
+
color: '#d0d0d0',
|
|
198
|
+
type: 'solid' as const,
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const labelLayoutLeft = () => ({
|
|
203
|
+
x: 0,
|
|
204
|
+
align: 'left' as const,
|
|
205
|
+
dx: 4,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const labelLayoutRight = () => ({
|
|
209
|
+
x: '100%',
|
|
210
|
+
align: 'right' as const,
|
|
211
|
+
dx: -6,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const funnelLayout = {
|
|
215
|
+
left: '12%',
|
|
216
|
+
top: 4,
|
|
217
|
+
bottom: 4,
|
|
218
|
+
width: '76%',
|
|
219
|
+
min: 0,
|
|
220
|
+
max: funnelMax,
|
|
221
|
+
minSize: '0%',
|
|
222
|
+
maxSize: '100%',
|
|
223
|
+
sort: 'none' as const,
|
|
224
|
+
gap: 1,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
backgroundColor: 'transparent',
|
|
229
|
+
legend: { show: false },
|
|
230
|
+
tooltip: {
|
|
231
|
+
trigger: 'item',
|
|
232
|
+
backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
|
233
|
+
borderColor: '#333',
|
|
234
|
+
textStyle: { color: '#fff' },
|
|
235
|
+
formatter: (params: unknown) => {
|
|
236
|
+
const p = params as { dataIndex?: number; seriesName?: string };
|
|
237
|
+
const idx = p.dataIndex ?? 0;
|
|
238
|
+
const st = stages[idx];
|
|
239
|
+
if (!st) return '';
|
|
240
|
+
return [
|
|
241
|
+
`${st.name}`,
|
|
242
|
+
`Percentage: ${st.conversionRate ?? '—'}`,
|
|
243
|
+
`Amount: ${st.amount}`,
|
|
244
|
+
`Quantity: ${st.count}`,
|
|
245
|
+
].join('<br/>');
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
series: [
|
|
249
|
+
{
|
|
250
|
+
name: 'Pipeline',
|
|
251
|
+
type: 'funnel',
|
|
252
|
+
z: 1,
|
|
253
|
+
...funnelLayout,
|
|
254
|
+
itemStyle: {
|
|
255
|
+
borderColor: '#fff',
|
|
256
|
+
borderWidth: 1,
|
|
257
|
+
},
|
|
258
|
+
emphasis: {
|
|
259
|
+
itemStyle: {
|
|
260
|
+
shadowBlur: 10,
|
|
261
|
+
shadowColor: 'rgba(0, 0, 0, 0.15)',
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
label: {
|
|
265
|
+
show: true,
|
|
266
|
+
position: 'left',
|
|
267
|
+
color: '#888',
|
|
268
|
+
fontSize: 11,
|
|
269
|
+
formatter: (p: { dataIndex?: number }) => {
|
|
270
|
+
const idx = typeof p.dataIndex === 'number' ? p.dataIndex : 0;
|
|
271
|
+
return stages[idx]?.conversionRate ?? '—';
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
labelLine: {
|
|
275
|
+
show: true,
|
|
276
|
+
...labelLineStyle,
|
|
277
|
+
},
|
|
278
|
+
labelLayout: labelLayoutLeft,
|
|
279
|
+
data: coloredData,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: 'PipelineLabels',
|
|
283
|
+
type: 'funnel',
|
|
284
|
+
z: 2,
|
|
285
|
+
animation: false,
|
|
286
|
+
animationDurationUpdate: 0,
|
|
287
|
+
...funnelLayout,
|
|
288
|
+
itemStyle: {
|
|
289
|
+
color: 'rgba(0,0,0,0)',
|
|
290
|
+
borderWidth: 0,
|
|
291
|
+
},
|
|
292
|
+
emphasis: { disabled: true },
|
|
293
|
+
label: {
|
|
294
|
+
show: true,
|
|
295
|
+
position: 'right',
|
|
296
|
+
color: '#555',
|
|
297
|
+
fontSize: 11,
|
|
298
|
+
formatter: (p: { dataIndex?: number }) => {
|
|
299
|
+
const idx = typeof p.dataIndex === 'number' ? p.dataIndex : 0;
|
|
300
|
+
const st = stages[idx];
|
|
301
|
+
return st ? `${st.amount} / ${st.count}` : '';
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
labelLine: {
|
|
305
|
+
show: true,
|
|
306
|
+
...labelLineStyle,
|
|
307
|
+
},
|
|
308
|
+
labelLayout: labelLayoutRight,
|
|
309
|
+
data: alignData,
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: 'PipelineName',
|
|
313
|
+
type: 'funnel',
|
|
314
|
+
z: 3,
|
|
315
|
+
silent: true,
|
|
316
|
+
animation: false,
|
|
317
|
+
animationDurationUpdate: 0,
|
|
318
|
+
tooltip: { show: false },
|
|
319
|
+
...funnelLayout,
|
|
320
|
+
itemStyle: {
|
|
321
|
+
color: 'rgba(0,0,0,0)',
|
|
322
|
+
borderWidth: 0,
|
|
323
|
+
},
|
|
324
|
+
emphasis: { disabled: true },
|
|
325
|
+
label: {
|
|
326
|
+
show: true,
|
|
327
|
+
position: 'inside',
|
|
328
|
+
color: '#fff',
|
|
329
|
+
fontSize: 12,
|
|
330
|
+
fontWeight: 600,
|
|
331
|
+
textBorderColor: 'rgba(0,0,0,0.4)',
|
|
332
|
+
textBorderWidth: 1,
|
|
333
|
+
formatter: (p: { name?: string; dataIndex?: number }) => {
|
|
334
|
+
const idx = typeof p.dataIndex === 'number' ? p.dataIndex : 0;
|
|
335
|
+
return stages[idx]?.name ?? p.name ?? '';
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
labelLine: { show: false },
|
|
339
|
+
data: alignData,
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
};
|
|
343
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { request as axiosFetcher } from 'neo-open-api';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Common query Open APIs are stored here
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Normalize where to SQL fragment: string is used directly; array items are joined with 'and' */
|
|
9
|
+
function normalizeWhere(where: unknown): string {
|
|
10
|
+
if (where == null || where === '') {
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
if (typeof where === 'string') {
|
|
14
|
+
return where.trim();
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(where)) {
|
|
17
|
+
return where
|
|
18
|
+
.map((part) => (part == null ? '' : String(part).trim()))
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.join(' and ');
|
|
21
|
+
}
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Query BI data by SQL
|
|
26
|
+
export const queryByCustomSQL = async (options?: any) => {
|
|
27
|
+
const apiUrl = '/rest/neobi/v2.0/query/queryByCustomSQL';
|
|
28
|
+
const curOptions = options || {};
|
|
29
|
+
const xObjectApiKey = curOptions.xObjectApiKey || '';
|
|
30
|
+
const fields = Object.assign([], curOptions.fields || []);
|
|
31
|
+
const page = curOptions.page || 1;
|
|
32
|
+
const pageSize = curOptions.pageSize || 10;
|
|
33
|
+
|
|
34
|
+
// Automatically add objectId field
|
|
35
|
+
if (!fields.includes('id')) {
|
|
36
|
+
fields.push('id');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Calculate pagination offset
|
|
40
|
+
const offset = (page - 1) * pageSize;
|
|
41
|
+
|
|
42
|
+
// Build SQL query
|
|
43
|
+
let querySql = `select ${fields.join(',')} from ${xObjectApiKey}`;
|
|
44
|
+
|
|
45
|
+
// Add sort conditions (if any)
|
|
46
|
+
if (curOptions.orderBy) {
|
|
47
|
+
querySql += ` order by ${curOptions.orderBy}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
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
|
+
*
|
|
61
|
+
* `where` can be a string, or a string array (multiple items joined with 'and' by default, equivalent to `a and b`).
|
|
62
|
+
*/
|
|
63
|
+
const whereClause = normalizeWhere(curOptions.where);
|
|
64
|
+
|
|
65
|
+
if (whereClause) {
|
|
66
|
+
querySql += ` where ${whereClause}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (curOptions.page || curOptions.pageSize) {
|
|
70
|
+
// Add pagination limit
|
|
71
|
+
querySql += ` limit ${offset},${pageSize}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const querySqlForm = new URLSearchParams();
|
|
75
|
+
querySqlForm.set('sqlObject', querySql);
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const config = {
|
|
79
|
+
url: apiUrl,
|
|
80
|
+
method: 'POST',
|
|
81
|
+
data: querySqlForm.toString(),
|
|
82
|
+
headers: {
|
|
83
|
+
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const resultData = await axiosFetcher(config);
|
|
88
|
+
|
|
89
|
+
if (resultData.code === 200) {
|
|
90
|
+
const records = resultData.data || [];
|
|
91
|
+
return {
|
|
92
|
+
status: true,
|
|
93
|
+
code: resultData.code,
|
|
94
|
+
msg:
|
|
95
|
+
resultData.msg || 'Successfully retrieved business object data list',
|
|
96
|
+
totalSize: records.length,
|
|
97
|
+
data: records || [],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
status: false,
|
|
103
|
+
code: resultData.code,
|
|
104
|
+
msg: resultData.msg || 'Failed to retrieve business object data list',
|
|
105
|
+
data: [],
|
|
106
|
+
};
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error('Failed to retrieve business object data list:', error);
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
status: false,
|
|
112
|
+
msg:
|
|
113
|
+
error.msg ||
|
|
114
|
+
error.message ||
|
|
115
|
+
'Failed to retrieve business object data list',
|
|
116
|
+
data: [],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export default queryByCustomSQL;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import debounce from 'lodash/debounce';
|
|
3
|
+
|
|
4
|
+
/** Data request debounce interval in milliseconds for components, used to merge repeated triggers within a short time */
|
|
5
|
+
export const REQUEST_DEBOUNCE_MS = 280;
|
|
6
|
+
|
|
7
|
+
/** lodash debounce instance (avoids dependency on @types/lodash) */
|
|
8
|
+
export type DebouncedRequestFn = {
|
|
9
|
+
(): void;
|
|
10
|
+
cancel(): void;
|
|
11
|
+
flush(): void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Trailing debounce: executes only once after a pause during continuous triggers, suitable for filter / props rapid update scenarios.
|
|
16
|
+
*/
|
|
17
|
+
export function createRequestDebounce(
|
|
18
|
+
fn: () => void,
|
|
19
|
+
wait: number = REQUEST_DEBOUNCE_MS,
|
|
20
|
+
): DebouncedRequestFn {
|
|
21
|
+
return debounce(fn, wait) as DebouncedRequestFn;
|
|
22
|
+
}
|