mall-components 1.0.0

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 (122) hide show
  1. package/README.md +128 -0
  2. package/build/_components-raw.css +791 -0
  3. package/build/_shims/antd.js +1 -0
  4. package/build/_shims/icons.js +1 -0
  5. package/build/_shims/moment.js +1 -0
  6. package/build/_shims/react-dom.js +1 -0
  7. package/build/_shims/react.js +1 -0
  8. package/build/adapters/DataSourceAdapter.d.ts +46 -0
  9. package/build/components/AdminLayout/AdminLayout.d.ts +5 -0
  10. package/build/components/AdminLayout/Breadcrumb.d.ts +8 -0
  11. package/build/components/AdminLayout/MainContent.d.ts +17 -0
  12. package/build/components/AdminLayout/Navbar.d.ts +10 -0
  13. package/build/components/AdminLayout/Sidebar.d.ts +14 -0
  14. package/build/components/AdminLayout/TabBar.d.ts +13 -0
  15. package/build/components/AdminLayout/TabPane.d.ts +4 -0
  16. package/build/components/AdminLayout/index.d.ts +3 -0
  17. package/build/components/AdminLayout/types.d.ts +42 -0
  18. package/build/components/CouponCard/CouponCard.d.ts +20 -0
  19. package/build/components/CouponCard/index.d.ts +1 -0
  20. package/build/components/OrderForm/OrderForm.d.ts +18 -0
  21. package/build/components/OrderForm/index.d.ts +1 -0
  22. package/build/components/OrderList/OrderList.d.ts +29 -0
  23. package/build/components/OrderList/index.d.ts +1 -0
  24. package/build/components/ProductForm/ProductForm.d.ts +18 -0
  25. package/build/components/ProductForm/index.d.ts +3 -0
  26. package/build/components/ProductList/ProductList.d.ts +47 -0
  27. package/build/components/ProductList/index.d.ts +3 -0
  28. package/build/components/PromotionCard/PromotionCard.d.ts +22 -0
  29. package/build/components/PromotionCard/index.d.ts +1 -0
  30. package/build/components/RoleCard/RoleCard.d.ts +18 -0
  31. package/build/components/RoleCard/index.d.ts +1 -0
  32. package/build/components/UserCard/UserCard.d.ts +17 -0
  33. package/build/components/UserCard/index.d.ts +1 -0
  34. package/build/entry-meta.d.ts +603 -0
  35. package/build/index.css +1 -0
  36. package/build/index.js +1 -0
  37. package/build/mall-components-meta.js +2563 -0
  38. package/build/mall-components.cdn.umd.css +1 -0
  39. package/build/mall-components.cdn.umd.js +8 -0
  40. package/build/mall-components.codesandbox.combined.js +1094 -0
  41. package/build/mall-components.codesandbox.css +401 -0
  42. package/build/mall-components.codesandbox.js +1080 -0
  43. package/build/mall-components.umd.css +1 -0
  44. package/build/mall-components.umd.js +8 -0
  45. package/build/meta/adminLayoutMeta.d.ts +3 -0
  46. package/build/meta/couponCardMeta.d.ts +128 -0
  47. package/build/meta/icons.d.ts +10 -0
  48. package/build/meta/orderFormMeta.d.ts +111 -0
  49. package/build/meta/orderListMeta.d.ts +170 -0
  50. package/build/meta/productFormMeta.d.ts +3 -0
  51. package/build/meta/productListMeta.d.ts +200 -0
  52. package/build/meta/promotionCardMeta.d.ts +129 -0
  53. package/build/meta/roleCardMeta.d.ts +3 -0
  54. package/build/meta/tabPaneMeta.d.ts +3 -0
  55. package/build/meta/userCardMeta.d.ts +3 -0
  56. package/build/meta.d.ts +605 -0
  57. package/build/setters/RestApiTester.d.ts +11 -0
  58. package/build/types/common.d.ts +17 -0
  59. package/build/types/marketing.d.ts +128 -0
  60. package/build/types/order.d.ts +174 -0
  61. package/build/types/permission.d.ts +101 -0
  62. package/build/types/product.d.ts +47 -0
  63. package/package.json +1 -0
  64. package/src/adapters/DataSourceAdapter.ts +445 -0
  65. package/src/components/AdminLayout/AdminLayout.scss +447 -0
  66. package/src/components/AdminLayout/AdminLayout.tsx +681 -0
  67. package/src/components/AdminLayout/Breadcrumb.tsx +60 -0
  68. package/src/components/AdminLayout/MainContent.tsx +54 -0
  69. package/src/components/AdminLayout/Navbar.tsx +76 -0
  70. package/src/components/AdminLayout/Sidebar.tsx +256 -0
  71. package/src/components/AdminLayout/TabBar.tsx +177 -0
  72. package/src/components/AdminLayout/TabPane.tsx +29 -0
  73. package/src/components/AdminLayout/index.ts +3 -0
  74. package/src/components/AdminLayout/types.ts +46 -0
  75. package/src/components/CouponCard/CouponCard.scss +55 -0
  76. package/src/components/CouponCard/CouponCard.tsx +687 -0
  77. package/src/components/CouponCard/index.ts +1 -0
  78. package/src/components/OrderForm/OrderForm.scss +148 -0
  79. package/src/components/OrderForm/OrderForm.tsx +503 -0
  80. package/src/components/OrderForm/index.ts +1 -0
  81. package/src/components/OrderList/OrderList.scss +160 -0
  82. package/src/components/OrderList/OrderList.tsx +885 -0
  83. package/src/components/OrderList/index.ts +1 -0
  84. package/src/components/ProductForm/ProductForm.scss +23 -0
  85. package/src/components/ProductForm/ProductForm.tsx +442 -0
  86. package/src/components/ProductForm/index.ts +3 -0
  87. package/src/components/ProductList/ProductList.scss +293 -0
  88. package/src/components/ProductList/ProductList.tsx +454 -0
  89. package/src/components/ProductList/index.ts +3 -0
  90. package/src/components/PromotionCard/PromotionCard.scss +71 -0
  91. package/src/components/PromotionCard/PromotionCard.tsx +579 -0
  92. package/src/components/PromotionCard/index.ts +1 -0
  93. package/src/components/RoleCard/RoleCard.scss +77 -0
  94. package/src/components/RoleCard/RoleCard.tsx +463 -0
  95. package/src/components/RoleCard/index.ts +1 -0
  96. package/src/components/UserCard/UserCard.scss +51 -0
  97. package/src/components/UserCard/UserCard.tsx +432 -0
  98. package/src/components/UserCard/index.ts +1 -0
  99. package/src/entry-components.ts +39 -0
  100. package/src/entry-meta.ts +23 -0
  101. package/src/index.scss +4 -0
  102. package/src/index.ts +36 -0
  103. package/src/index.tsx +17 -0
  104. package/src/meta/adminLayoutMeta.ts +154 -0
  105. package/src/meta/couponCardMeta.ts +287 -0
  106. package/src/meta/icons.ts +41 -0
  107. package/src/meta/orderFormMeta.ts +279 -0
  108. package/src/meta/orderListMeta.ts +443 -0
  109. package/src/meta/productFormMeta.ts +253 -0
  110. package/src/meta/productListMeta.ts +434 -0
  111. package/src/meta/promotionCardMeta.ts +276 -0
  112. package/src/meta/roleCardMeta.ts +142 -0
  113. package/src/meta/tabPaneMeta.ts +69 -0
  114. package/src/meta/userCardMeta.ts +128 -0
  115. package/src/meta.ts +25 -0
  116. package/src/setters/RestApiTester.tsx +219 -0
  117. package/src/shims/require.js +8 -0
  118. package/src/types/common.ts +19 -0
  119. package/src/types/marketing.ts +124 -0
  120. package/src/types/order.ts +169 -0
  121. package/src/types/permission.ts +102 -0
  122. package/src/types/product.ts +49 -0
