@appthen/cli 1.2.11 → 1.2.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 (59) hide show
  1. package/bin/main.js +47 -0
  2. package/dist/index.js +6185 -15001
  3. package/package.json +8 -4
  4. package/tests/test-app/.appthen/shadow-space-100001-test-app-e99876b1.json +1197 -741
  5. package/tests/test-app/.appthen/space-config.json +2 -2
  6. package/tests/test-app/src/components/MessageCenter.tsx +506 -0
  7. package/tests/test-app/src/pages/CustomerManagement.tsx +535 -0
  8. package/tests/test-app/src/pages/CyberpunkDashboard.tsx +348 -0
  9. package/tests/test-app/src/pages/CyberpunkProductManagement.tsx +637 -0
  10. package/tests/test-app/src/pages/CyberpunkUserList.tsx +316 -0
  11. package/tests/test-app/src/pages/DashboardV2.tsx +334 -0
  12. package/tests/test-app/src/pages/DataReport.tsx +298 -0
  13. package/tests/test-app/src/pages/DataStatistics.tsx +317 -0
  14. package/tests/test-app/src/pages/DepartmentManagement.tsx +503 -0
  15. package/tests/test-app/src/pages/FileExplorer.tsx +441 -0
  16. package/tests/test-app/src/pages/OrderDetail.tsx +393 -0
  17. package/tests/test-app/src/pages/ProductManagement.tsx +521 -0
  18. package/tests/test-app/src/pages/ProjectTimeline.tsx +395 -0
  19. package/tests/test-app/src/pages/RoleManagement.tsx +523 -0
  20. package/tests/test-app/src/pages/StaticCyberpunkDashboard.tsx +462 -0
  21. package/tests/test-app/src/pages/StaticCyberpunkUserList.tsx +567 -0
  22. package/tests/test-app/src/pages/StudentWeaknessList.tsx +547 -0
  23. package/tests/test-app/src/pages/SystemSettings.tsx +422 -0
  24. package/tests/test-app/src/pages/TaskManagement.tsx +467 -0
  25. package/tests/test-app/src/pages/TicketManagement.tsx +402 -0
  26. package/tests/test-app/src/pages/UserProfile.tsx +404 -0
  27. package/tests/test-app/src/pages/WorkflowDesigner.tsx +434 -0
  28. package/tests/test-app/src/pages/admin/dashboard.tsx +591 -0
  29. package/tests/test-app/src/pages/article-list.tsx +222 -0
  30. package/tests/test-app/src/pages/babyProductRecommendationPage.tsx +168 -0
  31. package/tests/test-app/src/pages/category-list.tsx +179 -0
  32. package/tests/test-app/src/pages/comment-list.tsx +194 -0
  33. package/tests/test-app/src/pages/cyberpunk/cyberpunkCRMPage.tsx +1299 -0
  34. package/tests/test-app/src/pages/data-analytics.tsx +1872 -0
  35. package/tests/test-app/src/pages/data-overview.tsx +600 -0
  36. package/tests/test-app/src/pages/demo-error-page.tsx +119 -0
  37. package/tests/test-app/src/pages/department-list.tsx +183 -0
  38. package/tests/test-app/src/pages/goods-list.tsx +233 -0
  39. package/tests/test-app/src/pages/housekeeping/adminDashboardPage.tsx +880 -0
  40. package/tests/test-app/src/pages/mobile_terminal/uiHandsOnPractice.tsx +1 -1
  41. package/tests/test-app/src/pages/notice-list.tsx +217 -0
  42. package/tests/test-app/src/pages/order-detail.tsx +330 -0
  43. package/tests/test-app/src/pages/order-list.tsx +195 -0
  44. package/tests/test-app/src/pages/order-management.tsx +563 -0
  45. package/tests/test-app/src/pages/page/OrderList.tsx +230 -0
  46. package/tests/test-app/src/pages/role-list.tsx +184 -0
  47. package/tests/test-app/src/pages/simple/simplePage.tsx +92 -0
  48. package/tests/test-app/src/pages/simple-page.tsx +43 -0
  49. package/tests/test-app/src/pages/test-destructure.tsx +44 -0
  50. package/tests/test-app/src/pages/test-error-page.tsx +75 -0
  51. package/tests/test-app/src/pages/test-page-with-errors.tsx +51 -0
  52. package/tests/test-app/src/pages/test-page.tsx +101 -0
  53. package/tests/test-app/src/pages/test-render.tsx +52 -0
  54. package/tests/test-app/src/pages/test-return-type.tsx +41 -0
  55. package/tests/test-app/src/pages/test-type-assertion.tsx +37 -0
  56. package/tests/test-app/src/pages/ui/styleSelectorPage.tsx +1554 -0
  57. package/tests/test-app/src/pages/user-list.tsx +212 -0
  58. package/tests/test-app/src/pages/wrong-page.tsx +50 -0
  59. package/tests/test-app/.appthen/shadow-space-unknown-user-test-app-e99876b1.json +0 -1060
