neo-cmp-cli 1.13.12 → 1.13.15

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 (67) hide show
  1. package/README.md +1 -1
  2. package/dist/neo/neoEnvManager.js +1 -1
  3. package/dist/neo/neoLogin.js +1 -1
  4. package/dist/neo/neoRequire.js +1 -1
  5. package/dist/neo/neoService.js +1 -1
  6. package/dist/package.json.js +1 -1
  7. package/package.json +1 -1
  8. package/template/antd-custom-cmp-template/package.json +1 -1
  9. package/template/asset-manage-template/package.json +1 -1
  10. package/template/echarts-custom-cmp-template/package.json +1 -1
  11. package/template/empty-custom-cmp-template/package.json +1 -1
  12. package/template/map-custom-cmp-template/package.json +1 -1
  13. package/template/neo-bi-cmps/docs/gartner-pipeline-apis.md +45 -73
  14. package/template/neo-bi-cmps/package.json +1 -1
  15. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/README.md +52 -0
  16. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/index.tsx +183 -0
  17. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/model.ts +90 -0
  18. package/template/neo-bi-cmps/src/components/aiCommitDrawer__c/style.scss +218 -0
  19. package/template/neo-bi-cmps/src/components/filterBar__c/README.md +35 -0
  20. package/template/neo-bi-cmps/src/components/filterBar__c/index.tsx +200 -0
  21. package/template/neo-bi-cmps/src/components/filterBar__c/model.ts +72 -0
  22. package/template/neo-bi-cmps/src/components/filterBar__c/style.scss +212 -0
  23. package/template/neo-bi-cmps/src/components/forecastChart__c/README.md +31 -0
  24. package/template/neo-bi-cmps/src/components/forecastChart__c/index.tsx +158 -0
  25. package/template/neo-bi-cmps/src/components/forecastChart__c/model.ts +40 -0
  26. package/template/neo-bi-cmps/src/components/forecastChart__c/style.scss +154 -0
  27. package/template/neo-bi-cmps/src/components/forecastGrid__c/README.md +36 -0
  28. package/template/neo-bi-cmps/src/components/forecastGrid__c/index.tsx +86 -0
  29. package/template/neo-bi-cmps/src/components/forecastGrid__c/model.ts +62 -0
  30. package/template/neo-bi-cmps/src/components/forecastGrid__c/style.scss +48 -0
  31. package/template/neo-bi-cmps/src/components/gapCloser__c/README.md +24 -0
  32. package/template/neo-bi-cmps/src/components/gapCloser__c/index.tsx +100 -0
  33. package/template/neo-bi-cmps/src/components/gapCloser__c/model.ts +46 -0
  34. package/template/neo-bi-cmps/src/components/gapCloser__c/style.scss +60 -0
  35. package/template/neo-bi-cmps/src/components/kpiCards__c/README.md +35 -0
  36. package/template/neo-bi-cmps/src/components/kpiCards__c/index.tsx +70 -0
  37. package/template/neo-bi-cmps/src/components/kpiCards__c/model.ts +50 -0
  38. package/template/neo-bi-cmps/src/components/kpiCards__c/style.scss +33 -0
  39. package/template/neo-bi-cmps/src/components/oppList__c/README.md +52 -0
  40. package/template/neo-bi-cmps/src/components/oppList__c/index.tsx +285 -0
  41. package/template/neo-bi-cmps/src/components/oppList__c/model.ts +86 -0
  42. package/template/neo-bi-cmps/src/components/oppList__c/style.scss +133 -0
  43. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/README.md +39 -0
  44. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/index.tsx +130 -0
  45. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/model.ts +66 -0
  46. package/template/neo-bi-cmps/src/components/pipelineFunnel__c/style.scss +133 -0
  47. package/template/neo-bi-cmps/src/components/stageSwitch__c/README.md +36 -0
  48. package/template/neo-bi-cmps/src/components/stageSwitch__c/index.tsx +118 -0
  49. package/template/neo-bi-cmps/src/components/stageSwitch__c/model.ts +92 -0
  50. package/template/neo-bi-cmps/src/components/stageSwitch__c/style.scss +89 -0
  51. package/template/neo-bi-cmps/src/components/stageTimeChart__c/README.md +37 -0
  52. package/template/neo-bi-cmps/src/components/stageTimeChart__c/index.tsx +126 -0
  53. package/template/neo-bi-cmps/src/components/stageTimeChart__c/model.ts +57 -0
  54. package/template/neo-bi-cmps/src/components/stageTimeChart__c/style.scss +140 -0
  55. package/template/neo-bi-cmps/src/components/tabSwitch__c/README.md +37 -0
  56. package/template/neo-bi-cmps/src/components/tabSwitch__c/index.tsx +80 -0
  57. package/template/neo-bi-cmps/src/components/tabSwitch__c/model.ts +45 -0
  58. package/template/neo-bi-cmps/src/components/tabSwitch__c/style.scss +37 -0
  59. package/template/neo-custom-cmp-template/package.json +1 -1
  60. package/template/neo-h5-cmps/package.json +1 -1
  61. package/template/neo-order-cmps/package.json +1 -1
  62. package/template/neo-web-entity-grid/package.json +1 -1
  63. package/template/neo-web-form/package.json +1 -1
  64. package/template/neo-web-form/src/components/batchAddTable__c/index.tsx +17 -17
  65. package/template/react-custom-cmp-template/package.json +1 -1
  66. package/template/react-ts-custom-cmp-template/package.json +1 -1
  67. package/template/vue2-custom-cmp-template/package.json +1 -1
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @file KPI指标卡片组件
3
+ * @description 展示Quota、Closed、Forecast、AI Forecast等核心KPI指标
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 KpiItem {
12
+ label: string;
13
+ value: string;
14
+ subLabel?: string;
15
+ subDirection?: 'negative' | 'positive';
16
+ }
17
+
18
+ interface KpiCardsProps {
19
+ items?: KpiItem[];
20
+ columns?: number;
21
+ className?: string;
22
+ style?: React.CSSProperties;
23
+ }
24
+
25
+ interface KpiCardsState {
26
+ loading: boolean;
27
+ }
28
+
29
+ class KpiCards extends BaseCmp<KpiCardsProps, KpiCardsState> {
30
+ constructor(props: KpiCardsProps) {
31
+ super(props);
32
+ this.state = {
33
+ loading: false,
34
+ };
35
+ }
36
+
37
+ componentDidMount() {
38
+ console.log('KpiCards 组件挂载');
39
+ }
40
+
41
+ render() {
42
+ const { items = [], columns = 2, className, style } = this.props;
43
+
44
+ return (
45
+ <div
46
+ className={`kpi-cards__c ${className || ''}`}
47
+ style={{
48
+ ...style,
49
+ display: 'grid',
50
+ gridTemplateColumns: `repeat(${columns}, 1fr)`,
51
+ gap: '8px',
52
+ }}
53
+ >
54
+ {items.map((item, index) => (
55
+ <div key={index} className="kpi-card">
56
+ <div className="kpi-label">{item.label}</div>
57
+ <div className="kpi-value">{item.value}</div>
58
+ {item.subLabel && (
59
+ <div className={`kpi-sub ${item.subDirection || ''}`}>
60
+ {item.subLabel}
61
+ </div>
62
+ )}
63
+ </div>
64
+ ))}
65
+ </div>
66
+ );
67
+ }
68
+ }
69
+
70
+ export default StatusHoc(KpiCards);
@@ -0,0 +1,50 @@
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
+ {
13
+ label: 'Closed',
14
+ value: '$3,200,000',
15
+ subLabel: 'Gap: $6,800,000',
16
+ subDirection: 'negative',
17
+ },
18
+ {
19
+ label: 'Forecast',
20
+ value: '$7,500,000',
21
+ subLabel: 'Gap: $2,500,000',
22
+ subDirection: 'negative',
23
+ },
24
+ {
25
+ label: '✨ AI Forecast',
26
+ value: '$6,800,000',
27
+ subLabel: 'Gap: $3,200,000',
28
+ subDirection: 'negative',
29
+ },
30
+ ],
31
+ };
32
+
33
+ functions = [
34
+ {
35
+ apiKey: 'refreshData',
36
+ label: '刷新数据',
37
+ helpTextKey: '刷新KPI指标数据',
38
+ },
39
+ ];
40
+
41
+ propsSchema = [
42
+ {
43
+ type: 'number',
44
+ name: 'columns',
45
+ label: '列数',
46
+ },
47
+ ];
48
+ }
49
+
50
+ 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,285 @@
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 ${
146
+ activeTab === tab.key ? 'active' : ''
147
+ }`}
148
+ onClick={() => this.handleTabChange(tab.key)}
149
+ >
150
+ {tab.icon} {tab.label}
151
+ </button>
152
+ ))}
153
+ </div>
154
+ )}
155
+
156
+ {/* 筛选Chips */}
157
+ {filterChips.length > 0 && (
158
+ <div className="filter-chips">
159
+ {filterChips.map((chip, index) => (
160
+ <button
161
+ key={index}
162
+ className={`watch-sub-tab ${
163
+ activeChip === chip.split('(')[0].trim().toLowerCase()
164
+ ? 'active'
165
+ : ''
166
+ }`}
167
+ onClick={() =>
168
+ this.handleChipChange(chip.split('(')[0].trim().toLowerCase())
169
+ }
170
+ >
171
+ {chip}
172
+ </button>
173
+ ))}
174
+ </div>
175
+ )}
176
+
177
+ {/* 标题和摘要 */}
178
+ <div className="list-header">
179
+ <h3 className="list-title">{title}</h3>
180
+ {summaryText && <div className="list-summary">{summaryText}</div>}
181
+ </div>
182
+
183
+ {/* 表格 */}
184
+ <div className="table-wrapper">
185
+ <table className="opp-table">
186
+ <thead>
187
+ <tr>
188
+ {columns.map((col, index) => (
189
+ <th
190
+ key={index}
191
+ style={{
192
+ textAlign:
193
+ col === 'Amount'
194
+ ? 'right'
195
+ : col === 'Close Date' ||
196
+ col === 'Stage' ||
197
+ col === 'Last Activity' ||
198
+ col === 'AI Score' ||
199
+ col === 'AI Win Rate'
200
+ ? 'center'
201
+ : 'left',
202
+ }}
203
+ >
204
+ {col}
205
+ <span
206
+ className="sort-icon"
207
+ onClick={() => this.handleSort(col)}
208
+ style={{
209
+ color: sortColumn === col ? '#6366f1' : '#ccc',
210
+ marginLeft: '4px',
211
+ }}
212
+ >
213
+ {sortColumn === col
214
+ ? sortDirection === 'asc'
215
+ ? '↑'
216
+ : '↓'
217
+ : '⇅'}
218
+ </span>
219
+ </th>
220
+ ))}
221
+ </tr>
222
+ </thead>
223
+ <tbody>
224
+ {filteredRows.map((opp, index) => (
225
+ <tr
226
+ key={index}
227
+ className={opp.source ? `opp-row source-${opp.source}` : ''}
228
+ >
229
+ <td
230
+ style={{
231
+ textAlign: 'left',
232
+ color: '#3b82f6',
233
+ cursor: 'pointer',
234
+ }}
235
+ >
236
+ {opp.name}
237
+ </td>
238
+ {opp.account && (
239
+ <td style={{ textAlign: 'left' }}>{opp.account}</td>
240
+ )}
241
+ {opp.forecastType && (
242
+ <td style={{ textAlign: 'left' }}>{opp.forecastType}</td>
243
+ )}
244
+ <td style={{ textAlign: 'right' }}>{opp.amount}</td>
245
+ <td style={{ textAlign: 'center' }}>{opp.closeDate}</td>
246
+ <td style={{ textAlign: 'center' }}>{opp.stage}</td>
247
+ {opp.aiScore && (
248
+ <td style={{ textAlign: 'center' }}>
249
+ <span
250
+ style={this.getScoreBadgeStyle(
251
+ opp.aiScore,
252
+ opp.aiScoreColor,
253
+ )}
254
+ >
255
+ {opp.aiScore}
256
+ </span>
257
+ </td>
258
+ )}
259
+ {opp.aiWinRate && (
260
+ <td
261
+ style={{
262
+ textAlign: 'center',
263
+ color: '#6366f1',
264
+ fontWeight: 600,
265
+ }}
266
+ >
267
+ {opp.aiWinRate}
268
+ </td>
269
+ )}
270
+ {opp.lastActivity && (
271
+ <td style={{ textAlign: 'center', color: '#999' }}>
272
+ {opp.lastActivity}
273
+ </td>
274
+ )}
275
+ </tr>
276
+ ))}
277
+ </tbody>
278
+ </table>
279
+ </div>
280
+ </div>
281
+ );
282
+ }
283
+ }
284
+
285
+ export default StatusHoc(OppList);
@@ -0,0 +1,86 @@
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: [
16
+ 'Opportunity Name',
17
+ 'Account',
18
+ 'Forecast Type',
19
+ 'Amount',
20
+ 'Close Date',
21
+ 'Stage',
22
+ '✨ AI Score',
23
+ '✨ AI Win Rate',
24
+ 'Last Activity',
25
+ ],
26
+ rows: [
27
+ {
28
+ name: 'Apollo Project',
29
+ account: 'Huawei Tech',
30
+ forecastType: 'Commit',
31
+ amount: '$1,000,000',
32
+ closeDate: '2026-04-15',
33
+ stage: 'Negotiation',
34
+ aiScore: 'Medium',
35
+ aiScoreColor: '#f59e0b',
36
+ aiWinRate: '42%',
37
+ lastActivity: '2026-03-28',
38
+ source: 'rescue',
39
+ },
40
+ {
41
+ name: 'Aurora Solution',
42
+ account: 'ByteDance',
43
+ forecastType: 'Best Case',
44
+ amount: '$800,000',
45
+ closeDate: '2026-04-30',
46
+ stage: 'Proposal',
47
+ aiScore: 'High',
48
+ aiScoreColor: '#22c55e',
49
+ aiWinRate: '91%',
50
+ lastActivity: '2026-03-29',
51
+ source: 'upgrade',
52
+ },
53
+ {
54
+ name: 'Cloud Migration',
55
+ account: 'Tencent',
56
+ forecastType: 'Pipeline',
57
+ amount: '$1,700,000',
58
+ closeDate: '2026-05-20',
59
+ stage: 'Discovery',
60
+ aiScore: 'Medium',
61
+ aiScoreColor: '#f59e0b',
62
+ aiWinRate: '78%',
63
+ lastActivity: '2026-03-27',
64
+ source: 'upgrade',
65
+ },
66
+ ],
67
+ };
68
+
69
+ functions = [
70
+ {
71
+ apiKey: 'refreshData',
72
+ label: '刷新数据',
73
+ helpTextKey: '刷新商机列表数据',
74
+ },
75
+ ];
76
+
77
+ propsSchema = [
78
+ {
79
+ type: 'string',
80
+ name: 'title',
81
+ label: '标题',
82
+ },
83
+ ];
84
+ }
85
+
86
+ 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
+ }