neo-cmp-cli 1.13.11 → 1.13.13

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.
Files changed (100) hide show
  1. package/dist/neo/neoService.js +1 -1
  2. package/dist/package.json.js +1 -1
  3. package/package.json +1 -1
  4. package/template/antd-custom-cmp-template/package.json +1 -1
  5. package/template/asset-manage-template/package.json +1 -1
  6. package/template/echarts-custom-cmp-template/package.json +1 -1
  7. package/template/empty-custom-cmp-template/package.json +1 -1
  8. package/template/map-custom-cmp-template/package.json +1 -1
  9. package/template/neo-bi-cmps/docs/gartner-pipeline-apis.md +279 -0
  10. package/template/neo-bi-cmps/docs/gartner-pipeline-prd.md +389 -0
  11. package/template/neo-bi-cmps/docs/neo-backend-dev/SKILL.md +188 -0
  12. package/template/neo-bi-cmps/docs/neo-backend-dev/references/01-Trigger/345/274/200/345/217/221.md +183 -0
  13. 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 +196 -0
  14. 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 +346 -0
  15. 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 +188 -0
  16. package/template/neo-bi-cmps/docs/neo-backend-dev/references/05-/351/241/265/351/235/242/345/274/200/345/217/221.md +293 -0
  17. 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 +175 -0
  18. 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 +313 -0
  19. package/template/neo-bi-cmps/docs/neo-backend-dev/references/auth-config.md +77 -0
  20. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/deploy_server_script.py +118 -0
  21. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/download_server_script.py +74 -0
  22. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entity_desc.py +69 -0
  23. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/gen_entitylist.py +87 -0
  24. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/query_crm.py +65 -0
  25. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/uninstall_server_script.py +48 -0
  26. package/template/neo-bi-cmps/docs/neo-backend-dev/scripts/update_model_jar.py +49 -0
  27. package/template/neo-bi-cmps/docs/neo-frontend-dev/SKILL.md +138 -0
  28. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/auth-config.md +77 -0
  29. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/component-dev.md +205 -0
  30. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/entityTable-example.md +167 -0
  31. package/template/neo-bi-cmps/docs/neo-frontend-dev/references/templates.md +38 -0
  32. package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entity_desc.py +69 -0
  33. package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/gen_entitylist.py +87 -0
  34. package/template/neo-bi-cmps/docs/neo-frontend-dev/scripts/query_crm.py +65 -0
  35. package/template/neo-bi-cmps/docs/prototype-pipeline-forecasting.html +2453 -0
  36. 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 +10 -0
  37. package/template/neo-bi-cmps/package.json +1 -1
  38. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/README.md +52 -0
  39. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/index.tsx +176 -0
  40. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/model.ts +49 -0
  41. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/style.scss +218 -0
  42. package/template/neo-bi-cmps/src/components/filterBar__c/README.md +35 -0
  43. package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +186 -0
  44. package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +72 -0
  45. package/template/neo-bi-cmps/src/components/filterBar__c/style.scss +212 -0
  46. package/template/neo-bi-cmps/src/components/forecastChart__c/README.md +31 -0
  47. package/template/neo-bi-cmps/src/components/forecastChart__c/index.tsx +161 -0
  48. package/template/neo-bi-cmps/src/components/forecastChart__c/model.ts +39 -0
  49. package/template/neo-bi-cmps/src/components/forecastChart__c/style.scss +154 -0
  50. package/template/neo-bi-cmps/src/components/forecastGrid__c/README.md +36 -0
  51. package/template/neo-bi-cmps/src/components/forecastGrid__c/index.tsx +86 -0
  52. package/template/neo-bi-cmps/src/components/forecastGrid__c/model.ts +34 -0
  53. package/template/neo-bi-cmps/src/components/forecastGrid__c/style.scss +48 -0
  54. package/template/neo-bi-cmps/src/components/gapCloser__c/README.md +24 -0
  55. package/template/neo-bi-cmps/src/components/gapCloser__c/index.tsx +95 -0
  56. package/template/neo-bi-cmps/src/components/gapCloser__c/model.ts +43 -0
  57. package/template/neo-bi-cmps/src/components/gapCloser__c/style.scss +60 -0
  58. package/template/neo-bi-cmps/src/components/kpiCards__c/README.md +35 -0
  59. package/template/neo-bi-cmps/src/components/kpiCards__c/index.tsx +70 -0
  60. package/template/neo-bi-cmps/src/components/kpiCards__c/model.ts +35 -0
  61. package/template/neo-bi-cmps/src/components/kpiCards__c/style.scss +33 -0
  62. package/template/neo-bi-cmps/src/components/oppList__c/README.md +52 -0
  63. package/template/neo-bi-cmps/src/components/oppList__c/index.tsx +228 -0
  64. package/template/neo-bi-cmps/src/components/oppList__c/model.ts +40 -0
  65. package/template/neo-bi-cmps/src/components/oppList__c/style.scss +133 -0
  66. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/README.md +39 -0
  67. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/index.tsx +128 -0
  68. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/model.ts +42 -0
  69. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/style.scss +133 -0
  70. package/template/neo-bi-cmps/src/components/stageSwitch__c/README.md +36 -0
  71. package/template/neo-bi-cmps/src/components/stageSwitch__c/index.tsx +103 -0
  72. package/template/neo-bi-cmps/src/components/stageSwitch__c/model.ts +37 -0
  73. package/template/neo-bi-cmps/src/components/stageSwitch__c/style.scss +89 -0
  74. package/template/neo-bi-cmps/src/components/stageTimeChart__c/README.md +37 -0
  75. package/template/neo-bi-cmps/src/components/stageTimeChart__c/index.tsx +126 -0
  76. package/template/neo-bi-cmps/src/components/stageTimeChart__c/model.ts +35 -0
  77. package/template/neo-bi-cmps/src/components/stageTimeChart__c/style.scss +140 -0
  78. package/template/neo-bi-cmps/src/components/tabSwitch__c/README.md +37 -0
  79. package/template/neo-bi-cmps/src/components/tabSwitch__c/index.tsx +80 -0
  80. package/template/neo-bi-cmps/src/components/tabSwitch__c/model.ts +45 -0
  81. package/template/neo-bi-cmps/src/components/tabSwitch__c/style.scss +37 -0
  82. package/template/neo-custom-cmp-template/package.json +1 -1
  83. package/template/neo-custom-cmp-template/src/components/entityForm__c/index.tsx +48 -54
  84. package/template/neo-custom-cmp-template/src/components/entityForm__c/model.ts +1 -1
  85. package/template/neo-custom-cmp-template/src/components/entityForm__c/style.scss +80 -77
  86. package/template/neo-h5-cmps/package.json +1 -1
  87. package/template/neo-order-cmps/package.json +1 -1
  88. package/template/neo-web-entity-grid/package.json +1 -1
  89. package/template/neo-web-entity-grid/src/components/createForm__c/index.tsx +46 -54
  90. package/template/neo-web-entity-grid/src/components/createForm__c/resetAntd.scss +74 -0
  91. package/template/neo-web-entity-grid/src/components/createForm__c/style.scss +81 -152
  92. package/template/neo-web-entity-grid/src/components/searchForm__c/index.tsx +47 -52
  93. package/template/neo-web-entity-grid/src/components/searchForm__c/style.scss +60 -74
  94. package/template/neo-web-form/package.json +1 -1
  95. package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +16 -7
  96. package/template/neo-web-form/src/components/batchAddTable__c/style.scss +14 -0
  97. package/template/neo-web-form/src/components/batchAddTable__c/tableModal.scss +60 -13
  98. package/template/react-custom-cmp-template/package.json +1 -1
  99. package/template/react-ts-custom-cmp-template/package.json +1 -1
  100. package/template/vue2-custom-cmp-template/package.json +1 -1