@@ -0,0 +1,535 @@
1
+ /**
2
+ * 客户管理
3
+ * 客户信息管理和跟进
4
+ *
5
+ * @type Page
6
+ * @route /customers
7
+ * @screen 1920w
8
+ * @frames web
9
+ */
10
+ import React from 'react';
11
+
12
+
13
+
14
+ class IProps {
15
+ customerId?: number;
16
+ }
17
+
18
+ /*
19
+ * 数据与接口请求定义
20
+ */
21
+ class IState {
22
+ customers?: {
23
+ /* @example 1 */id?: number,
24
+ /* @example 张三 */name?: string,
25
+ /* @example ABC公司 */company?: string,
26
+ /* @example 13800138000 */phone?: string,
27
+ /* @example zhangsan@abc.com */email?: string,
28
+ /* @example active */status?: string,
29
+ /* @example VIP */level?: string,
30
+ /* @example 25 */totalOrders?: number,
31
+ /* @example 125000 */totalAmount?: number,
32
+ /* @example 2024-01-15 */lastContact?: string,
33
+ }[];
34
+ loading?: boolean;
35
+ selectedCustomer?: any;
36
+ showEditModal?: boolean;
37
+ searchKeyword?: string;
38
+ statusFilter?: string;
39
+ formData?: any;
40
+ }
41
+
42
+ class Document extends React.Component<IProps, IState> {
43
+ state = {
44
+ customers: [
45
+ {
46
+ id: 1,
47
+ name: '张三',
48
+ company: 'ABC公司',
49
+ phone: '13800138000',
50
+ email: 'zhangsan@abc.com',
51
+ status: 'active',
52
+ level: 'VIP',
53
+ totalOrders: 25,
54
+ totalAmount: 125000,
55
+ lastContact: '2024-01-15',
56
+ },
57
+ {
58
+ id: 2,
59
+ name: '李四',
60
+ company: 'XYZ公司',
61
+ phone: '13900139000',
62
+ email: 'lisi@xyz.com',
63
+ status: 'active',
64
+ level: '普通',
65
+ totalOrders: 10,
66
+ totalAmount: 32000,
67
+ lastContact: '2024-01-14',
68
+ },
69
+ {
70
+ id: 3,
71
+ name: '王五',
72
+ company: 'DEF公司',
73
+ phone: '13700137000',
74
+ email: 'wangwu@def.com',
75
+ status: 'inactive',
76
+ level: 'VIP',
77
+ totalOrders: 50,
78
+ totalAmount: 250000,
79
+ lastContact: '2024-01-10',
80
+ },
81
+ {
82
+ id: 4,
83
+ name: '赵六',
84
+ company: 'GHI公司',
85
+ phone: '13600136000',
86
+ email: 'zhaoliu@ghi.com',
87
+ status: 'active',
88
+ level: '普通',
89
+ totalOrders: 8,
90
+ totalAmount: 18000,
91
+ lastContact: '2024-01-13',
92
+ },
93
+ {
94
+ id: 5,
95
+ name: '钱七',
96
+ company: 'JKL公司',
97
+ phone: '13500135000',
98
+ email: 'qianqi@jkl.com',
99
+ status: 'active',
100
+ level: 'VIP',
101
+ totalOrders: 30,
102
+ totalAmount: 180000,
103
+ lastContact: '2024-01-12',
104
+ },
105
+ ],
106
+ loading: false,
107
+ selectedCustomer: null,
108
+ showEditModal: false,
109
+ searchKeyword: '',
110
+ statusFilter: 'all',
111
+ formData: {},
112
+ };
113
+
114
+ handleSearch(keyword) {
115
+ this.setState({
116
+ searchKeyword: keyword,
117
+ });
118
+ }
119
+
120
+ handleStatusFilter(status) {
121
+ this.setState({
122
+ statusFilter: status,
123
+ });
124
+ }
125
+
126
+ getCustomerById(id) {
127
+ return this.state.customers.find(customer => customer.id === id);
128
+ }
129
+
130
+ getActiveCustomers() {
131
+ return this.state.customers.filter(
132
+ customer => customer.status === 'active'
133
+ );
134
+ }
135
+
136
+ getVipCustomers() {
137
+ return this.state.customers.filter(customer => customer.level === 'VIP');
138
+ }
139
+
140
+ handleEdit(customer) {
141
+ this.setState({
142
+ selectedCustomer: customer,
143
+ formData: {
144
+ ...customer,
145
+ },
146
+ showEditModal: true,
147
+ });
148
+ }
149
+
150
+ handleSave(id, data) {
151
+ console.log('保存客户:', id, data);
152
+ this.setState({
153
+ customers: this.state.customers.map(c => {
154
+ if (c.id === id) {
155
+ return {
156
+ ...c,
157
+ ...data,
158
+ };
159
+ }
160
+ return c;
161
+ }),
162
+ showEditModal: false,
163
+ });
164
+ }
165
+
166
+ handleDelete(id) {
167
+ this.setState({
168
+ customers: this.state.customers.filter(c => c.id !== id),
169
+ });
170
+ }
171
+
172
+ handleContact(customer) {
173
+ console.log('联系客户:', customer);
174
+ }
175
+
176
+ exportCustomers(format) {
177
+ console.log('导出客户:', format, this.state.customers);
178
+ }
179
+
180
+ calculateCustomerValue(customer) {
181
+ return customer.totalAmount / Math.max(customer.totalOrders, 1);
182
+ }
183
+
184
+ renderStatus(status) {
185
+ const config = {
186
+ active: {
187
+ color: 'green',
188
+ label: '活跃',
189
+ },
190
+ inactive: {
191
+ color: 'gray',
192
+ label: '非活跃',
193
+ },
194
+ };
195
+ return (
196
+ <Tag color={config[status]?.color || 'default'}>
197
+ {config[status]?.label || status}
198
+ </Tag>
199
+ );
200
+ }
201
+
202
+ renderLevel(level) {
203
+ const config = {
204
+ VIP: {
205
+ color: 'gold',
206
+ label: 'VIP',
207
+ },
208
+ 普通: {
209
+ color: 'blue',
210
+ label: '普通',
211
+ },
212
+ };
213
+ return (
214
+ <Tag color={config[level]?.color || 'default'}>
215
+ {config[level]?.label || level}
216
+ </Tag>
217
+ );
218
+ }
219
+
220
+ refreshData() {
221
+ this.setState({
222
+ loading: true,
223
+ });
224
+ setTimeout(() => {
225
+ this.setState({
226
+ loading: false,
227
+ });
228
+ }, 1000);
229
+ }
230
+
231
+ render() {
232
+ const customers = this.state.customers;
233
+ const loading = this.state.loading;
234
+ const selectedCustomer = this.state.selectedCustomer;
235
+ const showEditModal = this.state.showEditModal;
236
+ const searchKeyword = this.state.searchKeyword;
237
+ const statusFilter = this.state.statusFilter;
238
+ const formData = this.state.formData;
239
+ const filteredCustomers = customers.filter(customer => {
240
+ const matchSearch =
241
+ customer.name.includes(searchKeyword) ||
242
+ customer.company.includes(searchKeyword);
243
+ const matchStatus =
244
+ statusFilter === 'all' || customer.status === statusFilter;
245
+ return matchSearch && matchStatus;
246
+ });
247
+ const columns = [
248
+ {
249
+ title: '客户姓名',
250
+ dataIndex: 'name',
251
+ key: 'name',
252
+ render: text => <Text className="font-bold">{text}</Text>,
253
+ },
254
+ {
255
+ title: '公司',
256
+ dataIndex: 'company',
257
+ key: 'company',
258
+ },
259
+ {
260
+ title: '联系方式',
261
+ key: 'contact',
262
+ render: record => (
263
+ <View>
264
+ <Text block>{record.phone}</Text>
265
+ <Text className="text-gray-500 text-sm">{record.email}</Text>
266
+ </View>
267
+ ),
268
+ },
269
+ {
270
+ title: '状态',
271
+ dataIndex: 'status',
272
+ key: 'status',
273
+ render: status => this.renderStatus(status),
274
+ },
275
+ {
276
+ title: '等级',
277
+ dataIndex: 'level',
278
+ key: 'level',
279
+ render: level => this.renderLevel(level),
280
+ },
281
+ {
282
+ title: '订单数',
283
+ dataIndex: 'totalOrders',
284
+ key: 'totalOrders',
285
+ render: value => <Text>{value} 单</Text>,
286
+ },
287
+ {
288
+ title: '总金额',
289
+ dataIndex: 'totalAmount',
290
+ key: 'totalAmount',
291
+ render: value => <Text>¥{value.toLocaleString()}</Text>,
292
+ },
293
+ {
294
+ title: '平均客单价',
295
+ key: 'avgValue',
296
+ render: record => (
297
+ <Text>¥{this.calculateCustomerValue(record).toFixed(2)}</Text>
298
+ ),
299
+ },
300
+ {
301
+ title: '操作',
302
+ key: 'action',
303
+ render: (text, record) => (
304
+ <View>
305
+ <Button type="link" onClick={() => this.handleEdit(record)}>
306
+ 编辑
307
+ </Button>
308
+ <Button type="link" onClick={() => this.handleContact(record)}>
309
+ 联系
310
+ </Button>
311
+ <Button
312
+ type="link"
313
+ danger
314
+ onClick={() => this.handleDelete(record.id)}
315
+ >
316
+ 删除
317
+ </Button>
318
+ </View>
319
+ ),
320
+ },
321
+ ];
322
+ if (loading) {
323
+ return <View className="p-6">加载中...</View>;
324
+ }
325
+ return (
326
+ <Page className="p-[24px] bg-[var(--gray-50)] min-h-screen">
327
+ <View className="mb-[24px]">
328
+ <Text className="text-2xl font-bold text-[#1f2937]">客户管理</Text>
329
+ <Text className="text-[#6b7280] text-sm mt-[4px]">
330
+ 客户信息管理和跟进
331
+ </Text>
332
+ </View>
333
+ <Card className="mb-[24px]">
334
+ <View className="gap-4 grid grid-cols-4">
335
+ <View className="text-[var(--center)] p-[16px] bg-[var(--blue-50)] rounded">
336
+ <Text className="text-3xl font-bold text-[var(--blue-600)]">
337
+ {customers.length}
338
+ </Text>
339
+ <Text className="block text-sm text-[#4b5563] mt-[4px]">
340
+ 总客户数
341
+ </Text>
342
+ </View>
343
+ <View className="text-[var(--center)] p-[16px] bg-[var(--green-50)] rounded">
344
+ <Text className="text-3xl font-bold text-[var(--green-600)]">
345
+ {this.getActiveCustomers().length}
346
+ </Text>
347
+ <Text className="block text-sm text-[#4b5563] mt-[4px]">
348
+ 活跃客户
349
+ </Text>
350
+ </View>
351
+ <View className="text-[var(--center)] p-[16px] bg-[var(--orange-50)] rounded">
352
+ <Text className="text-3xl font-bold text-[var(--orange-600)]">
353
+ {this.getVipCustomers().length}
354
+ </Text>
355
+ <Text className="block text-sm text-[#4b5563] mt-[4px]">
356
+ VIP客户
357
+ </Text>
358
+ </View>
359
+ <View className="text-[var(--center)] p-[16px] bg-[var(--purple-50)] rounded">
360
+ <Text className="text-3xl font-bold text-[var(--purple-600)]">
361
+ {customers
362
+ .reduce((sum, c) => sum + c.totalAmount, 0)
363
+ .toLocaleString()}
364
+ </Text>
365
+ <Text className="block text-sm text-[#4b5563] mt-[4px]">
366
+ 总消费金额
367
+ </Text>
368
+ </View>
369
+ </View>
370
+ </Card>
371
+ <Card className="mb-[24px]">
372
+ <View className="flex items-center justify-between">
373
+ <View className="gap-4 flex items-center">
374
+ <Input
375
+ placeholder="搜索客户"
376
+ value={searchKeyword}
377
+ onChange={e => this.handleSearch(e.target.value)}
378
+ className=""
379
+ />
380
+ <Select
381
+ value={statusFilter}
382
+ onChange={value => this.handleStatusFilter(value)}
383
+ className=""
384
+ >
385
+ <Select.Option value="all">全部状态</Select.Option>
386
+ <Select.Option value="active">活跃</Select.Option>
387
+ <Select.Option value="inactive">非活跃</Select.Option>
388
+ </Select>
389
+ </View>
390
+ <View className="gap-2 flex">
391
+ <Button onClick={() => this.refreshData()}>刷新</Button>
392
+ <Button type="primary">+ 新增客户</Button>
393
+ </View>
394
+ </View>
395
+ </Card>
396
+ <Card>
397
+ <Table
398
+ columns={columns}
399
+ dataSource={filteredCustomers}
400
+ rowKey="id"
401
+ pagination={{
402
+ pageSize: 10,
403
+ showTotal: function (total) {
404
+ return `共 ${total} 条`;
405
+ },
406
+ }}
407
+ />
408
+ </Card>
409
+ <Modal
410
+ animate="pop"
411
+ visible={showEditModal}
412
+ renderView={() => (
413
+ <View className="bg-[#ffffff] w-[600px] h-[500px] p-[24px]">
414
+ <Text className="text-xl font-bold mb-[16px]">编辑客户</Text>
415
+ <View className="space-y-4">
416
+ <View>
417
+ <Text className="block mb-[8px]">姓名</Text>
418
+ <Input
419
+ value={formData?.name}
420
+ onChange={e =>
421
+ this.setState({
422
+ formData: {
423
+ ...formData,
424
+ name: e.target.value,
425
+ },
426
+ })
427
+ }
428
+ />
429
+ </View>
430
+ <View>
431
+ <Text className="block mb-[8px]">公司</Text>
432
+ <Input
433
+ value={formData?.company}
434
+ onChange={e =>
435
+ this.setState({
436
+ formData: {
437
+ ...formData,
438
+ company: e.target.value,
439
+ },
440
+ })
441
+ }
442
+ />
443
+ </View>
444
+ <View>
445
+ <Text className="block mb-[8px]">手机号</Text>
446
+ <Input
447
+ value={formData?.phone}
448
+ onChange={e =>
449
+ this.setState({
450
+ formData: {
451
+ ...formData,
452
+ phone: e.target.value,
453
+ },
454
+ })
455
+ }
456
+ />
457
+ </View>
458
+ <View>
459
+ <Text className="block mb-[8px]">邮箱</Text>
460
+ <Input
461
+ value={formData?.email}
462
+ onChange={e =>
463
+ this.setState({
464
+ formData: {
465
+ ...formData,
466
+ email: e.target.value,
467
+ },
468
+ })
469
+ }
470
+ />
471
+ </View>
472
+ <View>
473
+ <Text className="block mb-[8px]">状态</Text>
474
+ <Select
475
+ value={formData?.status}
476
+ onChange={value =>
477
+ this.setState({
478
+ formData: {
479
+ ...formData,
480
+ status: value,
481
+ },
482
+ })
483
+ }
484
+ className="w-full"
485
+ >
486
+ <Select.Option value="active">活跃</Select.Option>
487
+ <Select.Option value="inactive">非活跃</Select.Option>
488
+ </Select>
489
+ </View>
490
+ <View>
491
+ <Text className="block mb-[8px]">等级</Text>
492
+ <Select
493
+ value={formData?.level}
494
+ onChange={value =>
495
+ this.setState({
496
+ formData: {
497
+ ...formData,
498
+ level: value,
499
+ },
500
+ })
501
+ }
502
+ className="w-full"
503
+ >
504
+ <Select.Option value="VIP">VIP</Select.Option>
505
+ <Select.Option value="普通">普通</Select.Option>
506
+ </Select>
507
+ </View>
508
+ <View className="gap-2 flex justify-end mt-[24px]">
509
+ <Button
510
+ onClick={() =>
511
+ this.setState({
512
+ showEditModal: false,
513
+ })
514
+ }
515
+ >
516
+ 取消
517
+ </Button>
518
+ <Button
519
+ type="primary"
520
+ onClick={() => this.handleSave(formData?.id || 0, formData)}
521
+ >
522
+ 保存
523
+ </Button>
524
+ </View>
525
+ </View>
526
+ </View>
527
+ )}
528
+ maskClosable={true}
529
+ />
530
+ </Page>
531
+ );
532
+ }
533
+ }
534
+
535
+ export default Document;