neo-cmp-cli 1.13.15 → 1.13.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index2.js +1 -1
- package/dist/main2.js +1 -1
- package/dist/neo/neoLogin.js +1 -1
- package/dist/package.json.js +1 -1
- package/docs//351/200/232/347/224/250/344/273/243/347/220/206/346/216/245/345/217/243/forward.zip +0 -0
- package/docs//351/200/232/347/224/250/344/273/243/347/220/206/346/216/245/345/217/243//350/207/252/345/256/232/344/271/211API:/351/200/232/347/224/250/344/273/243/347/220/206/346/216/245/345/217/243/344/275/277/347/224/250/350/257/264/346/230/216.md +13 -0
- package/package.json +1 -1
- package/template/antd-custom-cmp-template/package.json +1 -1
- 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/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/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-bi-cmps/src/utils/queryByCustomSQL.ts +117 -0
- 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/package.json +2 -2
- package/template/neo-h5-cmps/package.json +2 -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 +124 -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 +730 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/model.ts +50 -0
- package/template/neo-pipeline-cmps/src/components/filterBar__c/style.scss +119 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/index.tsx +415 -0
- package/template/neo-pipeline-cmps/src/components/pipelineFunnel__c/model.ts +79 -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 +463 -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 +90 -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 +665 -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 +193 -0
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/index.tsx +511 -0
- package/template/neo-pipeline-cmps/src/components/stageSwitch__c/model.ts +70 -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/index.tsx +455 -0
- package/template/neo-pipeline-cmps/src/components/stageTimeChart__c/model.ts +103 -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 +268 -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 +117 -0
- package/template/neo-pipeline-cmps/src/utils/requestDebounce.ts +22 -0
- package/template/neo-pipeline-cmps/src/utils/simpleTable.tsx +344 -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/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/neo-bi-cmps/.npmrc copy +0 -1
- package/template/neo-bi-cmps/docs/gartner-pipeline-apis.md +0 -251
- package/template/neo-bi-cmps/docs/gartner-pipeline-prd.md +0 -389
- package/template/neo-bi-cmps/docs/neo-backend-dev/SKILL.md +0 -188
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/01-Trigger/345/274/200/345/217/221.md +0 -183
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/02-/350/207/252/345/256/232/344/271/211API/345/274/200/345/217/221.md +0 -196
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/03-SDK/345/267/245/345/205/267/347/261/273/346/216/245/345/217/243.md +0 -346
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/04-/350/256/241/345/210/222/344/275/234/344/270/232/345/274/200/345/217/221.md +0 -188
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/05-/351/241/265/351/235/242/345/274/200/345/217/221.md +0 -293
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/06-/346/265/201/347/250/213/346/211/251/345/261/225/345/274/200/345/217/221.md +0 -175
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/PaaS/345/271/263/345/217/260/345/274/200/345/217/221/346/211/213/345/206/214/350/247/243/350/257/273.md +0 -313
- package/template/neo-bi-cmps/docs/neo-backend-dev/references/auth-config.md +0 -77
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/deploy_server_script.py +0 -118
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/download_server_script.py +0 -74
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entity_desc.py +0 -69
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entitylist.py +0 -87
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/query_crm.py +0 -65
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/uninstall_server_script.py +0 -48
- package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/update_model_jar.py +0 -49
- package/template/neo-bi-cmps/docs/neo-frontend-dev/SKILL.md +0 -138
- package/template/neo-bi-cmps/docs/neo-frontend-dev/references/auth-config.md +0 -77
- package/template/neo-bi-cmps/docs/neo-frontend-dev/references/component-dev.md +0 -205
- package/template/neo-bi-cmps/docs/neo-frontend-dev/references/entityTable-example.md +0 -167
- package/template/neo-bi-cmps/docs/neo-frontend-dev/references/templates.md +0 -38
- package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entity_desc.py +0 -69
- package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entitylist.py +0 -87
- package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/query_crm.py +0 -65
- package/template/neo-bi-cmps/docs//350/264/246/345/217/267/347/233/270/345/205/263/344/277/241/346/201/257.md +0 -10
- 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/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/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/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-bi-cmps/{docs/prototype-pipeline-forecasting.html → public/demo.html} +0 -0
- /package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/pipelineFunnel__c/README.md +0 -0
- /package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageSwitch__c/README.md +0 -0
- /package/template/{neo-bi-cmps → neo-pipeline-cmps}/src/components/stageTimeChart__c/README.md +0 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 表格 simpleTable 专用:格式化、历史快照、趋势与单元格渲染
|
|
3
|
+
*/
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { Card, Popover } from 'antd';
|
|
6
|
+
import moment from 'moment';
|
|
7
|
+
|
|
8
|
+
import { toFiniteNumber } from './common';
|
|
9
|
+
|
|
10
|
+
/** 期间初商机快照(与当前行 id 关联) */
|
|
11
|
+
export interface HistoryOppSnap {
|
|
12
|
+
id: string;
|
|
13
|
+
closeDate: unknown;
|
|
14
|
+
opportunityName: string;
|
|
15
|
+
saleStageId: string;
|
|
16
|
+
money: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface FieldInfo {
|
|
20
|
+
name: string;
|
|
21
|
+
label: string;
|
|
22
|
+
apiKey: string;
|
|
23
|
+
type: string;
|
|
24
|
+
itemType: string;
|
|
25
|
+
checkitem: any[];
|
|
26
|
+
selectitem?: any[];
|
|
27
|
+
required: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseNumberForCompare(value: unknown): number | null {
|
|
31
|
+
if (value == null || value === '') return null;
|
|
32
|
+
const n = parseFloat(String(value).replace(/,/g, '').trim());
|
|
33
|
+
return Number.isFinite(n) ? n : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseCloseDateMs(value: unknown): number | null {
|
|
37
|
+
if (value == null || value === '') return null;
|
|
38
|
+
const m = moment(value);
|
|
39
|
+
return m.isValid() ? m.valueOf() : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type Trend = 'up' | 'down' | 'same';
|
|
43
|
+
|
|
44
|
+
function moneyTrend(
|
|
45
|
+
current: unknown,
|
|
46
|
+
history: HistoryOppSnap | null | undefined,
|
|
47
|
+
): Trend | null {
|
|
48
|
+
if (!history) return null;
|
|
49
|
+
const cur = parseNumberForCompare(current);
|
|
50
|
+
const prev = parseNumberForCompare(history.money);
|
|
51
|
+
if (cur == null || prev == null) return null;
|
|
52
|
+
if (cur > prev) return 'up';
|
|
53
|
+
if (cur < prev) return 'down';
|
|
54
|
+
return 'same';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function closeDateTrend(
|
|
58
|
+
current: unknown,
|
|
59
|
+
history: HistoryOppSnap | null | undefined,
|
|
60
|
+
): Trend | null {
|
|
61
|
+
if (!history) return null;
|
|
62
|
+
const cur = parseCloseDateMs(current);
|
|
63
|
+
const prev = parseCloseDateMs(history.closeDate);
|
|
64
|
+
if (cur == null || prev == null) return null;
|
|
65
|
+
if (cur > prev) return 'up';
|
|
66
|
+
if (cur < prev) return 'down';
|
|
67
|
+
return 'same';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** 自定义 SQL 返回行:数组列顺序与 HISTORY_QUERY_FIELDS 一致;或对象字段 */
|
|
71
|
+
export function historyRowToSnap(row: unknown): HistoryOppSnap | null {
|
|
72
|
+
if (row == null) return null;
|
|
73
|
+
if (Array.isArray(row)) {
|
|
74
|
+
if (row.length < 5) return null;
|
|
75
|
+
return {
|
|
76
|
+
id: String(row[0] ?? '').trim(),
|
|
77
|
+
closeDate: row[1],
|
|
78
|
+
opportunityName: row[2] != null ? String(row[2]) : '',
|
|
79
|
+
saleStageId: row[3] != null ? String(row[3]) : '',
|
|
80
|
+
money: row[4],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (typeof row === 'object') {
|
|
84
|
+
const o = row as Record<string, unknown>;
|
|
85
|
+
const id = o.id ?? o.opportunity_1_id;
|
|
86
|
+
if (id == null || id === '') return null;
|
|
87
|
+
return {
|
|
88
|
+
id: String(id).trim(),
|
|
89
|
+
closeDate:
|
|
90
|
+
o.opportunity_1_closeDate ?? o.closeDate ?? o.opportunity_1_close_date,
|
|
91
|
+
opportunityName: String(
|
|
92
|
+
o.opportunity_1_opportunityName ?? o.opportunityName ?? '',
|
|
93
|
+
),
|
|
94
|
+
saleStageId: String(
|
|
95
|
+
o.opportunity_1_saleStageId ?? o.saleStageId ?? '',
|
|
96
|
+
),
|
|
97
|
+
money: o.opportunity_1_money ?? o.money,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function buildHistoryMap(rows: unknown[]): Map<string, HistoryOppSnap> {
|
|
104
|
+
const map = new Map<string, HistoryOppSnap>();
|
|
105
|
+
for (const raw of rows) {
|
|
106
|
+
const snap = historyRowToSnap(raw);
|
|
107
|
+
if (snap?.id) map.set(snap.id, snap);
|
|
108
|
+
}
|
|
109
|
+
return map;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** closeDate:YYYY-MM-DD */
|
|
113
|
+
export function formatCloseDate(value: unknown): string {
|
|
114
|
+
if (value == null || value === '') return '—';
|
|
115
|
+
const m = moment(value);
|
|
116
|
+
return m.isValid() ? m.format('YYYY-MM-DD') : String(value);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** recentActivityRecordTime:YYYY-MM-DD HH:mm:ss */
|
|
120
|
+
export function formatActivityDateTime(value: unknown): string {
|
|
121
|
+
if (value == null || value === '') return '—';
|
|
122
|
+
const m = moment(value);
|
|
123
|
+
return m.isValid() ? m.format('YYYY-MM-DD HH:mm:ss') : String(value);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** customItem246/247:变更记录时间(毫秒/秒时间戳或可被 moment 解析的值)→ YYYY-MM-DD */
|
|
127
|
+
export function formatChangeRecordedAt(value: unknown): string {
|
|
128
|
+
if (value == null || value === '') return '—';
|
|
129
|
+
const n = toFiniteNumber(value);
|
|
130
|
+
if (n != null) {
|
|
131
|
+
const ms = n < 1e12 ? n * 1000 : n;
|
|
132
|
+
const m = moment(ms);
|
|
133
|
+
if (m.isValid()) return m.format('YYYY-MM-DD');
|
|
134
|
+
}
|
|
135
|
+
const m = moment(value);
|
|
136
|
+
return m.isValid() ? m.format('YYYY-MM-DD') : String(value);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** 金额列:$ 前缀 + 千分位(不缩写) */
|
|
140
|
+
export function formatMoneyCell(value: unknown): string {
|
|
141
|
+
if (value == null || value === '') return '—';
|
|
142
|
+
const n = parseFloat(String(value).replace(/,/g, '').trim());
|
|
143
|
+
if (!Number.isFinite(n)) return String(value);
|
|
144
|
+
const part = Number.isInteger(n)
|
|
145
|
+
? n.toLocaleString('en-US')
|
|
146
|
+
: n.toLocaleString('en-US', {
|
|
147
|
+
minimumFractionDigits: 0,
|
|
148
|
+
maximumFractionDigits: 2,
|
|
149
|
+
});
|
|
150
|
+
return `$${part}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** oppHealthAssessmentLevel:数值 → 健康度标签(带颜色背景) */
|
|
154
|
+
export function renderOppHealthAssessmentLevel(value: unknown): React.ReactNode {
|
|
155
|
+
if (value == null || value === '') return '—';
|
|
156
|
+
const n = toFiniteNumber(value);
|
|
157
|
+
if (n === 5) {
|
|
158
|
+
return (
|
|
159
|
+
<span className="opp-health-tag opp-health-tag--green">Excellent</span>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
if (n === 6) {
|
|
163
|
+
return <span className="opp-health-tag opp-health-tag--yellow">Health</span>;
|
|
164
|
+
}
|
|
165
|
+
if (n === 7) {
|
|
166
|
+
return <span className="opp-health-tag opp-health-tag--red">Risk</span>;
|
|
167
|
+
}
|
|
168
|
+
if (n === 8) {
|
|
169
|
+
return <span className="opp-health-tag opp-health-tag--red">Problem</span>;
|
|
170
|
+
}
|
|
171
|
+
return '—';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 百分比列:末尾加 %;接口常见为 0–1 小数或 0–100 数值,均按可读方式展示
|
|
176
|
+
*/
|
|
177
|
+
export function formatPercentCell(value: unknown): string {
|
|
178
|
+
if (value == null || value === '') return '—';
|
|
179
|
+
const raw = String(value).replace(/,/g, '').trim();
|
|
180
|
+
const n = parseFloat(raw.replace(/%/g, ''));
|
|
181
|
+
if (!Number.isFinite(n)) return String(value);
|
|
182
|
+
let pct = n;
|
|
183
|
+
if (n > 0 && n <= 1) {
|
|
184
|
+
pct = n * 100;
|
|
185
|
+
}
|
|
186
|
+
const text = Number.isInteger(pct)
|
|
187
|
+
? String(pct)
|
|
188
|
+
: pct.toLocaleString('en-US', {
|
|
189
|
+
minimumFractionDigits: 0,
|
|
190
|
+
maximumFractionDigits: 2,
|
|
191
|
+
});
|
|
192
|
+
return `${text}%`;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function moneyChangeTooltipTitle(
|
|
196
|
+
record: any,
|
|
197
|
+
current: unknown,
|
|
198
|
+
): React.ReactNode {
|
|
199
|
+
const preset = record?.customItem246__c;
|
|
200
|
+
const h = record?.historyData as HistoryOppSnap | undefined;
|
|
201
|
+
if (!h) return null;
|
|
202
|
+
return (
|
|
203
|
+
<div className="simpleTable-change-tip">
|
|
204
|
+
<div className="simpleTable-change-tip__title">Amount</div>
|
|
205
|
+
<div className="simpleTable-change-tip__detail">
|
|
206
|
+
{formatMoneyCell(h.money)}
|
|
207
|
+
{' → '}
|
|
208
|
+
<span className="simpleTable-change-tip__to">{formatMoneyCell(current)}</span>
|
|
209
|
+
</div>
|
|
210
|
+
<div className="simpleTable-change-tip__meta">
|
|
211
|
+
Changed on {formatChangeRecordedAt(preset)}
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function closeDateChangeTooltipTitle(
|
|
218
|
+
record: any,
|
|
219
|
+
current: unknown,
|
|
220
|
+
): React.ReactNode {
|
|
221
|
+
const preset = record?.customItem247__c;
|
|
222
|
+
const h = record?.historyData as HistoryOppSnap | undefined;
|
|
223
|
+
if (!h) return null;
|
|
224
|
+
return (
|
|
225
|
+
<div className="simpleTable-change-tip">
|
|
226
|
+
<div className="simpleTable-change-tip__title">Close Date</div>
|
|
227
|
+
<div className="simpleTable-change-tip__detail">
|
|
228
|
+
{formatCloseDate(h.closeDate)}
|
|
229
|
+
{' → '}
|
|
230
|
+
<span className="simpleTable-change-tip__to">{formatCloseDate(current)}</span>
|
|
231
|
+
</div>
|
|
232
|
+
<div className="simpleTable-change-tip__meta">
|
|
233
|
+
Changed on {formatChangeRecordedAt(preset)}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function renderTrendCell(args: {
|
|
240
|
+
displayText: string;
|
|
241
|
+
trend: Trend | null;
|
|
242
|
+
tooltip: React.ReactNode;
|
|
243
|
+
}): React.ReactNode {
|
|
244
|
+
const { displayText, trend, tooltip } = args;
|
|
245
|
+
const showArrow = trend === 'up' || trend === 'down';
|
|
246
|
+
const arrow =
|
|
247
|
+
trend === 'up' ? (
|
|
248
|
+
<span className="simpleTable-trend simpleTable-trend--up">↑</span>
|
|
249
|
+
) : trend === 'down' ? (
|
|
250
|
+
<span className="simpleTable-trend simpleTable-trend--down">↓</span>
|
|
251
|
+
) : null;
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<span className="simpleTable-cell-trend-wrap">
|
|
255
|
+
<span className="simpleTable-cell-trend-wrap__text">{displayText}</span>
|
|
256
|
+
{showArrow && arrow ? (
|
|
257
|
+
<Popover
|
|
258
|
+
content={tooltip}
|
|
259
|
+
trigger="hover"
|
|
260
|
+
placement="top"
|
|
261
|
+
overlayClassName="simpleTable-change-popover"
|
|
262
|
+
mouseEnterDelay={0.08}
|
|
263
|
+
>
|
|
264
|
+
<span className="simpleTable-cell-trend-wrap__arrow">{arrow}</span>
|
|
265
|
+
</Popover>
|
|
266
|
+
) : null}
|
|
267
|
+
</span>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** demo.html winRateTooltip 风格 */
|
|
272
|
+
export function renderWinRateHoverCard(record: any): React.ReactNode {
|
|
273
|
+
const baseline = record?.customItem244__c;
|
|
274
|
+
const positives = record?.customItem241__c;
|
|
275
|
+
const negatives = record?.customItem242__c;
|
|
276
|
+
const winRate = record?.customItem239__c;
|
|
277
|
+
|
|
278
|
+
const fmtLine = (v: unknown) =>
|
|
279
|
+
v == null || v === '' ? '—' : String(v);
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<Card
|
|
283
|
+
size="small"
|
|
284
|
+
bordered={false}
|
|
285
|
+
className="simpleTable-winrate-card"
|
|
286
|
+
>
|
|
287
|
+
<div className="simpleTable-winrate-card__baseline">
|
|
288
|
+
Baseline Probability{' '}
|
|
289
|
+
<span className="simpleTable-winrate-card__baseline-val">
|
|
290
|
+
{formatPercentCell(baseline)}
|
|
291
|
+
</span>
|
|
292
|
+
</div>
|
|
293
|
+
<hr className="simpleTable-winrate-card__hr" />
|
|
294
|
+
<div className="simpleTable-winrate-card__section-title">Positive Factors</div>
|
|
295
|
+
<div className="simpleTable-winrate-card__positives">
|
|
296
|
+
{fmtLine(positives)}
|
|
297
|
+
</div>
|
|
298
|
+
<hr className="simpleTable-winrate-card__hr" />
|
|
299
|
+
<div className="simpleTable-winrate-card__section-title">Negative Factors</div>
|
|
300
|
+
<div className="simpleTable-winrate-card__negatives">
|
|
301
|
+
{fmtLine(negatives)}
|
|
302
|
+
</div>
|
|
303
|
+
<hr className="simpleTable-winrate-card__hr" />
|
|
304
|
+
<div className="simpleTable-winrate-card__footer">
|
|
305
|
+
<span>AI Win Rate</span>
|
|
306
|
+
<span className="simpleTable-winrate-card__winrate-val">
|
|
307
|
+
{formatPercentCell(winRate)}
|
|
308
|
+
</span>
|
|
309
|
+
</div>
|
|
310
|
+
</Card>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** 汇总金额展示(与 formatMoneyCell 数字格式一致) */
|
|
315
|
+
export function formatMoneySummaryAmount(n: number): string {
|
|
316
|
+
const part = Number.isInteger(n)
|
|
317
|
+
? n.toLocaleString('en-US')
|
|
318
|
+
: n.toLocaleString('en-US', {
|
|
319
|
+
minimumFractionDigits: 0,
|
|
320
|
+
maximumFractionDigits: 2,
|
|
321
|
+
});
|
|
322
|
+
return `$${part}`;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** 按字段 apiKey 汇总行金额(解析规则与金额列一致) */
|
|
326
|
+
export function sumMoneyRows(rows: any[], moneyFieldKey: string): number {
|
|
327
|
+
let total = 0;
|
|
328
|
+
for (const row of rows) {
|
|
329
|
+
const v = row?.[moneyFieldKey];
|
|
330
|
+
if (v == null || v === '') continue;
|
|
331
|
+
const n = parseFloat(String(v).replace(/,/g, '').trim());
|
|
332
|
+
if (Number.isFinite(n)) total += n;
|
|
333
|
+
}
|
|
334
|
+
return total;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function isMoneyField(field: FieldInfo): boolean {
|
|
338
|
+
const k = field.apiKey?.toLowerCase?.() ?? '';
|
|
339
|
+
if (k === 'money' || k === 'amount') return true;
|
|
340
|
+
if (field.type === 'currency' || field.itemType === 'currency') return true;
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export { moneyTrend, closeDateTrend };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 阶段切换 stageSwitch 专用(商机行字段解析)
|
|
3
|
+
*/
|
|
4
|
+
import { parseNumberOrZero } from './common';
|
|
5
|
+
|
|
6
|
+
export function rowOpportunityStage(row: Record<string, unknown>): string {
|
|
7
|
+
return String(row.customItem248__c ?? '');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function rowMoney(row: Record<string, unknown>): number {
|
|
11
|
+
if (row.money !== undefined && row.money !== null && row.money !== '') {
|
|
12
|
+
return parseNumberOrZero(row.money);
|
|
13
|
+
}
|
|
14
|
+
return parseNumberOrZero(row.amount);
|
|
15
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 阶段停留时间 stageTimeChart 专用
|
|
3
|
+
*/
|
|
4
|
+
import { extractStageKeyFromStageName } from './common';
|
|
5
|
+
|
|
6
|
+
const COLOR_GREEN = '#22c55e';
|
|
7
|
+
const COLOR_YELLOW = '#eab308';
|
|
8
|
+
const COLOR_RED = '#ef4444';
|
|
9
|
+
const COLOR_NEUTRAL = '#94a3b8';
|
|
10
|
+
|
|
11
|
+
export interface StageTimeRow {
|
|
12
|
+
stageName: string;
|
|
13
|
+
actualTime: number;
|
|
14
|
+
actualTimeDisplay: string;
|
|
15
|
+
targetPercent: number;
|
|
16
|
+
limitPercent: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface StageTimeItem {
|
|
20
|
+
stageName: string;
|
|
21
|
+
actualTime: string;
|
|
22
|
+
actualPercent: number;
|
|
23
|
+
actualColor: string;
|
|
24
|
+
barActualWidthPct: number;
|
|
25
|
+
barTargetLeftPct: number;
|
|
26
|
+
barLimitLeftPct: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 将「16天11小时」「8天6时」等文案转为天数(浮点),如 16 + 11/24
|
|
31
|
+
*/
|
|
32
|
+
export function parseDurationToDays(text: unknown): number {
|
|
33
|
+
const s = String(text ?? '').trim();
|
|
34
|
+
if (!s) return 0;
|
|
35
|
+
let days = 0;
|
|
36
|
+
let hours = 0;
|
|
37
|
+
const dayMatch = s.match(/(\d+(?:\.\d+)?)\s*(天|Day)/);
|
|
38
|
+
if (dayMatch) days = parseFloat(dayMatch[1]) || 0;
|
|
39
|
+
const hourMatch = s.match(/(\d+(?:\.\d+)?)\s*(?:小时|时|Hour)/);
|
|
40
|
+
if (hourMatch) hours = parseFloat(hourMatch[1]) || 0;
|
|
41
|
+
return days + hours / 24;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** 终局阶段不参与「阶段停留时间」展示 */
|
|
45
|
+
export function isClosedOutcomeStage(stageName: string): boolean {
|
|
46
|
+
const key = extractStageKeyFromStageName(stageName).toLowerCase();
|
|
47
|
+
return key === 'closed won' || key === 'closed lost';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 配色:实际天数与 customItem3(目标)/customItem4(上限)比较 */
|
|
51
|
+
export function resolveActualColor(
|
|
52
|
+
actualDays: number,
|
|
53
|
+
target: number,
|
|
54
|
+
limit: number,
|
|
55
|
+
): string {
|
|
56
|
+
if (actualDays <= target) return COLOR_GREEN;
|
|
57
|
+
if (actualDays > limit) return COLOR_RED;
|
|
58
|
+
return COLOR_YELLOW;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** 将解析后的行转为图表条数据(配色与条形比例) */
|
|
62
|
+
export function buildStageTimeItems(rows: StageTimeRow[]): StageTimeItem[] {
|
|
63
|
+
return rows.map((row) => {
|
|
64
|
+
const actualPercent = row.actualTime;
|
|
65
|
+
const hasTh =
|
|
66
|
+
Number.isFinite(row.targetPercent) && Number.isFinite(row.limitPercent);
|
|
67
|
+
let targetNum = row.targetPercent;
|
|
68
|
+
let limitNum = row.limitPercent;
|
|
69
|
+
let actualColor: string;
|
|
70
|
+
const scaleMax = 60;
|
|
71
|
+
|
|
72
|
+
if (hasTh) {
|
|
73
|
+
actualColor = resolveActualColor(actualPercent, targetNum, limitNum);
|
|
74
|
+
} else {
|
|
75
|
+
targetNum = 0;
|
|
76
|
+
limitNum = 0;
|
|
77
|
+
actualColor = COLOR_NEUTRAL;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
stageName: row.stageName,
|
|
82
|
+
actualTime: row.actualTimeDisplay,
|
|
83
|
+
actualPercent,
|
|
84
|
+
actualColor,
|
|
85
|
+
barActualWidthPct: Math.min(100, (actualPercent / scaleMax) * 100),
|
|
86
|
+
barTargetLeftPct: hasTh ? (targetNum / scaleMax) * 100 : 0,
|
|
87
|
+
barLimitLeftPct: hasTh ? (limitNum / scaleMax) * 100 : 0,
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数值指标 targetNumber:样式辅助
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** margin/padding 字符串:无单位时补上 quantity,已有 px/rem等则原样返回 */
|
|
6
|
+
export function formatCssSpacing(value: string, unit: string): string {
|
|
7
|
+
if (!value || value === '0') return '0';
|
|
8
|
+
if (/\d+(px|rem|em|%)$/.test(value.trim())) {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
return `${value}${unit}`;
|
|
12
|
+
}
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"axios": "^1.7.0",
|
|
48
48
|
"antd": "^4.9.4",
|
|
49
49
|
"lodash": "^4.17.23",
|
|
50
|
-
"neo-open-api": "^1.2.
|
|
50
|
+
"neo-open-api": "^1.2.7"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@commitlint/cli": "^18.0.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@types/react": "^16.9.11",
|
|
56
56
|
"@types/react-dom": "^16.9.15",
|
|
57
57
|
"@types/axios": "^0.14.0",
|
|
58
|
-
"neo-cmp-cli": "^1.13.
|
|
58
|
+
"neo-cmp-cli": "^1.13.17",
|
|
59
59
|
"husky": "^4.2.5",
|
|
60
60
|
"lint-staged": "^10.2.9",
|
|
61
61
|
"prettier": "^2.0.5"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"axios": "^1.7.0",
|
|
40
40
|
"antd-mobile": "^2.3.3",
|
|
41
41
|
"lodash": "^4.17.23",
|
|
42
|
-
"neo-open-api": "^1.2.
|
|
42
|
+
"neo-open-api": "^1.2.7",
|
|
43
43
|
"tslib": "2.3.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@types/react": "^16.9.11",
|
|
50
50
|
"@types/react-dom": "^16.9.15",
|
|
51
51
|
"@types/axios": "^0.14.0",
|
|
52
|
-
"neo-cmp-cli": "^1.13.
|
|
52
|
+
"neo-cmp-cli": "^1.13.17",
|
|
53
53
|
"husky": "^4.2.5",
|
|
54
54
|
"lint-staged": "^10.2.9",
|
|
55
55
|
"prettier": "^2.0.5"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"axios": "^1.7.0",
|
|
40
40
|
"antd": "^4.9.4",
|
|
41
41
|
"lodash": "^4.17.23",
|
|
42
|
-
"neo-open-api": "^1.2.
|
|
42
|
+
"neo-open-api": "^1.2.7"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@commitlint/cli": "^18.0.0",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@types/react": "^16.9.11",
|
|
48
48
|
"@types/react-dom": "^16.9.15",
|
|
49
49
|
"@types/axios": "^0.14.0",
|
|
50
|
-
"neo-cmp-cli": "^1.13.
|
|
50
|
+
"neo-cmp-cli": "^1.13.17",
|
|
51
51
|
"husky": "^4.2.5",
|
|
52
52
|
"lint-staged": "^10.2.9",
|
|
53
53
|
"prettier": "^2.0.5"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
prettier 配置文件
|
|
3
|
+
更多配置信息:https://prettier.io/docs/en/options.html
|
|
4
|
+
*/
|
|
5
|
+
module.exports = {
|
|
6
|
+
semi: true, // Semicolons 分号,默认需要分号
|
|
7
|
+
tabWidth: 2, // 空格,默认 2,
|
|
8
|
+
useTabs: false,
|
|
9
|
+
singleQuote: true, // 单引号还是双引号,默认为false 双引号
|
|
10
|
+
trailingComma: 'all', // 逗号
|
|
11
|
+
jsxBracketSameLine: false, // 默认为false,Put the > of a multi-line JSX element at the end of the last line instead of being alone on the next line (does not apply to self closing elements).
|
|
12
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* BaseCmpProps 基础组件属性接口
|
|
5
|
+
*/
|
|
6
|
+
export interface BaseCmpProps {
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ScopedComponentType 作用域组件类型接口
|
|
12
|
+
*/
|
|
13
|
+
export interface ScopedComponentType {
|
|
14
|
+
[key: string]: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* BaseCmp 基础组件类
|
|
19
|
+
* 继承自 React.PureComponent,提供基础组件功能
|
|
20
|
+
*/
|
|
21
|
+
export declare class BaseCmp<
|
|
22
|
+
T extends BaseCmpProps = BaseCmpProps,
|
|
23
|
+
S = any
|
|
24
|
+
> extends React.PureComponent<T, S> implements ScopedComponentType {
|
|
25
|
+
props: Readonly<T> & Readonly<{ children?: React.ReactNode }>;
|
|
26
|
+
state: Readonly<S>;
|
|
27
|
+
setState<K extends keyof S>(
|
|
28
|
+
state:
|
|
29
|
+
| ((prevState: Readonly<S>, props: Readonly<T>) => Pick<S, K> | S | null)
|
|
30
|
+
| (Pick<S, K> | S | null),
|
|
31
|
+
callback?: () => void
|
|
32
|
+
): void;
|
|
33
|
+
forceUpdate(callback?: () => void): void;
|
|
34
|
+
render(): React.ReactNode;
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
### 目录说明
|
|
2
|
+
- src: 自定义组件源码;
|
|
3
|
+
- src/assets: 存放组件静态资源,比如 css、img等;
|
|
4
|
+
- src/components: 存放自定义组件代码,每个自定义组件以自身名称(cmpType 数值)作为目录进行存放;
|
|
5
|
+
- src/components/xxCmp/index: 自定义组件的内容文件;
|
|
6
|
+
- src/components/xxCmp/model: 自定义组件的模型文件,用于对接页面设计器;
|
|
7
|
+
- neo.config.js: neo-cmp-cli 配置文件。
|
|
8
|
+
|
|
9
|
+
### 组件开发规范
|
|
10
|
+
- 存放在 src/components 目录下的自定义组件,默认 index 为自定义组件源码入口文件,model.[tj]s 为自定义组件的模型文件(对接页面设计器需要);
|
|
11
|
+
- 当 neo.config.js 中的 entry 为空或者不存在时,cli 将根据 src/components 目录下的自定义组件结构生成对应的 entry 配置(可在命令控制台查看生成的 entry 配置);
|
|
12
|
+
- 自定义组件中可用的配置项类型 请见 [当前可用表单项](https://github.com/wibetter/neo-register/blob/master/docs/FormItemType.md);
|
|
13
|
+
- 自定义组件最外层请设置一个唯一的 ClassName(比如 xx-cmpType-container),所有内容样式请放在该 ClassName 中,避免自定义组件样式相互干扰;
|
|
14
|
+
- 默认开启代码规范检测(含样式内容),如需关闭,请调整 neo.config.js 相关配置;
|
|
15
|
+
- 请使用 react 16 版本;
|
|
16
|
+
- 支持在自定义组件中使用 Open API,详细见[使用说明](https://www.npmjs.com/package/neo-open-api)。
|
|
17
|
+
|
|
18
|
+
### 自定义组件注册器使用说明
|
|
19
|
+
- [neo-register 使用说明](https://www.npmjs.com/package/neo-register?activeTab=readme)
|
|
20
|
+
备注:预览、调试(linkDebug)和构建发布时 cli 会自动创建对应的注册文件(含 neo-register 的使用),用户无需关注 neo-register。
|
|
21
|
+
|
|
22
|
+
### 开发说明
|
|
23
|
+
|
|
24
|
+
1. **安装依赖**
|
|
25
|
+
```bash
|
|
26
|
+
$ npm i 或者 yarn
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
2. **linkDebug: 外链调试(在线上页面设计器端预览自定义组件)**
|
|
30
|
+
> linkDebug模式:用于在线上页面设计器中预览和调试自定义组件。
|
|
31
|
+
```bash
|
|
32
|
+
$ npm run linkDebug
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. **发布到 NeoCRM 平台**
|
|
36
|
+
> 需要确保 package.json 中的 name 值唯一,version 值不重复。
|
|
37
|
+
```bash
|
|
38
|
+
$ npm run pushCmp
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### OAuth2 登录授权
|
|
42
|
+
使用 `neo push cmp`、`neo pull cmp`、`neo delete cmp` 等命令与 NeoCRM 平台交互时,需要进行授权登录。
|
|
43
|
+
|
|
44
|
+
#### 使用步骤
|
|
45
|
+
|
|
46
|
+
1. **登录 NeoCRM 平台**
|
|
47
|
+
```bash
|
|
48
|
+
neo login
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
执行流程:
|
|
52
|
+
- 自动打开浏览器访问授权页面
|
|
53
|
+
- 在浏览器中输入 NeoCRM 账号密码进行登录(需选择对应的租户)
|
|
54
|
+
- 授权成功后自动跳转回本地(附带 code)
|
|
55
|
+
- cli 端 通过 code 获取 Token,并自动保存到项目的 `.neo-cli/token.json` 文件中
|
|
56
|
+
|
|
57
|
+
2. **登出 NeoCRM 平台**
|
|
58
|
+
```bash
|
|
59
|
+
neo logout
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
功能:清除本地保存的 token 文件,下次使用需要重新登录。
|
|
63
|
+
|
|
64
|
+
#### neo login 选择「自定义环境」时的授权配置示例
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
// neo.config.js
|
|
68
|
+
module.exports = {
|
|
69
|
+
neoConfig: {
|
|
70
|
+
neoBaseURL: 'https://crm-cd.xiaoshouyi.com', // 平台根地址(默认:https://crm.xiaoshouyi.com)
|
|
71
|
+
// 登录授权 URL(用于获取 code)
|
|
72
|
+
loginURL: 'https://login-cd.xiaoshouyi.com/auc/oauth2/auth',
|
|
73
|
+
tokenAPI: 'https://login-cd.xiaoshouyi.com/auc/oauth2/token', // Token 获取接口地址
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Token 有效期
|
|
79
|
+
|
|
80
|
+
- **access_token**:默认有效期 2 小时
|
|
81
|
+
- **refresh_token**:默认有效期 30 天
|
|
82
|
+
- 系统会在 access_token 过期前 5 分钟自动刷新
|
|
83
|
+
- 如果 refresh_token 也过期,需要重新执行 `neo login`
|
|
84
|
+
|
|
85
|
+
#### 常见问题
|
|
86
|
+
|
|
87
|
+
**Q1: 浏览器无法自动打开怎么办?**
|
|
88
|
+
A: 命令行会输出授权 URL,手动复制到浏览器中打开即可。
|
|
89
|
+
|
|
90
|
+
**Q2: Token 刷新失败怎么办?**
|
|
91
|
+
A: 如果 refresh_token 也过期(默认 30 天),需要重新执行 `neo login`。同时检查网络连接是否正常。
|
|
92
|
+
|
|
93
|
+
**Q3: 授权登录后没有正常跳回 redirect_uri**
|
|
94
|
+
A: 可能被浏览器安装的插件影响,目前已知会影响授权登录的浏览器插件有:Neo UI Extension,请关闭插件后重试。
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### 配置项说明(neo-cmp-cli)
|
|
99
|
+
[请查看neo-cmp-cli](https://www.npmjs.com/package/neo-cmp-cli)
|