@@ -0,0 +1,35 @@
1
+ export class KpiCardsModel {
2
+ label: string = 'KPI指标卡片';
3
+ description: string = '展示Quota、Closed、Forecast、AI Forecast等核心KPI指标';
4
+ iconUrl: string = 'https://custom-widgets.bj.bcebos.com/kpiCards.svg';
5
+ targetPage: string[] = ['all'];
6
+ targetDevice: string = 'all';
7
+
8
+ defaultComProps = {
9
+ columns: 2,
10
+ items: [
11
+ { label: 'Quota', value: '$10,000,000' },
12
+ { label: 'Closed', value: '$3,200,000', subLabel: 'Gap: $6,800,000', subDirection: 'negative' },
13
+ { label: 'Forecast', value: '$7,500,000', subLabel: 'Gap: $2,500,000', subDirection: 'negative' },
14
+ { label: '✨ AI Forecast', value: '$6,800,000', subLabel: 'Gap: $3,200,000', subDirection: 'negative' },
15
+ ],
16
+ };
17
+
18
+ functions = [
19
+ {
20
+ apiKey: 'refreshData',
21
+ label: '刷新数据',
22
+ helpTextKey: '刷新KPI指标数据',
23
+ },
24
+ ];
25
+
26
+ propsSchema = [
27
+ {
28
+ type: 'number',
29
+ name: 'columns',
30
+ label: '列数',
31
+ },
32
+ ];
33
+ }
34
+
35
+ export default KpiCardsModel;
@@ -0,0 +1,33 @@
1
+ .kpi-cards__c {
2
+ .kpi-card {
3
+ background: #fff;
4
+ border-radius: 8px;
5
+ padding: 16px 20px;
6
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
7
+
8
+ .kpi-label {
9
+ font-size: 12px;
10
+ color: #888;
11
+ margin-bottom: 4px;
12
+ }
13
+
14
+ .kpi-value {
15
+ font-size: 22px;
16
+ font-weight: 700;
17
+ }
18
+
19
+ .kpi-sub {
20
+ font-size: 12px;
21
+ color: #999;
22
+ margin-top: 4px;
23
+
24
+ &.negative {
25
+ color: #ef4444;
26
+ }
27
+
28
+ &.positive {
29
+ color: #22c55e;
30
+ }
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,52 @@
1
+ # OppList 组件
2
+
3
+ 商机列表组件,展示商机数据表格,支持排序、筛选、Tab切换等功能。
4
+
5
+ ## 使用方式
6
+
7
+ ```tsx
8
+ import OppList from './components/oppList__c';
9
+
10
+ <OppList
11
+ title="Opportunity List"
12
+ tabs={[
13
+ { key: 'watch', label: '✨ Prioritized Deals' },
14
+ { key: 'all', label: 'All Opportunities' },
15
+ ]}
16
+ filterChips={['All (3)', 'Rescue (1)', 'Upgrade (2)']}
17
+ columns={['Name', 'Amount', 'Close Date', 'Stage']}
18
+ rows={[...]}
19
+ onRowClick={(opp) => console.log('Clicked:', opp)}
20
+ />
21
+ ```
22
+
23
+ ## Props
24
+
25
+ | 属性 | 说明 | 类型 | 默认值 |
26
+ |------|------|------|--------|
27
+ | title | 标题 | string | 'Opportunity List' |
28
+ | summaryText | 摘要文本 | string | - |
29
+ | tabs | Tab配置 | OppTab[] | [] |
30
+ | activeTab | 当前Tab | string | 第一个Tab |
31
+ | filterChips | 筛选 Chips | string[] | [] |
32
+ | columns | 表头列 | string[] | [] |
33
+ | rows | 商机数据 | Opportunity[] | [] |
34
+ | onTabChange | Tab切换回调 | (tab: string) => void | - |
35
+ | onChipChange | Chip切换回调 | (chip: string) => void | - |
36
+ | onSort | 排序回调 | (column: string) => void | - |
37
+ | onRowClick | 行点击回调 | (opp: Opportunity) => void | - |
38
+
39
+ ## Opportunity
40
+
41
+ | 属性 | 说明 | 类型 |
42
+ |------|------|------|
43
+ | name | 商机名称 | string |
44
+ | account | 账户 | string |
45
+ | forecastType | 预测类型 | string |
46
+ | amount | 金额 | string |
47
+ | closeDate | 关闭日期 | string |
48
+ | stage | 阶段 | string |
49
+ | aiScore | AI评分 | string |
50
+ | aiWinRate | AI赢率 | string |
51
+ | lastActivity | 最近活动 | string |
52
+ | source | 来源 | 'rescue' \| 'upgrade' \| 'all' |
@@ -0,0 +1,228 @@
1
+ /**
2
+ * @file 商机列表组件
3
+ * @description 展示商机数据表格,支持排序、筛选、Tab切换等功能
4
+ */
5
+ import * as React from 'react';
6
+ // @ts-ignore
7
+ import { BaseCmp, StatusHoc, NeoEvent } from 'neo-ui-common';
8
+
9
+ import './style.scss';
10
+
11
+ interface OppTab {
12
+ key: string;
13
+ label: string;
14
+ icon?: string;
15
+ }
16
+
17
+ interface Opportunity {
18
+ name: string;
19
+ account?: string;
20
+ forecastType?: string;
21
+ amount: string;
22
+ closeDate: string;
23
+ stage: string;
24
+ aiScore?: string;
25
+ aiScoreColor?: string;
26
+ aiWinRate?: string;
27
+ lastActivity?: string;
28
+ source?: 'rescue' | 'upgrade' | 'all';
29
+ }
30
+
31
+ interface OppListProps {
32
+ title?: string;
33
+ summaryText?: string;
34
+ tabs?: OppTab[];
35
+ activeTab?: string;
36
+ filterChips?: string[];
37
+ activeChip?: string;
38
+ columns?: string[];
39
+ rows?: Opportunity[];
40
+ onTabChange?: (tab: string) => void;
41
+ onChipChange?: (chip: string) => void;
42
+ onSort?: (column: string) => void;
43
+ onRowClick?: (opp: Opportunity) => void;
44
+ className?: string;
45
+ style?: React.CSSProperties;
46
+ }
47
+
48
+ interface OppListState {
49
+ activeTab: string;
50
+ activeChip: string;
51
+ sortColumn?: string;
52
+ sortDirection?: 'asc' | 'desc';
53
+ }
54
+
55
+ class OppList extends BaseCmp<OppListProps, OppListState> {
56
+ constructor(props: OppListProps) {
57
+ super(props);
58
+ this.state = {
59
+ activeTab: props.activeTab || (props.tabs?.[0]?.key ?? 'watch'),
60
+ activeChip: props.activeChip || (props.filterChips?.[0] ?? 'all'),
61
+ sortColumn: undefined,
62
+ sortDirection: undefined,
63
+ };
64
+ }
65
+
66
+ componentDidMount() {
67
+ console.log('OppList 组件挂载');
68
+ }
69
+
70
+ handleTabChange = (tab: string) => {
71
+ this.setState({ activeTab: tab });
72
+ const { onTabChange } = this.props;
73
+ if (onTabChange) {
74
+ onTabChange(tab);
75
+ }
76
+ };
77
+
78
+ handleChipChange = (chip: string) => {
79
+ this.setState({ activeChip: chip });
80
+ const { onChipChange } = this.props;
81
+ if (onChipChange) {
82
+ onChipChange(chip);
83
+ }
84
+ };
85
+
86
+ handleSort = (column: string) => {
87
+ const { sortColumn, sortDirection } = this.state;
88
+ let newDirection: 'asc' | 'desc' = 'asc';
89
+
90
+ if (sortColumn === column) {
91
+ newDirection = sortDirection === 'asc' ? 'desc' : 'asc';
92
+ }
93
+
94
+ this.setState({ sortColumn: column, sortDirection: newDirection });
95
+ const { onSort } = this.props;
96
+ if (onSort) {
97
+ onSort(column);
98
+ }
99
+ };
100
+
101
+ getScoreBadgeStyle = (score: string, color?: string) => {
102
+ const bgColor = color || '#22c55e';
103
+ return {
104
+ backgroundColor: bgColor,
105
+ color: '#fff',
106
+ padding: '2px 8px',
107
+ borderRadius: '10px',
108
+ fontSize: '11px',
109
+ };
110
+ };
111
+
112
+ filterRows = () => {
113
+ const { rows = [] } = this.props;
114
+ const { activeChip } = this.state;
115
+
116
+ if (activeChip === 'all') {
117
+ return rows;
118
+ }
119
+
120
+ return rows.filter((row) => row.source === activeChip);
121
+ };
122
+
123
+ render() {
124
+ const {
125
+ title = 'Opportunity List',
126
+ summaryText,
127
+ tabs = [],
128
+ filterChips = [],
129
+ columns = [],
130
+ className,
131
+ style,
132
+ } = this.props;
133
+
134
+ const { activeTab, activeChip, sortColumn, sortDirection } = this.state;
135
+ const filteredRows = this.filterRows();
136
+
137
+ return (
138
+ <div className={`opp-list__c ${className || ''}`} style={style}>
139
+ {/* Tab切换 */}
140
+ {tabs.length > 0 && (
141
+ <div className="opp-tabs">
142
+ {tabs.map((tab) => (
143
+ <button
144
+ key={tab.key}
145
+ className={`opp-tab-btn ${activeTab === tab.key ? 'active' : ''}`}
146
+ onClick={() => this.handleTabChange(tab.key)}
147
+ >
148
+ {tab.icon} {tab.label}
149
+ </button>
150
+ ))}
151
+ </div>
152
+ )}
153
+
154
+ {/* 筛选Chips */}
155
+ {filterChips.length > 0 && (
156
+ <div className="filter-chips">
157
+ {filterChips.map((chip, index) => (
158
+ <button
159
+ key={index}
160
+ className={`watch-sub-tab ${activeChip === chip.split('(')[0].trim().toLowerCase() ? 'active' : ''}`}
161
+ onClick={() => this.handleChipChange(chip.split('(')[0].trim().toLowerCase())}
162
+ >
163
+ {chip}
164
+ </button>
165
+ ))}
166
+ </div>
167
+ )}
168
+
169
+ {/* 标题和摘要 */}
170
+ <div className="list-header">
171
+ <h3 className="list-title">{title}</h3>
172
+ {summaryText && <div className="list-summary">{summaryText}</div>}
173
+ </div>
174
+
175
+ {/* 表格 */}
176
+ <div className="table-wrapper">
177
+ <table className="opp-table">
178
+ <thead>
179
+ <tr>
180
+ {columns.map((col, index) => (
181
+ <th key={index} style={{ textAlign: col === 'Amount' ? 'right' : col === 'Close Date' || col === 'Stage' || col === 'Last Activity' || col === 'AI Score' || col === 'AI Win Rate' ? 'center' : 'left' }}>
182
+ {col}
183
+ <span
184
+ className="sort-icon"
185
+ onClick={() => this.handleSort(col)}
186
+ style={{ color: sortColumn === col ? '#6366f1' : '#ccc', marginLeft: '4px' }}
187
+ >
188
+ {sortColumn === col ? (sortDirection === 'asc' ? '↑' : '↓') : '⇅'}
189
+ </span>
190
+ </th>
191
+ ))}
192
+ </tr>
193
+ </thead>
194
+ <tbody>
195
+ {filteredRows.map((opp, index) => (
196
+ <tr key={index} className={opp.source ? `opp-row source-${opp.source}` : ''}>
197
+ <td style={{ textAlign: 'left', color: '#3b82f6', cursor: 'pointer' }}>{opp.name}</td>
198
+ {opp.account && <td style={{ textAlign: 'left' }}>{opp.account}</td>}
199
+ {opp.forecastType && <td style={{ textAlign: 'left' }}>{opp.forecastType}</td>}
200
+ <td style={{ textAlign: 'right' }}>{opp.amount}</td>
201
+ <td style={{ textAlign: 'center' }}>{opp.closeDate}</td>
202
+ <td style={{ textAlign: 'center' }}>{opp.stage}</td>
203
+ {opp.aiScore && (
204
+ <td style={{ textAlign: 'center' }}>
205
+ <span style={this.getScoreBadgeStyle(opp.aiScore, opp.aiScoreColor)}>
206
+ {opp.aiScore}
207
+ </span>
208
+ </td>
209
+ )}
210
+ {opp.aiWinRate && (
211
+ <td style={{ textAlign: 'center', color: '#6366f1', fontWeight: 600 }}>
212
+ {opp.aiWinRate}
213
+ </td>
214
+ )}
215
+ {opp.lastActivity && (
216
+ <td style={{ textAlign: 'center', color: '#999' }}>{opp.lastActivity}</td>
217
+ )}
218
+ </tr>
219
+ ))}
220
+ </tbody>
221
+ </table>
222
+ </div>
223
+ </div>
224
+ );
225
+ }
226
+ }
227
+
228
+ export default StatusHoc(OppList);
@@ -0,0 +1,40 @@
1
+ export class OppListModel {
2
+ label: string = '商机列表';
3
+ description: string = '展示商机数据表格,支持排序、筛选、Tab切换等功能';
4
+ iconUrl: string = 'https://custom-widgets.bj.bcebos.com/oppList.svg';
5
+ targetPage: string[] = ['all'];
6
+ targetDevice: string = 'all';
7
+
8
+ defaultComProps = {
9
+ title: 'Opportunity List',
10
+ tabs: [
11
+ { key: 'watch', label: '✨ Prioritized Deals', icon: '' },
12
+ { key: 'all', label: 'All Opportunities', icon: '' },
13
+ ],
14
+ filterChips: ['All (3)', 'Rescue (1)', 'Upgrade (2)'],
15
+ columns: ['Opportunity Name', 'Account', 'Forecast Type', 'Amount', 'Close Date', 'Stage', '✨ AI Score', '✨ AI Win Rate', 'Last Activity'],
16
+ rows: [
17
+ { name: 'Apollo Project', account: 'Huawei Tech', forecastType: 'Commit', amount: '$1,000,000', closeDate: '2026-04-15', stage: 'Negotiation', aiScore: 'Medium', aiScoreColor: '#f59e0b', aiWinRate: '42%', lastActivity: '2026-03-28', source: 'rescue' },
18
+ { name: 'Aurora Solution', account: 'ByteDance', forecastType: 'Best Case', amount: '$800,000', closeDate: '2026-04-30', stage: 'Proposal', aiScore: 'High', aiScoreColor: '#22c55e', aiWinRate: '91%', lastActivity: '2026-03-29', source: 'upgrade' },
19
+ { name: 'Cloud Migration', account: 'Tencent', forecastType: 'Pipeline', amount: '$1,700,000', closeDate: '2026-05-20', stage: 'Discovery', aiScore: 'Medium', aiScoreColor: '#f59e0b', aiWinRate: '78%', lastActivity: '2026-03-27', source: 'upgrade' },
20
+ ],
21
+ };
22
+
23
+ functions = [
24
+ {
25
+ apiKey: 'refreshData',
26
+ label: '刷新数据',
27
+ helpTextKey: '刷新商机列表数据',
28
+ },
29
+ ];
30
+
31
+ propsSchema = [
32
+ {
33
+ type: 'string',
34
+ name: 'title',
35
+ label: '标题',
36
+ },
37
+ ];
38
+ }
39
+
40
+ export default OppListModel;
@@ -0,0 +1,133 @@
1
+ .opp-list__c {
2
+ background: #fff;
3
+ border-radius: 8px;
4
+ padding: 20px;
5
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
6
+ overflow-x: auto;
7
+
8
+ .opp-tabs {
9
+ display: flex;
10
+ gap: 0;
11
+ margin-bottom: 16px;
12
+ border-bottom: 2px solid #e5e7eb;
13
+ }
14
+
15
+ .opp-tab-btn {
16
+ padding: 8px 20px;
17
+ font-size: 13px;
18
+ font-weight: 600;
19
+ cursor: pointer;
20
+ border: none;
21
+ background: none;
22
+ color: #999;
23
+ border-bottom: 2px solid transparent;
24
+ margin-bottom: -2px;
25
+ transition: all 0.2s;
26
+
27
+ &:hover {
28
+ color: #6366f1;
29
+ }
30
+
31
+ &.active {
32
+ color: #6366f1;
33
+ border-bottom-color: #6366f1;
34
+ }
35
+ }
36
+
37
+ .filter-chips {
38
+ display: flex;
39
+ gap: 8px;
40
+ margin-bottom: 16px;
41
+ }
42
+
43
+ .watch-sub-tab {
44
+ padding: 6px 14px;
45
+ font-size: 12px;
46
+ font-weight: 600;
47
+ cursor: pointer;
48
+ border: 1px solid #ddd;
49
+ background: #fff;
50
+ color: #666;
51
+ border-radius: 20px;
52
+ transition: all 0.2s;
53
+
54
+ &:hover {
55
+ border-color: #6366f1;
56
+ }
57
+
58
+ &.active {
59
+ border: 1px solid #6366f1;
60
+ background: #f0f0ff;
61
+ color: #6366f1;
62
+ }
63
+ }
64
+
65
+ .list-header {
66
+ display: flex;
67
+ justify-content: space-between;
68
+ align-items: center;
69
+ margin-bottom: 12px;
70
+ }
71
+
72
+ .list-title {
73
+ font-size: 14px;
74
+ font-weight: 600;
75
+ margin: 0;
76
+ }
77
+
78
+ .list-summary {
79
+ font-size: 13px;
80
+ color: #666;
81
+
82
+ span {
83
+ font-weight: 700;
84
+ color: #333;
85
+ }
86
+ }
87
+
88
+ .table-wrapper {
89
+ overflow-x: auto;
90
+ }
91
+
92
+ .opp-table {
93
+ width: 100%;
94
+ border-collapse: collapse;
95
+ font-size: 13px;
96
+
97
+ th,
98
+ td {
99
+ padding: 10px 12px;
100
+ border-bottom: 1px solid #f0f0f0;
101
+ }
102
+
103
+ th {
104
+ background: #f8f9fa;
105
+ font-weight: 600;
106
+ color: #333;
107
+ border-bottom: 2px solid #e5e7eb;
108
+ user-select: none;
109
+ }
110
+
111
+ td {
112
+ color: #333;
113
+ }
114
+
115
+ tbody tr {
116
+ transition: background 0.15s;
117
+ border-bottom: 1px solid #f0f0f0;
118
+
119
+ &:hover {
120
+ background: #fafafa;
121
+ }
122
+ }
123
+
124
+ .sort-icon {
125
+ cursor: pointer;
126
+ font-size: 10px;
127
+
128
+ &:hover {
129
+ color: #6366f1 !important;
130
+ }
131
+ }
132
+ }
133
+ }
@@ -0,0 +1,39 @@
1
+ # PipelineFunnel 组件
2
+
3
+ Pipeline漏斗图组件,展示销售管道的漏斗转化情况。
4
+
5
+ ## 使用方式
6
+
7
+ ```tsx
8
+ import PipelineFunnel from './components/pipelineFunnel__c';
9
+
10
+ <PipelineFunnel
11
+ title="Pipeline Funnel"
12
+ totalAmount="$11.1M"
13
+ stages={[
14
+ { name: 'Prospecting', amount: '$3.2M', count: 15, color: '#3b82f6' },
15
+ { name: 'Needs Analysis', amount: '$1.8M', count: 7, conversionRate: '56.3%', color: '#22c55e' },
16
+ ]}
17
+ onStageClick={(name) => console.log('Clicked:', name)}
18
+ />
19
+ ```
20
+
21
+ ## Props
22
+
23
+ | 属性 | 说明 | 类型 | 默认值 |
24
+ |------|------|------|--------|
25
+ | title | 标题 | string | 'Pipeline Funnel' |
26
+ | totalAmount | 总金额 | string | '$11.1M' |
27
+ | stages | 漏斗阶段数据 | FunnelStage[] | [] |
28
+ | showAiButton | 显示AI按钮 | boolean | true |
29
+ | onStageClick | 阶段点击回调 | (stageName: string) => void | - |
30
+
31
+ ## FunnelStage
32
+
33
+ | 属性 | 说明 | 类型 |
34
+ |------|------|------|
35
+ | name | 阶段名称 | string |
36
+ | amount | 金额 | string |
37
+ | count | 数量 | number |
38
+ | conversionRate | 转化率 | string |
39
+ | color | 颜色 | string |
@@ -0,0 +1,128 @@
1
+ /**
2
+ * @file Pipeline漏斗图组件
3
+ * @description 展示销售管道的漏斗转化情况
4
+ */
5
+ import * as React from 'react';
6
+ // @ts-ignore
7
+ import { BaseCmp, StatusHoc, NeoEvent } from 'neo-ui-common';
8
+
9
+ import './style.scss';
10
+
11
+ interface FunnelStage {
12
+ name: string;
13
+ amount: string;
14
+ count: number;
15
+ conversionRate?: string;
16
+ color: string;
17
+ }
18
+
19
+ interface PipelineFunnelProps {
20
+ title?: string;
21
+ totalAmount?: string;
22
+ stages?: FunnelStage[];
23
+ showAiButton?: boolean;
24
+ onStageClick?: (stageName: string) => void;
25
+ className?: string;
26
+ style?: React.CSSProperties;
27
+ }
28
+
29
+ interface PipelineFunnelState {
30
+ loading: boolean;
31
+ }
32
+
33
+ class PipelineFunnel extends BaseCmp<PipelineFunnelProps, PipelineFunnelState> {
34
+ constructor(props: PipelineFunnelProps) {
35
+ super(props);
36
+ this.state = {
37
+ loading: false,
38
+ };
39
+ }
40
+
41
+ componentDidMount() {
42
+ console.log('PipelineFunnel 组件挂载');
43
+ }
44
+
45
+ getFunnelClipPath = (index: number, total: number): string => {
46
+ const offset = index * 10;
47
+ return `polygon(${offset}% 0%, ${100 - offset}% 0%, ${100 - offset - 10}% 100%, ${offset + 10}% 100%)`;
48
+ };
49
+
50
+ render() {
51
+ const {
52
+ title = 'Pipeline Funnel',
53
+ totalAmount = '$11.1M',
54
+ stages = [],
55
+ showAiButton = true,
56
+ onStageClick,
57
+ className,
58
+ style,
59
+ } = this.props;
60
+
61
+ return (
62
+ <div className={`pipeline-funnel__c ${className || ''}`} style={style}>
63
+ <div className="funnel-header">
64
+ <h3 className="funnel-title">{title}</h3>
65
+ {showAiButton && (
66
+ <span
67
+ className="ai-btn"
68
+ onClick={() => console.log('AI Analysis clicked')}
69
+ title="AI Analysis"
70
+ >
71
+
72
+ </span>
73
+ )}
74
+ </div>
75
+
76
+ <div className="funnel-total">
77
+ <div className="funnel-total-label">Sales Amount</div>
78
+ <div className="funnel-total-value">{totalAmount}</div>
79
+ </div>
80
+
81
+ <div className="funnel-body">
82
+ <div className="funnel-left-rates">
83
+ {stages.slice(0, -1).map((stage, index) => (
84
+ <div key={index} className="funnel-rate">
85
+ {stage.conversionRate || '—'}
86
+ </div>
87
+ ))}
88
+ </div>
89
+
90
+ <div className="funnel-main">
91
+ {stages.map((stage, index) => (
92
+ <div
93
+ key={index}
94
+ className="funnel-segment"
95
+ style={{ backgroundColor: stage.color }}
96
+ onClick={() => onStageClick?.(stage.name)}
97
+ >
98
+ {stage.name}
99
+ </div>
100
+ ))}
101
+ </div>
102
+
103
+ <div className="funnel-right-data">
104
+ {stages.map((stage, index) => (
105
+ <div key={index} className="funnel-data">
106
+ {stage.amount} / {stage.count}
107
+ </div>
108
+ ))}
109
+ </div>
110
+ </div>
111
+
112
+ <div className="funnel-legend">
113
+ {stages.map((stage, index) => (
114
+ <span key={index} className="legend-item">
115
+ <span
116
+ className="legend-dot"
117
+ style={{ backgroundColor: stage.color }}
118
+ />
119
+ {stage.name}
120
+ </span>
121
+ ))}
122
+ </div>
123
+ </div>
124
+ );
125
+ }
126
+ }
127
+
128
+ export default StatusHoc(PipelineFunnel);