whistle.mock-plugins 1.1.3-dev.9 → 1.2.0-beta.10
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/VERSION.md +14 -0
- package/app/public/js/index.js +1 -1
- package/app/public/js/index.js.LICENSE.txt +4 -2
- package/package.json +1 -1
- package/ui/src/components/AppLayout.js +5 -3
- package/ui/src/pages/InterfaceManagement.js +53 -13
- package/ui/src/pages/InterfaceManagementV2.js +231 -186
- package/ui/src/pages/MockDataV2.js +256 -100
- package/ui/src/styles/app-layout.css +38 -2
- package/ui/src/styles/interface-management.css +156 -220
- package/ui/src/styles/mock-data.css +399 -22
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import React, { useState, useEffect } from 'react';
|
|
7
7
|
import { useHistory } from 'react-router-dom';
|
|
8
8
|
import AppLayout from '../components/AppLayout';
|
|
9
|
-
import { Card, Row, Col, Button, Modal, Form, Input, message, Switch, Empty, Spin, Typography, Tooltip, Badge, Select, Pagination } from 'antd';
|
|
10
|
-
import { PlusOutlined, EditOutlined, DeleteOutlined, ApiOutlined, ExportOutlined, InfoCircleOutlined, CalendarOutlined, SortAscendingOutlined } from '@ant-design/icons';
|
|
9
|
+
import { Card, Row, Col, Button, Modal, Form, Input, message, Switch, Empty, Spin, Typography, Tooltip, Badge, Select, Pagination, Tag, List } from 'antd';
|
|
10
|
+
import { PlusOutlined, EditOutlined, DeleteOutlined, ApiOutlined, ExportOutlined, InfoCircleOutlined, CalendarOutlined, SortAscendingOutlined, AppstoreOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
|
11
11
|
import '../styles/mock-data.css';
|
|
12
12
|
import axios from 'axios';
|
|
13
13
|
|
|
@@ -34,6 +34,12 @@ const MockData = () => {
|
|
|
34
34
|
const [loading, setLoading] = useState(true);
|
|
35
35
|
const [form] = Form.useForm();
|
|
36
36
|
|
|
37
|
+
// 视图模式配置(卡片/列表)
|
|
38
|
+
const [viewMode, setViewMode] = useState(() => {
|
|
39
|
+
const savedViewMode = localStorage.getItem('feature-view-mode');
|
|
40
|
+
return savedViewMode === 'list' ? 'list' : 'card'; // 默认为卡片视图
|
|
41
|
+
});
|
|
42
|
+
|
|
37
43
|
// 排序和分页配置(支持缓存)
|
|
38
44
|
const [listConfig, setListConfig] = useState(() => {
|
|
39
45
|
const cached = localStorage.getItem('feature-list-config');
|
|
@@ -51,6 +57,13 @@ const MockData = () => {
|
|
|
51
57
|
setListConfig(newConfig);
|
|
52
58
|
localStorage.setItem('feature-list-config', JSON.stringify(newConfig));
|
|
53
59
|
};
|
|
60
|
+
|
|
61
|
+
// 切换视图模式(卡片/列表)
|
|
62
|
+
const toggleViewMode = () => {
|
|
63
|
+
const newMode = viewMode === 'card' ? 'list' : 'card';
|
|
64
|
+
setViewMode(newMode);
|
|
65
|
+
localStorage.setItem('feature-view-mode', newMode);
|
|
66
|
+
};
|
|
54
67
|
|
|
55
68
|
// 加载功能列表
|
|
56
69
|
useEffect(() => {
|
|
@@ -413,6 +426,98 @@ const MockData = () => {
|
|
|
413
426
|
return sortedFeatures.slice(startIndex, endIndex);
|
|
414
427
|
};
|
|
415
428
|
|
|
429
|
+
// 渲染功能模块列表项
|
|
430
|
+
const renderFeatureListItem = (feature) => {
|
|
431
|
+
const formattedDate = feature.createdAt
|
|
432
|
+
? new Date(feature.createdAt).toLocaleDateString()
|
|
433
|
+
: '未知日期';
|
|
434
|
+
|
|
435
|
+
return (
|
|
436
|
+
<div
|
|
437
|
+
key={feature.id}
|
|
438
|
+
className={`feature-list-item ${!feature.active ? 'inactive-feature' : ''}`}
|
|
439
|
+
onClick={() => viewInterfaces(feature)}
|
|
440
|
+
>
|
|
441
|
+
<div className="feature-list-content">
|
|
442
|
+
<div className="feature-list-main">
|
|
443
|
+
<div className="feature-list-name">
|
|
444
|
+
<h3>{feature.name}</h3>
|
|
445
|
+
{!feature.active && <Tag color="red">已禁用</Tag>}
|
|
446
|
+
{feature.active && <Tag color="green">已启用</Tag>}
|
|
447
|
+
</div>
|
|
448
|
+
<div className="feature-list-description">
|
|
449
|
+
{feature.description || '无描述'}
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
|
|
453
|
+
<div className="feature-list-meta">
|
|
454
|
+
<div className="feature-list-stats">
|
|
455
|
+
<span><InfoCircleOutlined /> {feature.interfaceCount || 0} 个接口</span>
|
|
456
|
+
<span><CalendarOutlined /> {formattedDate}</span>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
|
|
461
|
+
<div className="feature-list-actions">
|
|
462
|
+
<Switch
|
|
463
|
+
checked={feature.active}
|
|
464
|
+
onChange={(checked, e) => {
|
|
465
|
+
e.stopPropagation();
|
|
466
|
+
handleToggleActive(feature.id, feature.active);
|
|
467
|
+
}}
|
|
468
|
+
onClick={(e) => e.stopPropagation()}
|
|
469
|
+
size="small"
|
|
470
|
+
/>
|
|
471
|
+
<Tooltip title="管理接口">
|
|
472
|
+
<Button
|
|
473
|
+
icon={<ApiOutlined />}
|
|
474
|
+
size="small"
|
|
475
|
+
type="text"
|
|
476
|
+
onClick={(e) => {
|
|
477
|
+
e.stopPropagation();
|
|
478
|
+
viewInterfaces(feature);
|
|
479
|
+
}}
|
|
480
|
+
/>
|
|
481
|
+
</Tooltip>
|
|
482
|
+
<Tooltip title="编辑功能">
|
|
483
|
+
<Button
|
|
484
|
+
icon={<EditOutlined />}
|
|
485
|
+
size="small"
|
|
486
|
+
type="text"
|
|
487
|
+
onClick={(e) => {
|
|
488
|
+
e.stopPropagation();
|
|
489
|
+
openModal(feature);
|
|
490
|
+
}}
|
|
491
|
+
/>
|
|
492
|
+
</Tooltip>
|
|
493
|
+
<Tooltip title="删除功能">
|
|
494
|
+
<Button
|
|
495
|
+
icon={<DeleteOutlined />}
|
|
496
|
+
size="small"
|
|
497
|
+
type="text"
|
|
498
|
+
danger
|
|
499
|
+
onClick={(e) => {
|
|
500
|
+
e.stopPropagation();
|
|
501
|
+
deleteFeature(feature.id);
|
|
502
|
+
}}
|
|
503
|
+
/>
|
|
504
|
+
</Tooltip>
|
|
505
|
+
<Tooltip title="导出配置">
|
|
506
|
+
<Button
|
|
507
|
+
icon={<ExportOutlined />}
|
|
508
|
+
size="small"
|
|
509
|
+
type="text"
|
|
510
|
+
onClick={(e) => {
|
|
511
|
+
e.stopPropagation();
|
|
512
|
+
exportFeatureConfig(feature);
|
|
513
|
+
}}
|
|
514
|
+
/>
|
|
515
|
+
</Tooltip>
|
|
516
|
+
</div>
|
|
517
|
+
</div>
|
|
518
|
+
);
|
|
519
|
+
};
|
|
520
|
+
|
|
416
521
|
// 渲染功能模块卡片
|
|
417
522
|
const renderFeatureCard = (feature) => {
|
|
418
523
|
const formattedDate = feature.createdAt
|
|
@@ -520,24 +625,22 @@ const MockData = () => {
|
|
|
520
625
|
return (
|
|
521
626
|
<AppLayout>
|
|
522
627
|
<div className="page-container">
|
|
523
|
-
{/* V2版本标识 */}
|
|
524
|
-
<div style={{
|
|
525
|
-
position: 'absolute',
|
|
526
|
-
top: '10px',
|
|
527
|
-
right: '10px',
|
|
528
|
-
backgroundColor: '#52c41a',
|
|
529
|
-
color: 'white',
|
|
530
|
-
padding: '4px 8px',
|
|
531
|
-
borderRadius: '4px',
|
|
532
|
-
fontSize: '12px',
|
|
533
|
-
zIndex: 1000
|
|
534
|
-
}}>
|
|
535
|
-
V2版本
|
|
536
|
-
</div>
|
|
537
|
-
|
|
538
628
|
<div className="page-title-bar">
|
|
539
|
-
<div>
|
|
540
|
-
<
|
|
629
|
+
<div className="title-section">
|
|
630
|
+
<div className="title-with-badge">
|
|
631
|
+
<h1 className="page-title">功能模块管理</h1>
|
|
632
|
+
<Badge
|
|
633
|
+
count="V2"
|
|
634
|
+
style={{
|
|
635
|
+
backgroundColor: '#52c41a',
|
|
636
|
+
fontSize: '12px',
|
|
637
|
+
height: '20px',
|
|
638
|
+
lineHeight: '20px',
|
|
639
|
+
borderRadius: '10px',
|
|
640
|
+
marginLeft: '12px'
|
|
641
|
+
}}
|
|
642
|
+
/>
|
|
643
|
+
</div>
|
|
541
644
|
<div className="page-description">
|
|
542
645
|
创建和管理功能模块,为每个功能配置独立的接口
|
|
543
646
|
</div>
|
|
@@ -561,117 +664,170 @@ const MockData = () => {
|
|
|
561
664
|
</div>
|
|
562
665
|
</div>
|
|
563
666
|
|
|
564
|
-
{/*
|
|
667
|
+
{/* 控制面板 */}
|
|
565
668
|
{mockFeatures.length > 0 && !loading && (
|
|
566
|
-
<
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
669
|
+
<Card className="control-panel-card" style={{ marginBottom: 24 }}>
|
|
670
|
+
<Row gutter={[24, 16]} align="middle">
|
|
671
|
+
{/* 统计信息区域 */}
|
|
672
|
+
<Col xs={24} sm={12} md={8} lg={6}>
|
|
673
|
+
<div className="stats-section">
|
|
674
|
+
<div className="stats-item">
|
|
675
|
+
<span className="stats-number">{mockFeatures.length}</span>
|
|
676
|
+
<span className="stats-label">功能模块</span>
|
|
677
|
+
</div>
|
|
678
|
+
<div className="stats-item">
|
|
679
|
+
<span className="stats-number">
|
|
680
|
+
{mockFeatures.filter(f => f.active).length}
|
|
681
|
+
</span>
|
|
682
|
+
<span className="stats-label">已启用</span>
|
|
683
|
+
</div>
|
|
684
|
+
<div className="stats-item">
|
|
685
|
+
<span className="stats-number">
|
|
686
|
+
{mockFeatures.reduce((sum, f) => sum + (f.interfaceCount || 0), 0)}
|
|
687
|
+
</span>
|
|
688
|
+
<span className="stats-label">接口总数</span>
|
|
689
|
+
</div>
|
|
690
|
+
</div>
|
|
691
|
+
</Col>
|
|
692
|
+
|
|
693
|
+
{/* 排序控制区域 */}
|
|
694
|
+
<Col xs={24} sm={12} md={8} lg={9}>
|
|
695
|
+
<div className="sort-controls">
|
|
696
|
+
<div className="control-group">
|
|
697
|
+
<span className="control-label">
|
|
698
|
+
<SortAscendingOutlined /> 排序方式
|
|
699
|
+
</span>
|
|
700
|
+
<Select
|
|
701
|
+
value={listConfig.sortBy}
|
|
702
|
+
onChange={handleSortChange}
|
|
703
|
+
style={{ width: 120 }}
|
|
704
|
+
size="middle"
|
|
705
|
+
>
|
|
706
|
+
<Select.Option value="name">名称</Select.Option>
|
|
707
|
+
<Select.Option value="createdAt">创建时间</Select.Option>
|
|
708
|
+
<Select.Option value="interfaceCount">接口数量</Select.Option>
|
|
709
|
+
<Select.Option value="active">状态</Select.Option>
|
|
710
|
+
</Select>
|
|
711
|
+
<Select
|
|
712
|
+
value={listConfig.sortOrder}
|
|
713
|
+
onChange={handleSortOrderChange}
|
|
714
|
+
style={{ width: 80 }}
|
|
715
|
+
size="middle"
|
|
716
|
+
>
|
|
717
|
+
<Select.Option value="ascend">升序</Select.Option>
|
|
718
|
+
<Select.Option value="descend">降序</Select.Option>
|
|
719
|
+
</Select>
|
|
720
|
+
</div>
|
|
721
|
+
</div>
|
|
722
|
+
</Col>
|
|
723
|
+
|
|
724
|
+
{/* 分页控制区域 */}
|
|
725
|
+
<Col xs={24} sm={12} md={5} lg={6}>
|
|
726
|
+
<div className="pagination-controls">
|
|
727
|
+
<div className="control-group">
|
|
728
|
+
<span className="control-label">每页显示</span>
|
|
729
|
+
<Select
|
|
730
|
+
value={listConfig.pageSize}
|
|
731
|
+
onChange={handlePageSizeChange}
|
|
732
|
+
style={{ width: 80 }}
|
|
733
|
+
size="middle"
|
|
734
|
+
>
|
|
735
|
+
<Select.Option value={8}>8</Select.Option>
|
|
736
|
+
<Select.Option value={12}>12</Select.Option>
|
|
737
|
+
<Select.Option value={16}>16</Select.Option>
|
|
738
|
+
<Select.Option value={24}>24</Select.Option>
|
|
739
|
+
</Select>
|
|
740
|
+
<span className="page-info">
|
|
741
|
+
第 {(listConfig.current - 1) * listConfig.pageSize + 1}-{Math.min(listConfig.current * listConfig.pageSize, mockFeatures.length)} 条
|
|
742
|
+
</span>
|
|
743
|
+
</div>
|
|
744
|
+
</div>
|
|
745
|
+
</Col>
|
|
746
|
+
|
|
747
|
+
{/* 视图切换按钮区域 */}
|
|
748
|
+
<Col xs={24} sm={12} md={3} lg={3}>
|
|
749
|
+
<div className="view-toggle-controls">
|
|
750
|
+
<Tooltip title={viewMode === 'card' ? '切换到列表视图' : '切换到卡片视图'}>
|
|
751
|
+
<Button
|
|
752
|
+
type="default"
|
|
753
|
+
icon={viewMode === 'card' ? <UnorderedListOutlined /> : <AppstoreOutlined />}
|
|
754
|
+
onClick={toggleViewMode}
|
|
755
|
+
className="view-toggle-button"
|
|
756
|
+
>
|
|
757
|
+
{viewMode === 'card' ? '列表视图' : '卡片视图'}
|
|
758
|
+
</Button>
|
|
759
|
+
</Tooltip>
|
|
760
|
+
</div>
|
|
761
|
+
</Col>
|
|
762
|
+
</Row>
|
|
763
|
+
</Card>
|
|
621
764
|
)}
|
|
622
765
|
|
|
623
|
-
<
|
|
766
|
+
<Card className="feature-list-card">
|
|
624
767
|
{loading ? (
|
|
625
|
-
<div className="loading">
|
|
626
|
-
<
|
|
627
|
-
<div>正在加载功能模块...</div>
|
|
768
|
+
<div className="loading-state">
|
|
769
|
+
<Spin size="large" />
|
|
770
|
+
<div className="loading-text">正在加载功能模块...</div>
|
|
628
771
|
</div>
|
|
629
772
|
) : mockFeatures.length > 0 ? (
|
|
630
773
|
<>
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
774
|
+
{/* 根据视图模式渲染不同的视图 */}
|
|
775
|
+
{viewMode === 'card' ? (
|
|
776
|
+
<Row gutter={[20, 20]} className="feature-grid">
|
|
777
|
+
{getPaginatedFeatures().map(feature => renderFeatureCard(feature))}
|
|
778
|
+
</Row>
|
|
779
|
+
) : (
|
|
780
|
+
<div className="feature-list-view">
|
|
781
|
+
{getPaginatedFeatures().map(feature => renderFeatureListItem(feature))}
|
|
782
|
+
</div>
|
|
783
|
+
)}
|
|
634
784
|
|
|
635
785
|
{/* 分页组件 */}
|
|
636
786
|
{mockFeatures.length > listConfig.pageSize && (
|
|
637
|
-
<div
|
|
638
|
-
marginTop: 24,
|
|
639
|
-
textAlign: 'center',
|
|
640
|
-
padding: '16px',
|
|
641
|
-
borderTop: '1px solid #f0f0f0'
|
|
642
|
-
}}>
|
|
787
|
+
<div className="pagination-wrapper">
|
|
643
788
|
<Pagination
|
|
644
789
|
current={listConfig.current}
|
|
645
790
|
pageSize={listConfig.pageSize}
|
|
646
791
|
total={mockFeatures.length}
|
|
647
792
|
onChange={handlePageChange}
|
|
648
793
|
onShowSizeChange={handlePageChange}
|
|
649
|
-
showSizeChanger={
|
|
794
|
+
showSizeChanger={false}
|
|
650
795
|
showQuickJumper={true}
|
|
651
796
|
showTotal={(total, range) =>
|
|
652
797
|
`第 ${range[0]}-${range[1]} 条,共 ${total} 个功能模块`
|
|
653
798
|
}
|
|
654
|
-
pageSizeOptions={['8', '12', '16', '24']}
|
|
655
799
|
size="default"
|
|
800
|
+
className="custom-pagination"
|
|
656
801
|
/>
|
|
657
802
|
</div>
|
|
658
803
|
)}
|
|
659
804
|
</>
|
|
660
805
|
) : (
|
|
661
|
-
<
|
|
662
|
-
|
|
663
|
-
|
|
806
|
+
<Empty
|
|
807
|
+
className="custom-empty"
|
|
808
|
+
image={
|
|
809
|
+
<div className="empty-image">
|
|
810
|
+
<ApiOutlined style={{ fontSize: 64, color: '#d9d9d9' }} />
|
|
811
|
+
</div>
|
|
812
|
+
}
|
|
813
|
+
description={
|
|
814
|
+
<div className="empty-description">
|
|
815
|
+
<div className="empty-title">暂无功能模块</div>
|
|
816
|
+
<div className="empty-subtitle">创建您的第一个功能模块,开始管理接口</div>
|
|
817
|
+
</div>
|
|
818
|
+
}
|
|
819
|
+
>
|
|
664
820
|
<div className="empty-actions">
|
|
665
|
-
<
|
|
821
|
+
<Button type="primary" icon={<PlusOutlined />} onClick={() => openModal()}>
|
|
666
822
|
创建新功能
|
|
667
|
-
</
|
|
668
|
-
<
|
|
669
|
-
|
|
670
|
-
</
|
|
823
|
+
</Button>
|
|
824
|
+
<Button icon={<ExportOutlined />} onClick={importFeatureConfig}>
|
|
825
|
+
导入功能
|
|
826
|
+
</Button>
|
|
671
827
|
</div>
|
|
672
|
-
</
|
|
828
|
+
</Empty>
|
|
673
829
|
)}
|
|
674
|
-
</
|
|
830
|
+
</Card>
|
|
675
831
|
</div>
|
|
676
832
|
|
|
677
833
|
<Modal
|
|
@@ -221,10 +221,37 @@ body {
|
|
|
221
221
|
margin-right: 24px;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
.logo-icon-group {
|
|
225
|
+
position: relative;
|
|
226
|
+
width: 32px;
|
|
227
|
+
height: 32px;
|
|
228
|
+
margin-right: 12px;
|
|
229
|
+
display: flex;
|
|
230
|
+
align-items: center;
|
|
231
|
+
justify-content: center;
|
|
232
|
+
}
|
|
233
|
+
|
|
224
234
|
.logo-icon {
|
|
225
235
|
font-size: 24px;
|
|
226
236
|
color: #1890ff;
|
|
227
|
-
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.logo-icon-primary {
|
|
240
|
+
position: relative;
|
|
241
|
+
z-index: 2;
|
|
242
|
+
font-size: 22px;
|
|
243
|
+
display: none;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.logo-icon-secondary {
|
|
247
|
+
position: absolute;
|
|
248
|
+
right: -6px;
|
|
249
|
+
font-size: 16px;
|
|
250
|
+
color: #52c41a;
|
|
251
|
+
background: white;
|
|
252
|
+
border-radius: 50%;
|
|
253
|
+
box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
|
|
254
|
+
padding: 2px;
|
|
228
255
|
}
|
|
229
256
|
|
|
230
257
|
.logo-text {
|
|
@@ -232,6 +259,10 @@ body {
|
|
|
232
259
|
font-size: 18px;
|
|
233
260
|
font-weight: 600;
|
|
234
261
|
white-space: nowrap;
|
|
262
|
+
background: linear-gradient(135deg, #1890ff, #52c41a);
|
|
263
|
+
-webkit-background-clip: text;
|
|
264
|
+
-webkit-text-fill-color: transparent;
|
|
265
|
+
letter-spacing: -0.5px;
|
|
235
266
|
}
|
|
236
267
|
|
|
237
268
|
.collapse-button {
|
|
@@ -332,6 +363,11 @@ body {
|
|
|
332
363
|
}
|
|
333
364
|
|
|
334
365
|
.dark-theme .logo-text,
|
|
335
|
-
.dark-theme .logo-icon {
|
|
366
|
+
.dark-theme .logo-icon-primary {
|
|
336
367
|
color: #177ddc;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.dark-theme .logo-icon-secondary {
|
|
371
|
+
color: #49aa19;
|
|
372
|
+
background: #141414;
|
|
337
373
|
}
|