neo-cmp-cli 1.13.16 → 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/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/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/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/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 → 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,665 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file XObject 数据表格组件(简化版)
|
|
3
|
+
* @description 基于 Neo 平台的 XObject 实体对象数据表格组件,仅支持数据展示
|
|
4
|
+
*/
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { Table, Spin, Empty, Popover } from 'antd';
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
import { xObject } from 'neo-open-api'; // Neo Open API
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
import isEqual from 'lodash/isEqual';
|
|
11
|
+
|
|
12
|
+
// Neo Open API// 引入 neo-ui-common / BaseCmp
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
import { BaseCmp } from 'neo-ui-common';
|
|
15
|
+
|
|
16
|
+
// 引入 neo-ui-common / NeoEvent
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
import { NeoEvent } from 'neo-ui-common';
|
|
19
|
+
|
|
20
|
+
import queryByCustomSQL from '../../utils/queryByCustomSQL';
|
|
21
|
+
import {
|
|
22
|
+
closeRangeFromFilter,
|
|
23
|
+
entityTypeIdForWhere,
|
|
24
|
+
normalizeOwnerIdsForWhere,
|
|
25
|
+
getDefaultFilterByProps,
|
|
26
|
+
} from '../../utils/common';
|
|
27
|
+
import {
|
|
28
|
+
buildHistoryMap,
|
|
29
|
+
closeDateChangeTooltipTitle,
|
|
30
|
+
closeDateTrend,
|
|
31
|
+
formatActivityDateTime,
|
|
32
|
+
formatCloseDate,
|
|
33
|
+
formatMoneyCell,
|
|
34
|
+
formatMoneySummaryAmount,
|
|
35
|
+
formatPercentCell,
|
|
36
|
+
isMoneyField,
|
|
37
|
+
moneyChangeTooltipTitle,
|
|
38
|
+
moneyTrend,
|
|
39
|
+
renderOppHealthAssessmentLevel,
|
|
40
|
+
renderTrendCell,
|
|
41
|
+
renderWinRateHoverCard,
|
|
42
|
+
sumMoneyRows,
|
|
43
|
+
type FieldInfo,
|
|
44
|
+
type HistoryOppSnap,
|
|
45
|
+
} from '../../utils/simpleTable';
|
|
46
|
+
|
|
47
|
+
import ShowHealthResult from '../showHealthResult__c';
|
|
48
|
+
|
|
49
|
+
import './common.scss';
|
|
50
|
+
import './style.scss';
|
|
51
|
+
|
|
52
|
+
export type { HistoryOppSnap } from '../../utils/simpleTable';
|
|
53
|
+
|
|
54
|
+
/** 商机详情页 hash 路由(与平台 neoweb 一致) */
|
|
55
|
+
function buildOpportunityDetailUrl(id: string, objectApiKey: string): string {
|
|
56
|
+
const q = new URLSearchParams({
|
|
57
|
+
objectApiKey,
|
|
58
|
+
recordId: id,
|
|
59
|
+
});
|
|
60
|
+
return `/bff/neoweb#/entityDetail/opportunity/${id}?${q.toString()}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 不在表格中展示的字段(仍可通过 xObjectDataApi.fields 拉取,用于 hover 卡片 / tooltip) */
|
|
64
|
+
const HIDDEN_TABLE_COLUMN_KEYS = new Set([
|
|
65
|
+
'customItem244__c',
|
|
66
|
+
'customItem241__c',
|
|
67
|
+
'customItem242__c',
|
|
68
|
+
'customItem246__c',
|
|
69
|
+
'customItem247__c',
|
|
70
|
+
'customItem245__c',
|
|
71
|
+
// 'objectId', // 跳转到详情页需要
|
|
72
|
+
// 'businessTypeId' // 跳转到详情页需要
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
/** 历史商机 SQL 与 stageSwitch 一致;字段顺序需与 query fields 一致(数组行按列下标解析) */
|
|
76
|
+
const HISTORY_OPPORTUNITY_BI_KEY = 'biCustomModel_397169_20260401104916618';
|
|
77
|
+
const HISTORY_QUERY_FIELDS = [
|
|
78
|
+
// 'id', // 历史版本ID
|
|
79
|
+
'opportunity_1__id', // 商机ID
|
|
80
|
+
'opportunity_1_closeDate',
|
|
81
|
+
'opportunity_1_opportunityName',
|
|
82
|
+
'opportunity_1_saleStageId',
|
|
83
|
+
'opportunity_1_money',
|
|
84
|
+
] as const;
|
|
85
|
+
|
|
86
|
+
const defaultXObjectDataApi = {
|
|
87
|
+
xObjectApiKey: 'opportunity',
|
|
88
|
+
fields: [
|
|
89
|
+
'opportunityName', // OpportunityStageName 商机名称
|
|
90
|
+
'money', // Amount
|
|
91
|
+
'closeDate', // Close Date
|
|
92
|
+
'customItem248__c', // Stage
|
|
93
|
+
// 'oppHealthAssessmentScore',
|
|
94
|
+
'recentActivityRecordTime',
|
|
95
|
+
'oppHealthAssessmentLevel', // 1.AI Score; hover卡片:调用商机健康度接口
|
|
96
|
+
'customItem239__c', // 2.AI Win Rate: hover卡片展示的3个信息,对应商机上的3个字段(baseline、positiveFactors、negativeFactors)
|
|
97
|
+
'customItem244__c', // baseline
|
|
98
|
+
'customItem241__c', // positiveFactors
|
|
99
|
+
'customItem242__c', // negativeFactors
|
|
100
|
+
'customItem246__c', // AmoutChanged
|
|
101
|
+
'customItem247__c', // ClosedDateChanged
|
|
102
|
+
'customItem245__c', // StageChanged
|
|
103
|
+
// 'objectId', // 跳转到详情页需要
|
|
104
|
+
// 'businessTypeId' // 跳转到详情页需要
|
|
105
|
+
],
|
|
106
|
+
page: 1,
|
|
107
|
+
pageSize: 50,
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* 组件属性接口
|
|
111
|
+
*/
|
|
112
|
+
interface SimpleTableProps {
|
|
113
|
+
/** XObject 实体对象的 API Key,用于标识要操作的数据对象 */
|
|
114
|
+
xObjectDataApi: {
|
|
115
|
+
xObjectApiKey: string;
|
|
116
|
+
fields: string[];
|
|
117
|
+
fieldDescList?: any[];
|
|
118
|
+
autoFetchData?: boolean;
|
|
119
|
+
};
|
|
120
|
+
/** Neo 平台传递的数据,包含系统信息等 */
|
|
121
|
+
data?: any;
|
|
122
|
+
entityData?: any; // Neo 平台自动获取的实体数据
|
|
123
|
+
/** 是否为自定义实体对象 */
|
|
124
|
+
custom?: boolean;
|
|
125
|
+
/** 组件类名 */
|
|
126
|
+
className?: string;
|
|
127
|
+
/** 表格标题,优先使用此配置 */
|
|
128
|
+
title?: string;
|
|
129
|
+
/** 标题下方说明文案 */
|
|
130
|
+
description?: string;
|
|
131
|
+
/** 当前激活的销售阶段 */
|
|
132
|
+
activeStage?: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 组件状态接口
|
|
137
|
+
*/
|
|
138
|
+
interface SimpleTableState {
|
|
139
|
+
/** 表格标题 */
|
|
140
|
+
title?: string;
|
|
141
|
+
fieldList: FieldInfo[];
|
|
142
|
+
/** 表格数据源 */
|
|
143
|
+
dataSource: any[];
|
|
144
|
+
/** 加载状态 */
|
|
145
|
+
loading: boolean;
|
|
146
|
+
/** 错误信息 */
|
|
147
|
+
error: string | null;
|
|
148
|
+
activeStage: string; // 当前激活的销售阶段
|
|
149
|
+
/**
|
|
150
|
+
* 与 FilterBar 事件 payload 一致(closeDateCustomRange / opportunityOwner / businessType 等);
|
|
151
|
+
* 默认值为 defaultFilter,getFilterWhere 仅从中取值
|
|
152
|
+
*/
|
|
153
|
+
filter: any;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* XObject 数据表格组件(简化版)
|
|
158
|
+
* 仅支持数据展示,不支持增删改查操作
|
|
159
|
+
*/
|
|
160
|
+
export default class SimpleTable extends BaseCmp<
|
|
161
|
+
SimpleTableProps,
|
|
162
|
+
SimpleTableState
|
|
163
|
+
> {
|
|
164
|
+
constructor(props: SimpleTableProps) {
|
|
165
|
+
super(props);
|
|
166
|
+
|
|
167
|
+
// 初始化组件状态
|
|
168
|
+
this.state = {
|
|
169
|
+
fieldList: [],
|
|
170
|
+
dataSource: [],
|
|
171
|
+
loading: false,
|
|
172
|
+
error: null,
|
|
173
|
+
activeStage: props.activeStage || 'Prospecting',
|
|
174
|
+
filter: getDefaultFilterByProps(props),
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
this.loadData = this.loadData.bind(this);
|
|
178
|
+
this.loadFieldList = this.loadFieldList.bind(this);
|
|
179
|
+
this.getCurFieldList = this.getCurFieldList.bind(this);
|
|
180
|
+
this.getMoneyFieldApiKey = this.getMoneyFieldApiKey.bind(this);
|
|
181
|
+
this.generateColumns = this.generateColumns.bind(this);
|
|
182
|
+
this.getFilterWhere = this.getFilterWhere.bind(this);
|
|
183
|
+
this.setFilter = this.setFilter.bind(this);
|
|
184
|
+
this.updateActiveStage = this.updateActiveStage.bind(this);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
componentDidMount() {
|
|
188
|
+
// const { xObjectDataApi } = this.props;
|
|
189
|
+
// const { xObjectApiKey } = xObjectDataApi || {};
|
|
190
|
+
const { xObjectApiKey } = defaultXObjectDataApi;
|
|
191
|
+
|
|
192
|
+
if (xObjectApiKey) {
|
|
193
|
+
// 初始化字段列表、加载数据
|
|
194
|
+
this.loadFieldList();
|
|
195
|
+
this.loadData();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/*
|
|
199
|
+
// 广播事件:更新 FilterBar filter
|
|
200
|
+
NeoEvent.listen('updateFilterData', (filterData: any) => {
|
|
201
|
+
console.log('SimpleTable 监听到了一个广播事件 updateFilterData: ', filterData);
|
|
202
|
+
this.setFilter(filterData);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// 广播事件:更新当前激活的销售阶段
|
|
206
|
+
NeoEvent.listen('updateActiveStage', (activeStage: string) => {
|
|
207
|
+
console.log('SimpleTable 监听到了一个广播事件 updateActiveStage: ', activeStage);
|
|
208
|
+
this.updateActiveStage(activeStage);
|
|
209
|
+
});
|
|
210
|
+
*/
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 加载字段列表
|
|
215
|
+
* 从 Neo 平台获取 XObject 的字段描述信息
|
|
216
|
+
*/
|
|
217
|
+
async loadFieldList() {
|
|
218
|
+
const xObjectDataApi: any = defaultXObjectDataApi;
|
|
219
|
+
|
|
220
|
+
// 方式一:直接从 props.xObjectDetailApi 中获取字段描述
|
|
221
|
+
if (xObjectDataApi && xObjectDataApi.fieldDescList) {
|
|
222
|
+
this.setState({ fieldList: xObjectDataApi.fieldDescList });
|
|
223
|
+
} else {
|
|
224
|
+
// 方式二:自行通过 OpenAPI SDK 获取字段描述
|
|
225
|
+
if (!xObjectDataApi.xObjectApiKey) return;
|
|
226
|
+
try {
|
|
227
|
+
const resultData = await xObject.getDesc(xObjectDataApi.xObjectApiKey);
|
|
228
|
+
if (resultData && resultData.status) {
|
|
229
|
+
const result = resultData.data || {};
|
|
230
|
+
const fieldList = result.fields || [];
|
|
231
|
+
this.setState({ fieldList, title: result.label });
|
|
232
|
+
}
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error('获取字段列表失败:', error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 当前表格可见字段列表(与列配置一致)
|
|
241
|
+
*/
|
|
242
|
+
getCurFieldList(): FieldInfo[] {
|
|
243
|
+
// const { xObjectDataApi } = this.props;
|
|
244
|
+
const xObjectDataApi: any = defaultXObjectDataApi;
|
|
245
|
+
const { fieldList } = this.state;
|
|
246
|
+
const { fields, fieldDescList } = xObjectDataApi || {};
|
|
247
|
+
let curFieldList =
|
|
248
|
+
fieldDescList && fieldDescList.length > 0 ? fieldDescList : fieldList;
|
|
249
|
+
|
|
250
|
+
if (fields && fields.length > 0) {
|
|
251
|
+
curFieldList = curFieldList.filter((field: FieldInfo) =>
|
|
252
|
+
fields.includes(field.apiKey),
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return curFieldList;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 汇总用金额字段 apiKey(与金额列一致,取第一个货币字段)
|
|
260
|
+
*/
|
|
261
|
+
getMoneyFieldApiKey(): string | null {
|
|
262
|
+
const moneyField = this.getCurFieldList().find((f) => isMoneyField(f));
|
|
263
|
+
return moneyField?.apiKey ?? null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 生成表格列配置
|
|
268
|
+
* @returns 表格列配置数组
|
|
269
|
+
*/
|
|
270
|
+
generateColumns() {
|
|
271
|
+
const xObjectDataApi: any = defaultXObjectDataApi;
|
|
272
|
+
|
|
273
|
+
const curFieldList = this.getCurFieldList().filter(
|
|
274
|
+
(field) => !HIDDEN_TABLE_COLUMN_KEYS.has(field.apiKey),
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// 根据字段列表生成基础列配置
|
|
278
|
+
const columns: any[] = curFieldList.map((field) => {
|
|
279
|
+
const col: any = {
|
|
280
|
+
title: field.label,
|
|
281
|
+
dataIndex: field.apiKey,
|
|
282
|
+
key: field.apiKey,
|
|
283
|
+
ellipsis: true,
|
|
284
|
+
};
|
|
285
|
+
if (
|
|
286
|
+
field.apiKey === 'oppHealthAssessmentLevel' ||
|
|
287
|
+
field.apiKey === 'customItem239__c'
|
|
288
|
+
) {
|
|
289
|
+
col.title = (
|
|
290
|
+
<span className="simpleTable-th-title-with-ai-icon">
|
|
291
|
+
<span className="simpleTable-th-ai-icon" />
|
|
292
|
+
<span>{field.label}</span>
|
|
293
|
+
</span>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
if (field.apiKey === 'closeDate') {
|
|
297
|
+
col.render = (text: unknown, record: any) => {
|
|
298
|
+
const display = formatCloseDate(text);
|
|
299
|
+
const trend = closeDateTrend(text, record?.historyData);
|
|
300
|
+
if (trend !== 'up' && trend !== 'down') {
|
|
301
|
+
return display;
|
|
302
|
+
}
|
|
303
|
+
return renderTrendCell({
|
|
304
|
+
displayText: display,
|
|
305
|
+
trend,
|
|
306
|
+
tooltip: closeDateChangeTooltipTitle(record, text),
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
} else if (field.apiKey === 'opportunityName') {
|
|
310
|
+
col.render = (text: unknown, record: any) => {
|
|
311
|
+
const display = text == null || text === '' ? '—' : String(text);
|
|
312
|
+
const id =
|
|
313
|
+
record?.id != null && record.id !== '' ? String(record.id) : '';
|
|
314
|
+
const objectApiKey = xObjectDataApi?.xObjectApiKey;
|
|
315
|
+
if (!id || !objectApiKey) {
|
|
316
|
+
return display;
|
|
317
|
+
}
|
|
318
|
+
const url = buildOpportunityDetailUrl(id, objectApiKey);
|
|
319
|
+
return (
|
|
320
|
+
<a
|
|
321
|
+
className="simpleTable-opp-name-link"
|
|
322
|
+
href={url}
|
|
323
|
+
target="_blank"
|
|
324
|
+
/*
|
|
325
|
+
onClick={(e) => {
|
|
326
|
+
e.preventDefault();
|
|
327
|
+
window.location.assign(url);
|
|
328
|
+
}}
|
|
329
|
+
*/
|
|
330
|
+
>
|
|
331
|
+
{display}
|
|
332
|
+
</a>
|
|
333
|
+
);
|
|
334
|
+
};
|
|
335
|
+
} else if (field.apiKey === 'recentActivityRecordTime') {
|
|
336
|
+
col.render = (text: unknown) => formatActivityDateTime(text);
|
|
337
|
+
} else if (field.apiKey === 'oppHealthAssessmentLevel') {
|
|
338
|
+
col.render = (text: unknown, record: any) => {
|
|
339
|
+
const cell = renderOppHealthAssessmentLevel(text);
|
|
340
|
+
const id =
|
|
341
|
+
record?.id != null && record.id !== '' ? String(record.id) : '';
|
|
342
|
+
const xObjectApiKey = xObjectDataApi?.xObjectApiKey;
|
|
343
|
+
if (!id || !xObjectApiKey) {
|
|
344
|
+
return cell;
|
|
345
|
+
}
|
|
346
|
+
return (
|
|
347
|
+
<Popover
|
|
348
|
+
content={
|
|
349
|
+
<div className="simpleTable-health-popover-body">
|
|
350
|
+
<ShowHealthResult id={id} xObjectApiKey={xObjectApiKey} />
|
|
351
|
+
</div>
|
|
352
|
+
}
|
|
353
|
+
trigger="hover"
|
|
354
|
+
placement="topLeft"
|
|
355
|
+
overlayClassName="simpleTable-health-popover-overlay"
|
|
356
|
+
mouseEnterDelay={0.08}
|
|
357
|
+
>
|
|
358
|
+
<span className="simpleTable-health-popover-trigger">{cell}</span>
|
|
359
|
+
</Popover>
|
|
360
|
+
);
|
|
361
|
+
};
|
|
362
|
+
} else if (field.apiKey === 'customItem239__c') {
|
|
363
|
+
col.align = 'center';
|
|
364
|
+
col.render = (_text: unknown, record: any) => (
|
|
365
|
+
<Popover
|
|
366
|
+
content={renderWinRateHoverCard(record)}
|
|
367
|
+
trigger="hover"
|
|
368
|
+
placement="topLeft"
|
|
369
|
+
overlayClassName="simpleTable-winrate-popover"
|
|
370
|
+
mouseEnterDelay={0.08}
|
|
371
|
+
>
|
|
372
|
+
<span className="simpleTable-winrate-trigger">
|
|
373
|
+
{formatPercentCell(record?.customItem239__c)}
|
|
374
|
+
</span>
|
|
375
|
+
</Popover>
|
|
376
|
+
);
|
|
377
|
+
} else if (isMoneyField(field)) {
|
|
378
|
+
// col.align = 'right';
|
|
379
|
+
col.render = (text: unknown, record: any) => {
|
|
380
|
+
const display = formatMoneyCell(text);
|
|
381
|
+
const trend = moneyTrend(text, record?.historyData);
|
|
382
|
+
if (trend !== 'up' && trend !== 'down') {
|
|
383
|
+
return display;
|
|
384
|
+
}
|
|
385
|
+
return renderTrendCell({
|
|
386
|
+
displayText: display,
|
|
387
|
+
trend,
|
|
388
|
+
tooltip: moneyChangeTooltipTitle(record, text),
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
return col;
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
return columns;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* 将 FilterBar 结构 filter 转为 xObject.√ 的 where 字符串数组(与 stageSwitch 一致,SDK 按顺序 and 拼接)
|
|
400
|
+
*/
|
|
401
|
+
getFilterWhere(): string[] {
|
|
402
|
+
const raw = this.state.filter;
|
|
403
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
404
|
+
return [];
|
|
405
|
+
}
|
|
406
|
+
const curFilter = raw as Record<string, unknown>;
|
|
407
|
+
const where: string[] = [];
|
|
408
|
+
|
|
409
|
+
const range = closeRangeFromFilter(curFilter.closeDateCustomRange);
|
|
410
|
+
if (range) {
|
|
411
|
+
where.push(`closeDate >= ${range.start} and closeDate <= ${range.end}`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const ownerIds = normalizeOwnerIdsForWhere(curFilter.opportunityOwner);
|
|
415
|
+
if (ownerIds.length === 1) {
|
|
416
|
+
where.push(`ownerId = ${ownerIds[0]}`);
|
|
417
|
+
} else if (ownerIds.length > 1) {
|
|
418
|
+
where.push(`ownerId in (${ownerIds.join(', ')})`);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const entityTypeId = entityTypeIdForWhere(curFilter.businessType);
|
|
422
|
+
if (entityTypeId != null) {
|
|
423
|
+
where.push(`entityType = ${entityTypeId}`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/*
|
|
427
|
+
// 如果 activeStage 存在,则添加 stage 过滤条件
|
|
428
|
+
if (this.state.activeStage) {
|
|
429
|
+
// customItem248__c 是销售阶段字段
|
|
430
|
+
where.push(`customItem248__c = '${this.state.activeStage}'`);
|
|
431
|
+
}
|
|
432
|
+
*/
|
|
433
|
+
|
|
434
|
+
return where;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** 与 stageSwitch一致:历史商机自定义 SQL 的 where(opportunity_1_* 字段) */
|
|
438
|
+
getHistoryOpportunityQueryFilter(): string[] {
|
|
439
|
+
const raw = this.state.filter;
|
|
440
|
+
const curFilter: Record<string, unknown> =
|
|
441
|
+
raw && typeof raw === 'object' && !Array.isArray(raw)
|
|
442
|
+
? Object.keys(raw as object).length > 0
|
|
443
|
+
? (raw as Record<string, unknown>)
|
|
444
|
+
: defaultFilter
|
|
445
|
+
: defaultFilter;
|
|
446
|
+
|
|
447
|
+
const where: string[] = [];
|
|
448
|
+
|
|
449
|
+
const range = closeRangeFromFilter(curFilter.closeDateCustomRange);
|
|
450
|
+
if (range) {
|
|
451
|
+
where.push(
|
|
452
|
+
`opportunity_1_closeDate >= ${range.start} and opportunity_1_closeDate <= ${range.end}`,
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const ownerIds = normalizeOwnerIdsForWhere(curFilter.opportunityOwner);
|
|
457
|
+
if (ownerIds.length === 1) {
|
|
458
|
+
where.push(`opportunity_1_ownerId = ${ownerIds[0]}`);
|
|
459
|
+
} else if (ownerIds.length > 1) {
|
|
460
|
+
where.push(`opportunity_1_ownerId in (${ownerIds.join(', ')})`);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const entityTypeId = entityTypeIdForWhere(curFilter.businessType);
|
|
464
|
+
if (entityTypeId != null) {
|
|
465
|
+
where.push(`opportunity_1_entityType = ${entityTypeId}`);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (curFilter.changesSinceCustomTime) {
|
|
469
|
+
where.push(`version = ${curFilter.changesSinceCustomTime}`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return where;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** 查询期间初历史商机列表(与 stageSwitch.fetchHistoryOpportunityList 同源,字段按需求扩展) */
|
|
476
|
+
async fetchHistoryOpportunityList(): Promise<unknown[]> {
|
|
477
|
+
const result = await queryByCustomSQL({
|
|
478
|
+
xObjectApiKey: HISTORY_OPPORTUNITY_BI_KEY,
|
|
479
|
+
fields: [...HISTORY_QUERY_FIELDS],
|
|
480
|
+
page: 1,
|
|
481
|
+
pageSize: 1000,
|
|
482
|
+
where: this.getHistoryOpportunityQueryFilter(),
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
return result?.status ? result.data ?? [] : [];
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* 加载表格数据
|
|
490
|
+
*/
|
|
491
|
+
@NeoEvent.function
|
|
492
|
+
async loadData() {
|
|
493
|
+
// const api = this.props.xObjectDataApi || {};
|
|
494
|
+
const api: any = defaultXObjectDataApi;
|
|
495
|
+
const { xObjectApiKey } = api;
|
|
496
|
+
if (!xObjectApiKey) return;
|
|
497
|
+
|
|
498
|
+
this.setState({ loading: true, error: null });
|
|
499
|
+
|
|
500
|
+
try {
|
|
501
|
+
const rawWhere = (api as { where?: unknown }).where;
|
|
502
|
+
const baseWhere: string[] = Array.isArray(rawWhere)
|
|
503
|
+
? rawWhere.filter((w): w is string => typeof w === 'string')
|
|
504
|
+
: typeof rawWhere === 'string' && rawWhere
|
|
505
|
+
? [rawWhere]
|
|
506
|
+
: [];
|
|
507
|
+
const filterWhere =
|
|
508
|
+
xObjectApiKey === 'opportunity' ? this.getFilterWhere() : [];
|
|
509
|
+
const [result, historyRows] = await Promise.all([
|
|
510
|
+
xObject.query({
|
|
511
|
+
...api,
|
|
512
|
+
where: [...baseWhere, ...filterWhere],
|
|
513
|
+
}),
|
|
514
|
+
this.fetchHistoryOpportunityList(),
|
|
515
|
+
]);
|
|
516
|
+
|
|
517
|
+
if (result && result.status) {
|
|
518
|
+
let records = result.data || [];
|
|
519
|
+
|
|
520
|
+
console.log('[SimpleTable__c] records:', records, historyRows);
|
|
521
|
+
|
|
522
|
+
// 根据阶段过滤数据
|
|
523
|
+
if (this.state.activeStage) {
|
|
524
|
+
records = records.filter((record: any) => {
|
|
525
|
+
return record.customItem248__c === this.state.activeStage;
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const historyMap =
|
|
530
|
+
xObjectApiKey === 'opportunity'
|
|
531
|
+
? buildHistoryMap(historyRows)
|
|
532
|
+
: new Map<string, HistoryOppSnap>();
|
|
533
|
+
|
|
534
|
+
const merged = records.map((r: any) => {
|
|
535
|
+
const id = r?.id != null ? String(r.id) : '';
|
|
536
|
+
const historyData = id ? historyMap.get(id) ?? null : null;
|
|
537
|
+
return { ...r, historyData };
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
this.setState({
|
|
541
|
+
dataSource: merged,
|
|
542
|
+
loading: false,
|
|
543
|
+
});
|
|
544
|
+
} else {
|
|
545
|
+
this.setState({
|
|
546
|
+
error: result?.msg || '获取数据失败',
|
|
547
|
+
loading: false,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
} catch (error: any) {
|
|
551
|
+
this.setState({
|
|
552
|
+
error: error.message || '获取数据失败',
|
|
553
|
+
loading: false,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* 与 stageSwitch 一致:更新 FilterBar filter 并重新拉取表格数据。
|
|
560
|
+
*/
|
|
561
|
+
@NeoEvent.function
|
|
562
|
+
setFilter(filter?: any) {
|
|
563
|
+
if (isEqual(filter, this.state.filter)) {
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
console.log('[SimpleTable__c] setFilter:', filter);
|
|
567
|
+
this.setState({ filter }, () => {
|
|
568
|
+
this.loadData();
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* 更新当前激活的销售阶段
|
|
574
|
+
*/
|
|
575
|
+
@NeoEvent.function
|
|
576
|
+
updateActiveStage(activeStage?: string) {
|
|
577
|
+
if (isEqual(activeStage, this.state.activeStage)) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
console.log('[SimpleTable__c] updateActiveStage:', activeStage);
|
|
581
|
+
this.setState({ activeStage: activeStage || '' }, () => {
|
|
582
|
+
this.loadData();
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* 渲染组件
|
|
588
|
+
* @returns 组件 JSX 元素
|
|
589
|
+
*/
|
|
590
|
+
render() {
|
|
591
|
+
const { title: stateTitle, dataSource, loading, error } = this.state;
|
|
592
|
+
const { className, title: propsTitle, description } = this.props;
|
|
593
|
+
// const { xObjectApiKey } = this.props.xObjectDataApi || {};
|
|
594
|
+
const { xObjectApiKey } = defaultXObjectDataApi;
|
|
595
|
+
const columns = this.generateColumns();
|
|
596
|
+
|
|
597
|
+
// 优先使用 props.title,其次使用 state.title,最后使用默认值
|
|
598
|
+
const displayTitle = propsTitle || stateTitle || '数据表格';
|
|
599
|
+
|
|
600
|
+
const moneyKey = this.getMoneyFieldApiKey();
|
|
601
|
+
const totalAmount = moneyKey ? sumMoneyRows(dataSource, moneyKey) : 0;
|
|
602
|
+
const oppCount = dataSource.length;
|
|
603
|
+
const oppWord = oppCount === 1 ? 'opportunity' : 'opportunities';
|
|
604
|
+
const summaryText = `Total: ${formatMoneySummaryAmount(
|
|
605
|
+
totalAmount,
|
|
606
|
+
)} · ${oppCount} ${oppWord}`;
|
|
607
|
+
|
|
608
|
+
console.log(
|
|
609
|
+
'simpleTable__c / dataSource:',
|
|
610
|
+
dataSource,
|
|
611
|
+
this.props,
|
|
612
|
+
this.state,
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
return (
|
|
616
|
+
<div className={`simpleTable__c ${className}`} data-time="2026.4.15 01">
|
|
617
|
+
<div className="table-wrapper">
|
|
618
|
+
<div className="panel-header">
|
|
619
|
+
<div className="panel-header-text">
|
|
620
|
+
<h3>
|
|
621
|
+
{displayTitle} - {this.state.activeStage}
|
|
622
|
+
</h3>
|
|
623
|
+
{description ? (
|
|
624
|
+
<p className="panel-header-description">{description}</p>
|
|
625
|
+
) : null}
|
|
626
|
+
</div>
|
|
627
|
+
<div className="panel-header-summary" title={summaryText}>
|
|
628
|
+
Total:{' '}
|
|
629
|
+
<span className="summary-amount">
|
|
630
|
+
{formatMoneySummaryAmount(totalAmount)}
|
|
631
|
+
</span>
|
|
632
|
+
{' · '}
|
|
633
|
+
<span className="summary-amount">{oppCount}</span> {oppWord}
|
|
634
|
+
</div>
|
|
635
|
+
</div>
|
|
636
|
+
<div className="table-container">
|
|
637
|
+
<Spin spinning={loading} tip="加载数据中...">
|
|
638
|
+
{error ? (
|
|
639
|
+
<Empty
|
|
640
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
641
|
+
description={
|
|
642
|
+
<div>
|
|
643
|
+
<div style={{ color: '#ff4d4f', marginBottom: 8 }}>
|
|
644
|
+
{error}
|
|
645
|
+
</div>
|
|
646
|
+
</div>
|
|
647
|
+
}
|
|
648
|
+
/>
|
|
649
|
+
) : (
|
|
650
|
+
<Table
|
|
651
|
+
key={`${xObjectApiKey}-${columns.length}`}
|
|
652
|
+
columns={columns}
|
|
653
|
+
dataSource={dataSource}
|
|
654
|
+
rowKey="id"
|
|
655
|
+
pagination={false}
|
|
656
|
+
scroll={{ x: 'max-content' }}
|
|
657
|
+
/>
|
|
658
|
+
)}
|
|
659
|
+
</Spin>
|
|
660
|
+
</div>
|
|
661
|
+
</div>
|
|
662
|
+
</div>
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
}
|