whistle.mock-plugins 1.2.2-beta.3 → 1.2.2-beta.4
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 +15 -0
- package/app/public/js/index.js +1 -1
- package/package.json +1 -1
- package/ui/src/components/interface-management/DataManagementDrawer.js +115 -0
- package/ui/src/components/interface-management/index.js +1 -0
- package/ui/src/pages/InterfaceManagementV2.js +133 -19
- package/ui/src/pages/Settings.js +40 -6
- package/ui/src/styles/settings.css +29 -0
package/package.json
CHANGED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { Drawer, Form, Input, message, Button, Space } from 'antd';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import { ResponseContentEditor, PreviewModal, formatResponseContent, refreshCacheAfterUpdate } from '.';
|
|
5
|
+
|
|
6
|
+
const DataManagementDrawer = ({ visible, onCancel, interfaceItem, onSaved }) => {
|
|
7
|
+
const [form] = Form.useForm();
|
|
8
|
+
const [previewVisible, setPreviewVisible] = useState(false);
|
|
9
|
+
const [previewContent, setPreviewContent] = useState(null);
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (visible && interfaceItem) {
|
|
13
|
+
let responses = Array.isArray(interfaceItem.responses) ? interfaceItem.responses : [];
|
|
14
|
+
let activeResponseId = interfaceItem.activeResponseId || (responses[0] && responses[0].id) || '';
|
|
15
|
+
form.setFieldsValue({ responses, activeResponseId });
|
|
16
|
+
}
|
|
17
|
+
}, [visible, interfaceItem]);
|
|
18
|
+
|
|
19
|
+
const handlePreview = () => {
|
|
20
|
+
const values = form.getFieldsValue(true);
|
|
21
|
+
const responses = Array.isArray(values.responses) ? values.responses : [];
|
|
22
|
+
const activeResponse = responses.find(r => r.id === values.activeResponseId) || responses[0];
|
|
23
|
+
if (!activeResponse) {
|
|
24
|
+
message.error('未找到有效的响应内容');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const contentType = 'application/json; charset=utf-8';
|
|
28
|
+
const formatted = formatResponseContent(activeResponse.content || '', contentType);
|
|
29
|
+
setPreviewContent({ title: `预览: ${activeResponse.name || '未命名响应'}`, content: formatted, contentType, description: '' });
|
|
30
|
+
setPreviewVisible(true);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleOk = async () => {
|
|
34
|
+
try {
|
|
35
|
+
await form.validateFields();
|
|
36
|
+
const values = form.getFieldsValue(true);
|
|
37
|
+
const payload = {
|
|
38
|
+
responses: Array.isArray(values.responses) ? values.responses : [],
|
|
39
|
+
activeResponseId: values.activeResponseId || (values.responses && values.responses[0] && values.responses[0].id) || ''
|
|
40
|
+
};
|
|
41
|
+
if (!interfaceItem || !interfaceItem.id) {
|
|
42
|
+
message.error('缺少接口ID,无法保存');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const res = await axios.patch(`/cgi-bin/interfaces/${interfaceItem.id}`, payload);
|
|
46
|
+
if (res.data && res.data.code === 0) {
|
|
47
|
+
message.success('保存成功');
|
|
48
|
+
await refreshCacheAfterUpdate();
|
|
49
|
+
if (onSaved) onSaved();
|
|
50
|
+
if (onCancel) onCancel();
|
|
51
|
+
} else {
|
|
52
|
+
throw new Error(res.data?.message || '保存失败');
|
|
53
|
+
}
|
|
54
|
+
} catch (err) {
|
|
55
|
+
if (err && err.errorFields) return; // 表单校验错误
|
|
56
|
+
console.error('保存失败', err);
|
|
57
|
+
message.error(err.response?.data?.message || err.message || '保存失败');
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
<Drawer
|
|
64
|
+
title="数据管理"
|
|
65
|
+
placement="right"
|
|
66
|
+
width={1040}
|
|
67
|
+
open={visible}
|
|
68
|
+
onClose={onCancel}
|
|
69
|
+
destroyOnClose
|
|
70
|
+
bodyStyle={{ paddingBottom: 80 }}
|
|
71
|
+
footer={
|
|
72
|
+
<div style={{ textAlign: 'right' }}>
|
|
73
|
+
<Space>
|
|
74
|
+
<Button onClick={onCancel}>取消</Button>
|
|
75
|
+
<Button type="primary" onClick={handleOk}>保存</Button>
|
|
76
|
+
</Space>
|
|
77
|
+
</div>
|
|
78
|
+
}
|
|
79
|
+
>
|
|
80
|
+
<Form form={form} layout="vertical" initialValues={{ responses: [], activeResponseId: '' }}>
|
|
81
|
+
<Form.Item name="responses" hidden>
|
|
82
|
+
<Input />
|
|
83
|
+
</Form.Item>
|
|
84
|
+
<Form.Item name="activeResponseId" hidden>
|
|
85
|
+
<Input />
|
|
86
|
+
</Form.Item>
|
|
87
|
+
<Form.Item noStyle shouldUpdate>
|
|
88
|
+
{({ getFieldValue }) => {
|
|
89
|
+
const responses = getFieldValue('responses') || [];
|
|
90
|
+
const activeResponseId = getFieldValue('activeResponseId') || (responses[0] && responses[0].id) || '';
|
|
91
|
+
return (
|
|
92
|
+
<ResponseContentEditor
|
|
93
|
+
form={form}
|
|
94
|
+
responses={responses}
|
|
95
|
+
activeResponseId={activeResponseId}
|
|
96
|
+
onPreview={handlePreview}
|
|
97
|
+
enableSorting={true}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}}
|
|
101
|
+
</Form.Item>
|
|
102
|
+
</Form>
|
|
103
|
+
</Drawer>
|
|
104
|
+
|
|
105
|
+
<PreviewModal
|
|
106
|
+
visible={previewVisible}
|
|
107
|
+
onCancel={() => setPreviewVisible(false)}
|
|
108
|
+
previewContent={previewContent}
|
|
109
|
+
/>
|
|
110
|
+
</>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export default DataManagementDrawer;
|
|
115
|
+
|
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
export { default as ResponseContentEditor } from './ResponseContentEditor';
|
|
3
3
|
export { default as InterfaceTestModal } from './InterfaceTestModal';
|
|
4
4
|
export { default as PreviewModal } from './PreviewModal';
|
|
5
|
+
export { default as DataManagementDrawer } from './DataManagementDrawer';
|
|
5
6
|
export * from './constants';
|
|
6
7
|
export * from './utils';
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
generateResponseId
|
|
33
33
|
} from '../components/interface-management';
|
|
34
34
|
import DataManagementModal from '../components/interface-management/DataManagementModal';
|
|
35
|
+
import { DataManagementDrawer } from '../components/interface-management';
|
|
35
36
|
|
|
36
37
|
const { Option } = Select;
|
|
37
38
|
const { Search } = AntInput;
|
|
@@ -91,6 +92,9 @@ const InterfaceManagement = () => {
|
|
|
91
92
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
|
92
93
|
const [drawerVisible, setDrawerVisible] = useState(false);
|
|
93
94
|
|
|
95
|
+
// 数据管理显示模式
|
|
96
|
+
const [displayMode, setDisplayMode] = useState('modal');
|
|
97
|
+
|
|
94
98
|
// 表单实例
|
|
95
99
|
const [form] = Form.useForm();
|
|
96
100
|
|
|
@@ -138,6 +142,29 @@ const InterfaceManagement = () => {
|
|
|
138
142
|
}
|
|
139
143
|
}, [selectedFeatureId]);
|
|
140
144
|
|
|
145
|
+
// 监听数据管理显示模式变化
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
// 初始化时从 localStorage 读取配置
|
|
148
|
+
const savedMode = localStorage.getItem('dataManagementDisplayMode');
|
|
149
|
+
if (savedMode) {
|
|
150
|
+
setDisplayMode(savedMode);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 监听 storage 事件,实现设置立即生效
|
|
154
|
+
const handleStorageChange = () => {
|
|
155
|
+
const newMode = localStorage.getItem('dataManagementDisplayMode');
|
|
156
|
+
if (newMode) {
|
|
157
|
+
setDisplayMode(newMode);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
window.addEventListener('storage', handleStorageChange);
|
|
162
|
+
|
|
163
|
+
return () => {
|
|
164
|
+
window.removeEventListener('storage', handleStorageChange);
|
|
165
|
+
};
|
|
166
|
+
}, []);
|
|
167
|
+
|
|
141
168
|
// 获取当前选中的功能模块
|
|
142
169
|
const selectedFeature = features.find(f => f.id === selectedFeatureId);
|
|
143
170
|
|
|
@@ -693,7 +720,17 @@ const InterfaceManagement = () => {
|
|
|
693
720
|
saveTableConfig({
|
|
694
721
|
visibleColumns: defaultColumns,
|
|
695
722
|
columnWidths: {
|
|
696
|
-
|
|
723
|
+
active: 80,
|
|
724
|
+
name: 150,
|
|
725
|
+
group: 120,
|
|
726
|
+
urlPattern: 250,
|
|
727
|
+
proxyType: 120,
|
|
728
|
+
currentResponse: 180,
|
|
729
|
+
responseDelay: 100,
|
|
730
|
+
httpStatus: 100,
|
|
731
|
+
contentType: 120,
|
|
732
|
+
targetUrl: 200,
|
|
733
|
+
customHeaders: 100
|
|
697
734
|
}
|
|
698
735
|
});
|
|
699
736
|
};
|
|
@@ -740,11 +777,21 @@ const InterfaceManagement = () => {
|
|
|
740
777
|
|
|
741
778
|
const startX = e.clientX;
|
|
742
779
|
const startWidth = width;
|
|
780
|
+
let currentWidth = startWidth;
|
|
743
781
|
|
|
744
782
|
const handleMouseMove = (moveEvent) => {
|
|
745
783
|
const deltaX = moveEvent.clientX - startX;
|
|
746
|
-
|
|
747
|
-
|
|
784
|
+
currentWidth = Math.max(50, startWidth + deltaX); // 最小宽度 50px
|
|
785
|
+
|
|
786
|
+
// 实时更新列宽(不保存到 localStorage)
|
|
787
|
+
const newColumnWidths = {
|
|
788
|
+
...tableConfig.columnWidths,
|
|
789
|
+
[columnKey]: currentWidth
|
|
790
|
+
};
|
|
791
|
+
setTableConfig(prev => ({
|
|
792
|
+
...prev,
|
|
793
|
+
columnWidths: newColumnWidths
|
|
794
|
+
}));
|
|
748
795
|
};
|
|
749
796
|
|
|
750
797
|
const handleMouseUp = () => {
|
|
@@ -752,6 +799,9 @@ const InterfaceManagement = () => {
|
|
|
752
799
|
document.removeEventListener('mouseup', handleMouseUp);
|
|
753
800
|
document.body.style.cursor = '';
|
|
754
801
|
document.body.style.userSelect = '';
|
|
802
|
+
|
|
803
|
+
// 拖动结束后保存到 localStorage
|
|
804
|
+
onResize(columnKey, currentWidth);
|
|
755
805
|
};
|
|
756
806
|
|
|
757
807
|
document.addEventListener('mousemove', handleMouseMove);
|
|
@@ -815,7 +865,12 @@ const InterfaceManagement = () => {
|
|
|
815
865
|
title: '状态',
|
|
816
866
|
dataIndex: 'active',
|
|
817
867
|
key: 'active',
|
|
818
|
-
width: 80,
|
|
868
|
+
width: tableConfig.columnWidths?.active || 80,
|
|
869
|
+
onHeaderCell: (column) => ({
|
|
870
|
+
width: column.width,
|
|
871
|
+
columnKey: 'active',
|
|
872
|
+
onResize: handleResize,
|
|
873
|
+
}),
|
|
819
874
|
render: (active, record) => (
|
|
820
875
|
<Switch
|
|
821
876
|
checked={active !== false}
|
|
@@ -828,6 +883,12 @@ const InterfaceManagement = () => {
|
|
|
828
883
|
title: '名称',
|
|
829
884
|
dataIndex: 'name',
|
|
830
885
|
key: 'name',
|
|
886
|
+
width: tableConfig.columnWidths?.name || 150,
|
|
887
|
+
onHeaderCell: (column) => ({
|
|
888
|
+
width: column.width,
|
|
889
|
+
columnKey: 'name',
|
|
890
|
+
onResize: handleResize,
|
|
891
|
+
}),
|
|
831
892
|
sorter: (a, b) => a.name.localeCompare(b.name),
|
|
832
893
|
render: (text) => <span className="interface-name">{text}</span>,
|
|
833
894
|
},
|
|
@@ -835,7 +896,12 @@ const InterfaceManagement = () => {
|
|
|
835
896
|
title: '分组',
|
|
836
897
|
dataIndex: 'group',
|
|
837
898
|
key: 'group',
|
|
838
|
-
width: 120,
|
|
899
|
+
width: tableConfig.columnWidths?.group || 120,
|
|
900
|
+
onHeaderCell: (column) => ({
|
|
901
|
+
width: column.width,
|
|
902
|
+
columnKey: 'group',
|
|
903
|
+
onResize: handleResize,
|
|
904
|
+
}),
|
|
839
905
|
sorter: (a, b) => {
|
|
840
906
|
const groupA = a.group || '';
|
|
841
907
|
const groupB = b.group || '';
|
|
@@ -856,6 +922,12 @@ const InterfaceManagement = () => {
|
|
|
856
922
|
title: 'URL匹配规则',
|
|
857
923
|
dataIndex: 'urlPattern',
|
|
858
924
|
key: 'urlPattern',
|
|
925
|
+
width: tableConfig.columnWidths?.urlPattern || 250,
|
|
926
|
+
onHeaderCell: (column) => ({
|
|
927
|
+
width: column.width,
|
|
928
|
+
columnKey: 'urlPattern',
|
|
929
|
+
onResize: handleResize,
|
|
930
|
+
}),
|
|
859
931
|
ellipsis: true,
|
|
860
932
|
render: (text) => <span className="url-pattern">{text}</span>,
|
|
861
933
|
},
|
|
@@ -863,7 +935,12 @@ const InterfaceManagement = () => {
|
|
|
863
935
|
title: '处理方式',
|
|
864
936
|
dataIndex: 'proxyType',
|
|
865
937
|
key: 'proxyType',
|
|
866
|
-
width: 120,
|
|
938
|
+
width: tableConfig.columnWidths?.proxyType || 120,
|
|
939
|
+
onHeaderCell: (column) => ({
|
|
940
|
+
width: column.width,
|
|
941
|
+
columnKey: 'proxyType',
|
|
942
|
+
onResize: handleResize,
|
|
943
|
+
}),
|
|
867
944
|
render: (text) => {
|
|
868
945
|
const proxyType = proxyTypes.find(item => item.value === text);
|
|
869
946
|
return (
|
|
@@ -921,7 +998,12 @@ const InterfaceManagement = () => {
|
|
|
921
998
|
title: '延迟(毫秒)',
|
|
922
999
|
dataIndex: 'responseDelay',
|
|
923
1000
|
key: 'responseDelay',
|
|
924
|
-
width: 100,
|
|
1001
|
+
width: tableConfig.columnWidths?.responseDelay || 100,
|
|
1002
|
+
onHeaderCell: (column) => ({
|
|
1003
|
+
width: column.width,
|
|
1004
|
+
columnKey: 'responseDelay',
|
|
1005
|
+
onResize: handleResize,
|
|
1006
|
+
}),
|
|
925
1007
|
sorter: (a, b) => (a.responseDelay || 0) - (b.responseDelay || 0),
|
|
926
1008
|
render: (delay) => {
|
|
927
1009
|
const delayValue = parseInt(delay, 10) || 0;
|
|
@@ -936,7 +1018,12 @@ const InterfaceManagement = () => {
|
|
|
936
1018
|
title: '状态码',
|
|
937
1019
|
dataIndex: 'httpStatus',
|
|
938
1020
|
key: 'httpStatus',
|
|
939
|
-
width: 100,
|
|
1021
|
+
width: tableConfig.columnWidths?.httpStatus || 100,
|
|
1022
|
+
onHeaderCell: (column) => ({
|
|
1023
|
+
width: column.width,
|
|
1024
|
+
columnKey: 'httpStatus',
|
|
1025
|
+
onResize: handleResize,
|
|
1026
|
+
}),
|
|
940
1027
|
render: (status) => {
|
|
941
1028
|
const statusCode = status || 200;
|
|
942
1029
|
let statusClass = 'status-success';
|
|
@@ -952,7 +1039,12 @@ const InterfaceManagement = () => {
|
|
|
952
1039
|
title: '内容类型',
|
|
953
1040
|
dataIndex: 'contentType',
|
|
954
1041
|
key: 'contentType',
|
|
955
|
-
width: 120,
|
|
1042
|
+
width: tableConfig.columnWidths?.contentType || 120,
|
|
1043
|
+
onHeaderCell: (column) => ({
|
|
1044
|
+
width: column.width,
|
|
1045
|
+
columnKey: 'contentType',
|
|
1046
|
+
onResize: handleResize,
|
|
1047
|
+
}),
|
|
956
1048
|
sorter: (a, b) => {
|
|
957
1049
|
const aType = a.contentType || '';
|
|
958
1050
|
const bType = b.contentType || '';
|
|
@@ -971,6 +1063,12 @@ const InterfaceManagement = () => {
|
|
|
971
1063
|
title: '目标URL',
|
|
972
1064
|
dataIndex: 'targetUrl',
|
|
973
1065
|
key: 'targetUrl',
|
|
1066
|
+
width: tableConfig.columnWidths?.targetUrl || 200,
|
|
1067
|
+
onHeaderCell: (column) => ({
|
|
1068
|
+
width: column.width,
|
|
1069
|
+
columnKey: 'targetUrl',
|
|
1070
|
+
onResize: handleResize,
|
|
1071
|
+
}),
|
|
974
1072
|
ellipsis: true,
|
|
975
1073
|
sorter: (a, b) => {
|
|
976
1074
|
const aUrl = a.targetUrl || '';
|
|
@@ -986,7 +1084,12 @@ const InterfaceManagement = () => {
|
|
|
986
1084
|
title: '自定义头',
|
|
987
1085
|
dataIndex: 'customHeaders',
|
|
988
1086
|
key: 'customHeaders',
|
|
989
|
-
width: 100,
|
|
1087
|
+
width: tableConfig.columnWidths?.customHeaders || 100,
|
|
1088
|
+
onHeaderCell: (column) => ({
|
|
1089
|
+
width: column.width,
|
|
1090
|
+
columnKey: 'customHeaders',
|
|
1091
|
+
onResize: handleResize,
|
|
1092
|
+
}),
|
|
990
1093
|
sorter: (a, b) => {
|
|
991
1094
|
const aCount = Object.keys(a.customHeaders || {}).length;
|
|
992
1095
|
const bCount = Object.keys(b.customHeaders || {}).length;
|
|
@@ -2012,15 +2115,26 @@ const InterfaceManagement = () => {
|
|
|
2012
2115
|
previewContent={previewContent}
|
|
2013
2116
|
/>
|
|
2014
2117
|
|
|
2015
|
-
{/*
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2118
|
+
{/* 数据管理弹窗/抽屉 */}
|
|
2119
|
+
{displayMode === 'drawer' ? (
|
|
2120
|
+
<DataManagementDrawer
|
|
2121
|
+
visible={dataModalVisible}
|
|
2122
|
+
onCancel={() => { setDataModalVisible(false); setDataModalRecord(null); }}
|
|
2123
|
+
interfaceItem={dataModalRecord}
|
|
2124
|
+
onSaved={() => {
|
|
2125
|
+
fetchInterfaces();
|
|
2126
|
+
}}
|
|
2127
|
+
/>
|
|
2128
|
+
) : (
|
|
2129
|
+
<DataManagementModal
|
|
2130
|
+
visible={dataModalVisible}
|
|
2131
|
+
onCancel={() => { setDataModalVisible(false); setDataModalRecord(null); }}
|
|
2132
|
+
interfaceItem={dataModalRecord}
|
|
2133
|
+
onSaved={() => {
|
|
2134
|
+
fetchInterfaces();
|
|
2135
|
+
}}
|
|
2136
|
+
/>
|
|
2137
|
+
)}
|
|
2024
2138
|
</div>
|
|
2025
2139
|
</AppLayout>
|
|
2026
2140
|
);
|
package/ui/src/pages/Settings.js
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import AppLayout from '../components/AppLayout';
|
|
3
|
-
import { Card, Typography,
|
|
3
|
+
import { Card, Typography, Switch, Space, Divider } from 'antd';
|
|
4
4
|
import '../styles/settings.css';
|
|
5
5
|
|
|
6
6
|
const { Title, Text, Paragraph } = Typography;
|
|
7
7
|
|
|
8
8
|
const Settings = () => {
|
|
9
|
+
const [displayMode, setDisplayMode] = useState('modal');
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
// 从 localStorage 读取配置
|
|
13
|
+
const savedMode = localStorage.getItem('dataManagementDisplayMode');
|
|
14
|
+
if (savedMode) {
|
|
15
|
+
setDisplayMode(savedMode);
|
|
16
|
+
}
|
|
17
|
+
}, []);
|
|
18
|
+
|
|
19
|
+
const handleDisplayModeChange = (checked) => {
|
|
20
|
+
const newMode = checked ? 'drawer' : 'modal';
|
|
21
|
+
setDisplayMode(newMode);
|
|
22
|
+
localStorage.setItem('dataManagementDisplayMode', newMode);
|
|
23
|
+
|
|
24
|
+
// 触发 storage 事件,通知其他页面
|
|
25
|
+
window.dispatchEvent(new Event('storage'));
|
|
26
|
+
};
|
|
27
|
+
|
|
9
28
|
return (
|
|
10
29
|
<AppLayout>
|
|
11
30
|
<div className="page-container">
|
|
@@ -19,10 +38,25 @@ const Settings = () => {
|
|
|
19
38
|
</div>
|
|
20
39
|
|
|
21
40
|
<Card title="界面设置" className="settings-card">
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
41
|
+
<div className="settings-item">
|
|
42
|
+
<div className="settings-item-header">
|
|
43
|
+
<div>
|
|
44
|
+
<div className="settings-item-title">数据管理交互</div>
|
|
45
|
+
<div className="settings-item-description">
|
|
46
|
+
选择数据管理的显示方式
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<Space>
|
|
50
|
+
<Text type="secondary">
|
|
51
|
+
{displayMode === 'drawer' ? '抽屉模式' : '弹窗模式'}
|
|
52
|
+
</Text>
|
|
53
|
+
<Switch
|
|
54
|
+
checked={displayMode === 'drawer'}
|
|
55
|
+
onChange={handleDisplayModeChange}
|
|
56
|
+
/>
|
|
57
|
+
</Space>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
26
60
|
</Card>
|
|
27
61
|
</div>
|
|
28
62
|
</AppLayout>
|
|
@@ -33,6 +33,35 @@
|
|
|
33
33
|
font-weight: 500;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/* 设置项样式 */
|
|
37
|
+
.settings-item {
|
|
38
|
+
padding: 16px 0;
|
|
39
|
+
border-bottom: 1px solid #f0f0f0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.settings-item:last-child {
|
|
43
|
+
border-bottom: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.settings-item-header {
|
|
47
|
+
display: flex;
|
|
48
|
+
justify-content: space-between;
|
|
49
|
+
align-items: center;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.settings-item-title {
|
|
53
|
+
font-size: 14px;
|
|
54
|
+
font-weight: 500;
|
|
55
|
+
color: #262626;
|
|
56
|
+
margin-bottom: 4px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.settings-item-description {
|
|
60
|
+
font-size: 12px;
|
|
61
|
+
color: #8c8c8c;
|
|
62
|
+
line-height: 1.5;
|
|
63
|
+
}
|
|
64
|
+
|
|
36
65
|
.settings-section {
|
|
37
66
|
margin-bottom: 32px;
|
|
38
67
|
}
|