@@ -0,0 +1,454 @@
1
+ import React, { useMemo, useEffect, useCallback } from 'react'
2
+ import { Table, Input, Pagination, Card, Space, Button, Switch, Select } from 'antd'
3
+ import { SearchOutlined, ReloadOutlined, PlusOutlined, DownloadOutlined, UploadOutlined } from '@ant-design/icons'
4
+ import { DataSourceAdapterFactory } from '../../adapters/DataSourceAdapter'
5
+ import type { PmsProduct, ProductQueryParam } from '../../types/product'
6
+ import type { DataSourceConfig } from '../../adapters/DataSourceAdapter'
7
+ import './ProductList.scss'
8
+
9
+ interface ProductListProps {
10
+ dataSourceType?: 'rest' | 'mock' | 'variable'
11
+ api?: string
12
+ method?: 'GET' | 'POST'
13
+ mockData?: string | any
14
+ variableName?: string
15
+ dataSource?: any
16
+
17
+ showFilter?: boolean
18
+ showAction?: boolean
19
+ showSelection?: boolean
20
+ showOperation?: boolean
21
+ showStatus?: boolean
22
+ showPagination?: boolean
23
+
24
+ filterFields?: Array<{
25
+ name: string
26
+ label: string
27
+ type: 'input' | 'select' | 'date'
28
+ options?: Array<{ label: string; value: any }>
29
+ }>
30
+ actionButtons?: Array<{
31
+ text: string
32
+ icon?: string
33
+ type?: 'primary' | 'default' | 'dashed' | 'link' | 'text'
34
+ onClick?: string
35
+ }>
36
+ columns?: any[]
37
+ batchOperations?: Array<{
38
+ text: string
39
+ value: string
40
+ action?: string
41
+ }>
42
+
43
+ onRowClick?: (record: PmsProduct) => void
44
+ onSearch?: (keyword: string) => void
45
+ onPageChange?: (page: number, pageSize: number) => void
46
+ onActionClick?: (action: string) => void
47
+ onBatchOperation?: (operation: string, selectedRows: PmsProduct[]) => void
48
+
49
+ style?: React.CSSProperties
50
+ className?: string
51
+ }
52
+
53
+ const ProductList: React.FC<ProductListProps> = ({
54
+ dataSourceType = 'mock',
55
+ api,
56
+ method = 'GET',
57
+ mockData,
58
+ variableName,
59
+ dataSource,
60
+ showFilter = true,
61
+ showAction = true,
62
+ showSelection = true,
63
+ showOperation = true,
64
+ showStatus = true,
65
+ showPagination = true,
66
+ filterFields = [],
67
+ actionButtons = [],
68
+ columns,
69
+ batchOperations = [],
70
+ onRowClick,
71
+ onSearch,
72
+ onPageChange,
73
+ onActionClick,
74
+ onBatchOperation,
75
+ style,
76
+ className,
77
+ }) => {
78
+ const [loading, setLoading] = React.useState(false)
79
+ const [data, setData] = React.useState<PmsProduct[]>([])
80
+ const [total, setTotal] = React.useState(0)
81
+ const [currentPage, setCurrentPage] = React.useState(1)
82
+ const [pageSize, setPageSize] = React.useState(10)
83
+ const [searchText, setSearchText] = React.useState('')
84
+ const [selectedRowKeys, setSelectedRowKeys] = React.useState<React.Key[]>([])
85
+ const [selectedRows, setSelectedRows] = React.useState<PmsProduct[]>([])
86
+ const [batchOperation, setBatchOperation] = React.useState<string>()
87
+
88
+ const defaultMockDataObj = {
89
+ code: 200,
90
+ message: 'success',
91
+ data: {
92
+ pageNum: 1,
93
+ pageSize: 10,
94
+ total: 50,
95
+ list: [
96
+ {
97
+ id: 1,
98
+ name: '时尚运动鞋',
99
+ productSn: 'PRODUCT001',
100
+ price: 269,
101
+ stock: 100,
102
+ sale: 120,
103
+ brandName: '时尚运动',
104
+ productCategoryName: '鞋子',
105
+ pic: 'https://img.yzcdn.cn/vant/cat.jpeg',
106
+ publishStatus: 1,
107
+ newStatus: 1,
108
+ recommandStatus: 1,
109
+ verifyStatus: 1,
110
+ },
111
+ {
112
+ id: 2,
113
+ name: '休闲T恤',
114
+ productSn: 'PRODUCT002',
115
+ price: 99,
116
+ stock: 200,
117
+ sale: 350,
118
+ brandName: '休闲服饰',
119
+ productCategoryName: '衣服',
120
+ pic: 'https://img.yzcdn.cn/vant/cat.jpeg',
121
+ publishStatus: 1,
122
+ newStatus: 0,
123
+ recommandStatus: 1,
124
+ verifyStatus: 1,
125
+ },
126
+ {
127
+ id: 3,
128
+ name: '双肩背包',
129
+ productSn: 'PRODUCT003',
130
+ price: 189,
131
+ stock: 80,
132
+ sale: 80,
133
+ brandName: '旅行箱包',
134
+ productCategoryName: '配饰',
135
+ pic: 'https://img.yzcdn.cn/vant/cat.jpeg',
136
+ publishStatus: 1,
137
+ newStatus: 1,
138
+ recommandStatus: 0,
139
+ verifyStatus: 1,
140
+ },
141
+ {
142
+ id: 4,
143
+ name: '运动手表',
144
+ productSn: 'PRODUCT004',
145
+ price: 499,
146
+ stock: 50,
147
+ sale: 60,
148
+ brandName: '智能数码',
149
+ productCategoryName: '数码',
150
+ pic: 'https://img.yzcdn.cn/vant/cat.jpeg',
151
+ publishStatus: 1,
152
+ newStatus: 1,
153
+ recommandStatus: 1,
154
+ verifyStatus: 1,
155
+ },
156
+ {
157
+ id: 5,
158
+ name: '牛仔裤',
159
+ productSn: 'PRODUCT005',
160
+ price: 199,
161
+ stock: 150,
162
+ sale: 280,
163
+ brandName: '时尚牛仔',
164
+ productCategoryName: '衣服',
165
+ pic: 'https://img.yzcdn.cn/vant/cat.jpeg',
166
+ publishStatus: 1,
167
+ newStatus: 0,
168
+ recommandStatus: 0,
169
+ verifyStatus: 1,
170
+ },
171
+ ],
172
+ },
173
+ }
174
+
175
+ const defaultMockData = JSON.stringify(defaultMockDataObj)
176
+
177
+ const dataSourceConfig: DataSourceConfig = useMemo(() => {
178
+ let actualMockData = mockData
179
+ if (!actualMockData) {
180
+ actualMockData = defaultMockData
181
+ }
182
+
183
+ console.log('[ProductList] dataSourceConfig 重新计算:', {
184
+ dataSourceType,
185
+ mockData: typeof mockData === 'string' ? mockData.substring(0, 100) + '...' : mockData,
186
+ variableName,
187
+ dataSource: typeof dataSource === 'object' ? 'object' : dataSource
188
+ })
189
+
190
+ return {
191
+ type: dataSourceType,
192
+ api: api || '',
193
+ method: method,
194
+ mockData: actualMockData,
195
+ variableName: variableName || '',
196
+ dataSource: dataSource,
197
+ }
198
+ }, [dataSourceType, api, method, mockData, variableName, dataSource])
199
+
200
+ console.log('[ProductList] 组件渲染,props:', {
201
+ dataSourceType, api, method,
202
+ mockData: typeof mockData === 'string' ? mockData.substring(0, 100) + '...' : mockData,
203
+ variableName,
204
+ dataSource: typeof dataSource === 'object' ? 'object' : dataSource,
205
+ showFilter, showAction, showSelection
206
+ })
207
+
208
+ const adapter = useMemo(() => {
209
+ console.log('[ProductList] adapter 重新创建, dataSourceType:', dataSourceType)
210
+ return DataSourceAdapterFactory.create(dataSourceConfig)
211
+ }, [dataSourceConfig])
212
+
213
+ const fetchData = useCallback(async () => {
214
+ console.log('[ProductList] fetchData 开始,参数:', { currentPage, pageSize, searchText })
215
+ setLoading(true)
216
+ try {
217
+ const params: ProductQueryParam = {
218
+ pageNum: currentPage,
219
+ pageSize: pageSize,
220
+ keyword: searchText || undefined,
221
+ }
222
+
223
+ console.log('[ProductList] 调用 adapter.fetch,参数:', params)
224
+ const response = await adapter.fetch(params)
225
+ console.log('[ProductList] adapter.fetch 返回结果:', response)
226
+
227
+ if (response.code === 200) {
228
+ setData(response.data.list)
229
+ setTotal(response.data.total)
230
+ console.log('[ProductList] 数据更新成功,总数:', response.data.total)
231
+ }
232
+ } catch (error) {
233
+ console.error('[ProductList] 获取数据失败:', error)
234
+ } finally {
235
+ setLoading(false)
236
+ }
237
+ }, [currentPage, pageSize, searchText, adapter])
238
+
239
+ useEffect(() => {
240
+ console.log('[ProductList] useEffect 触发,开始获取数据, dataSourceType:', dataSourceType)
241
+ fetchData()
242
+ }, [fetchData, dataSourceType])
243
+
244
+ const handleSearch = () => {
245
+ setCurrentPage(1)
246
+ fetchData()
247
+ onSearch?.(searchText)
248
+ }
249
+
250
+ const handlePageChange = (page: number, size: number) => {
251
+ setCurrentPage(page)
252
+ setPageSize(size)
253
+ onPageChange?.(page, size)
254
+ }
255
+
256
+ const handleBatchOperation = () => {
257
+ if (batchOperation && selectedRows.length > 0) {
258
+ onBatchOperation?.(batchOperation, selectedRows)
259
+ }
260
+ }
261
+
262
+ const defaultColumns = [
263
+ {
264
+ title: '商品图片',
265
+ dataIndex: 'pic',
266
+ key: 'pic',
267
+ width: 100,
268
+ render: (text: string) => <img src={text} alt="商品图片" style={{ width: 60, height: 60, objectFit: 'cover' }} />,
269
+ },
270
+ {
271
+ title: '商品名称',
272
+ dataIndex: 'name',
273
+ key: 'name',
274
+ width: 200,
275
+ },
276
+ {
277
+ title: '商品编号',
278
+ dataIndex: 'productSn',
279
+ key: 'productSn',
280
+ width: 150,
281
+ },
282
+ {
283
+ title: '价格',
284
+ dataIndex: 'price',
285
+ key: 'price',
286
+ width: 100,
287
+ render: (price: number) => `¥${price.toFixed(2)}`,
288
+ },
289
+ {
290
+ title: '库存',
291
+ dataIndex: 'stock',
292
+ key: 'stock',
293
+ width: 80,
294
+ },
295
+ {
296
+ title: '销量',
297
+ dataIndex: 'sale',
298
+ key: 'sale',
299
+ width: 80,
300
+ },
301
+ {
302
+ title: '品牌',
303
+ dataIndex: 'brandName',
304
+ key: 'brandName',
305
+ width: 100,
306
+ },
307
+ {
308
+ title: '分类',
309
+ dataIndex: 'productCategoryName',
310
+ key: 'productCategoryName',
311
+ width: 100,
312
+ },
313
+ ...(showStatus
314
+ ? [
315
+ {
316
+ title: '状态',
317
+ dataIndex: 'publishStatus',
318
+ key: 'publishStatus',
319
+ width: 100,
320
+ render: (status: number) => (
321
+ <Switch checked={status === 1} checkedChildren="上架" unCheckedChildren="下架" />
322
+ ),
323
+ },
324
+ ]
325
+ : []),
326
+ ...(showOperation
327
+ ? [
328
+ {
329
+ title: '操作',
330
+ key: 'operation',
331
+ width: 150,
332
+ render: (_: any, record: PmsProduct) => (
333
+ <Space size="small">
334
+ <Button type="link" size="small" onClick={() => onRowClick?.(record)}>
335
+ 查看
336
+ </Button>
337
+ <Button type="link" size="small">
338
+ 编辑
339
+ </Button>
340
+ <Button type="link" size="small" danger>
341
+ 删除
342
+ </Button>
343
+ </Space>
344
+ ),
345
+ },
346
+ ]
347
+ : []),
348
+ ]
349
+
350
+ const rowSelection = showSelection
351
+ ? {
352
+ selectedRowKeys,
353
+ onChange: (newSelectedRowKeys: React.Key[], newSelectedRows: PmsProduct[]) => {
354
+ setSelectedRowKeys(newSelectedRowKeys)
355
+ setSelectedRows(newSelectedRows)
356
+ },
357
+ }
358
+ : undefined
359
+
360
+ const getIcon = (iconName?: string): React.ReactNode => {
361
+ switch (iconName) {
362
+ case 'plus':
363
+ return <PlusOutlined />
364
+ case 'download':
365
+ return <DownloadOutlined />
366
+ case 'upload':
367
+ return <UploadOutlined />
368
+ default:
369
+ return null
370
+ }
371
+ }
372
+
373
+ return (
374
+ <div className={`mall-product-list ${className || ''}`} style={style}>
375
+ {showFilter && (
376
+ <Card className="mall-product-list-filter" size="small">
377
+ <Space>
378
+ <Input
379
+ placeholder="请输入商品名称或编号"
380
+ prefix={<SearchOutlined />}
381
+ value={searchText}
382
+ onChange={(e) => setSearchText(e.target.value)}
383
+ onPressEnter={handleSearch}
384
+ style={{ width: 300 }}
385
+ />
386
+ <Button type="primary" icon={<SearchOutlined />} onClick={handleSearch}>
387
+ 搜索
388
+ </Button>
389
+ <Button icon={<ReloadOutlined />} onClick={() => fetchData()}>
390
+ 刷新
391
+ </Button>
392
+ </Space>
393
+ </Card>
394
+ )}
395
+
396
+ {showAction && (
397
+ <Card className="mall-product-list-action" size="small">
398
+ <Space>
399
+ {actionButtons.map((btn, index) => (
400
+ <Button key={index} type={btn.type || 'default'} icon={getIcon(btn.icon)} onClick={() => onActionClick?.(btn.onClick || '')}>
401
+ {btn.text}
402
+ </Button>
403
+ ))}
404
+ {showSelection && batchOperations.length > 0 && (
405
+ <>
406
+ <Select
407
+ placeholder="批量操作"
408
+ value={batchOperation}
409
+ onChange={setBatchOperation}
410
+ style={{ width: 150 }}
411
+ >
412
+ {batchOperations.map((op, index) => (
413
+ <Select.Option key={index} value={op.value}>
414
+ {op.text}
415
+ </Select.Option>
416
+ ))}
417
+ </Select>
418
+ <Button onClick={handleBatchOperation}>执行</Button>
419
+ </>
420
+ )}
421
+ </Space>
422
+ </Card>
423
+ )}
424
+
425
+ <div className="mall-product-list-table">
426
+ <Table
427
+ rowKey="id"
428
+ columns={columns || defaultColumns}
429
+ dataSource={data}
430
+ loading={loading}
431
+ rowSelection={rowSelection}
432
+ pagination={false}
433
+ scroll={{ x: 1200 }}
434
+ />
435
+ </div>
436
+
437
+ {showPagination && (
438
+ <div className="pagination-wrapper">
439
+ <Pagination
440
+ current={currentPage}
441
+ pageSize={pageSize}
442
+ total={total}
443
+ showSizeChanger
444
+ showQuickJumper
445
+ showTotal={(total) => `共 ${total} 条`}
446
+ onChange={handlePageChange}
447
+ />
448
+ </div>
449
+ )}
450
+ </div>
451
+ )
452
+ }
453
+
454
+ export default ProductList
@@ -0,0 +1,3 @@
1
+ import ProductList from './ProductList'
2
+ export { ProductList }
3
+ export default ProductList
@@ -0,0 +1,71 @@
1
+ .mall-promotion-card {
2
+ .statistics-section {
3
+ padding: 16px;
4
+ background: linear-gradient(135deg, #fff7e6 0%, #fffbe6 100%);
5
+ border-radius: 4px;
6
+ margin-bottom: 16px;
7
+
8
+ .ant-statistic-title {
9
+ font-size: 13px;
10
+ color: #8c8c8c;
11
+ }
12
+
13
+ .ant-statistic-content {
14
+ font-size: 28px;
15
+ }
16
+ }
17
+
18
+ .content-section {
19
+ display: flex;
20
+ gap: 16px;
21
+
22
+ .table-section {
23
+ flex: 1;
24
+ }
25
+
26
+ .timeline-section {
27
+ width: 300px;
28
+
29
+ .ant-card {
30
+ height: 100%;
31
+ }
32
+
33
+ .ant-timeline {
34
+ padding-top: 8px;
35
+ }
36
+
37
+ .ant-timeline-item-content {
38
+ font-size: 13px;
39
+ }
40
+ }
41
+ }
42
+
43
+ .filter-section {
44
+ margin-bottom: 16px;
45
+ }
46
+
47
+ .action-section {
48
+ margin-bottom: 16px;
49
+ }
50
+
51
+ .promotion-title {
52
+ font-weight: 500;
53
+ color: #262626;
54
+ }
55
+
56
+ .ant-table {
57
+ .ant-tag {
58
+ margin: 0;
59
+ }
60
+ }
61
+
62
+ @media (max-width: 1200px) {
63
+ .content-section {
64
+ flex-direction: column;
65
+
66
+ .timeline-section {
67
+ width: 100%;
68
+ }
69
+ }
70
+ }
71
+ }