Merge branch 'master' of http://gitlab.erry.com/zhouhui.jiang/test_cursor
Showing
3 changed files
with
1230 additions
and
653 deletions
| ... | @@ -116,7 +116,7 @@ | ... | @@ -116,7 +116,7 @@ |
| 116 | <th>异常类型</th> | 116 | <th>异常类型</th> |
| 117 | <th>严重程度</th> | 117 | <th>严重程度</th> |
| 118 | <th class="sortable">日期 ↕️</th> | 118 | <th class="sortable">日期 ↕️</th> |
| 119 | - <th>操作</th> | 119 | + <th class="action-header">操作</th> |
| 120 | </tr> | 120 | </tr> |
| 121 | </thead> | 121 | </thead> |
| 122 | <tbody> | 122 | <tbody> |
| ... | @@ -744,7 +744,7 @@ const handleExport = async () => { | ... | @@ -744,7 +744,7 @@ const handleExport = async () => { |
| 744 | 744 | ||
| 745 | ElMessage.info('正在导出数据,请稍候...') | 745 | ElMessage.info('正在导出数据,请稍候...') |
| 746 | 746 | ||
| 747 | - const response = await exceptionWorkorderApi.exportExceptionWorkorders(searchParams.value) | 747 | + const response = await exceptionWorkorderApi.exportExceptionWorkorders(searchParams) |
| 748 | 748 | ||
| 749 | // 创建下载链接 | 749 | // 创建下载链接 |
| 750 | const blob = new Blob([response as unknown as BlobPart], { | 750 | const blob = new Blob([response as unknown as BlobPart], { |
| ... | @@ -899,21 +899,20 @@ onMounted(() => { | ... | @@ -899,21 +899,20 @@ onMounted(() => { |
| 899 | 899 | ||
| 900 | <style scoped> | 900 | <style scoped> |
| 901 | .exception-workorder-container { | 901 | .exception-workorder-container { |
| 902 | - padding: 0px; | ||
| 903 | background: #f5f5f5; | 902 | background: #f5f5f5; |
| 904 | min-height: 100vh; | 903 | min-height: 100vh; |
| 905 | } | 904 | } |
| 906 | 905 | ||
| 907 | -/* 搜索区域样式 */ | 906 | +/* 搜索区域 */ |
| 908 | .search-section { | 907 | .search-section { |
| 909 | background: white; | 908 | background: white; |
| 910 | - padding: 16px 20px; | 909 | + padding: 16px; |
| 911 | border-bottom: 1px solid #e0e0e0; | 910 | border-bottom: 1px solid #e0e0e0; |
| 912 | } | 911 | } |
| 913 | 912 | ||
| 914 | .search-row { | 913 | .search-row { |
| 915 | display: flex; | 914 | display: flex; |
| 916 | - gap: 25px; | 915 | + gap: 16px; |
| 917 | align-items: center; | 916 | align-items: center; |
| 918 | flex-wrap: wrap; | 917 | flex-wrap: wrap; |
| 919 | margin-bottom: 12px; | 918 | margin-bottom: 12px; |
| ... | @@ -927,127 +926,147 @@ onMounted(() => { | ... | @@ -927,127 +926,147 @@ onMounted(() => { |
| 927 | display: flex; | 926 | display: flex; |
| 928 | align-items: center; | 927 | align-items: center; |
| 929 | gap: 8px; | 928 | gap: 8px; |
| 930 | - flex: 0 0 auto; | ||
| 931 | } | 929 | } |
| 932 | 930 | ||
| 933 | .search-item label { | 931 | .search-item label { |
| 934 | - color: #666; | 932 | + font-size: 12px; |
| 935 | - font-size: 14px; | 933 | + color: #333; |
| 936 | - width: 90px; | ||
| 937 | white-space: nowrap; | 934 | white-space: nowrap; |
| 938 | - text-align: right; | ||
| 939 | } | 935 | } |
| 940 | 936 | ||
| 941 | -.search-input { | 937 | +.search-input, .search-select { |
| 942 | - width: 180px; | 938 | + padding: 6px 8px; |
| 943 | - height: 32px; | 939 | + border: 1px solid #d9d9d9; |
| 944 | - border: 1px solid #ddd; | ||
| 945 | border-radius: 4px; | 940 | border-radius: 4px; |
| 946 | - padding: 0 8px; | 941 | + font-size: 12px; |
| 947 | - font-size: 14px; | 942 | + width: 120px; |
| 948 | } | 943 | } |
| 949 | 944 | ||
| 950 | -.search-select { | 945 | +.search-input:focus, .search-select:focus { |
| 951 | - width: 160px; | 946 | + outline: none; |
| 952 | - height: 32px; | 947 | + border-color: #1890ff; |
| 953 | - border: 1px solid #ddd; | ||
| 954 | - border-radius: 4px; | ||
| 955 | - padding: 0 8px; | ||
| 956 | - font-size: 14px; | ||
| 957 | - background: white; | ||
| 958 | } | 948 | } |
| 959 | 949 | ||
| 960 | .search-actions { | 950 | .search-actions { |
| 961 | - margin-left: auto; | ||
| 962 | display: flex; | 951 | display: flex; |
| 963 | - gap: 10px; | 952 | + gap: 8px; |
| 964 | } | 953 | } |
| 965 | 954 | ||
| 966 | -.search-btn, .reset-btn { | 955 | +.search-btn { |
| 967 | - padding: 6px 16px; | 956 | + background: #1890ff; |
| 957 | + color: white; | ||
| 968 | border: none; | 958 | border: none; |
| 969 | - border-radius: 4px; | 959 | + padding: 4px 8px; |
| 960 | + border-radius: 3px; | ||
| 961 | + font-size: 11px; | ||
| 970 | cursor: pointer; | 962 | cursor: pointer; |
| 971 | - font-size: 14px; | ||
| 972 | - height: 32px; | ||
| 973 | } | 963 | } |
| 974 | 964 | ||
| 975 | -.search-btn { | 965 | +.search-btn:hover { |
| 976 | - background: #007bff; | 966 | + background: #40a9ff; |
| 977 | - color: white; | ||
| 978 | } | 967 | } |
| 979 | 968 | ||
| 980 | .reset-btn { | 969 | .reset-btn { |
| 981 | - background: #6c757d; | 970 | + background: #ff7875; |
| 982 | color: white; | 971 | color: white; |
| 972 | + border: none; | ||
| 973 | + padding: 4px 8px; | ||
| 974 | + border-radius: 3px; | ||
| 975 | + font-size: 11px; | ||
| 976 | + cursor: pointer; | ||
| 977 | + height: 24px; | ||
| 978 | + display: flex; | ||
| 979 | + align-items: center; | ||
| 980 | +} | ||
| 981 | + | ||
| 982 | +.reset-btn:hover { | ||
| 983 | + background: #ff9c99; | ||
| 983 | } | 984 | } |
| 984 | 985 | ||
| 985 | -/* 操作按钮区域样式 */ | 986 | +/* 操作区域 */ |
| 986 | .action-section { | 987 | .action-section { |
| 987 | background: white; | 988 | background: white; |
| 988 | - padding: 12px 20px 12px 20px; | 989 | + padding: 12px 16px; |
| 989 | border-bottom: 1px solid #e0e0e0; | 990 | border-bottom: 1px solid #e0e0e0; |
| 990 | } | 991 | } |
| 991 | 992 | ||
| 992 | -.action-section .action-buttons { | 993 | +.action-buttons { |
| 993 | display: flex; | 994 | display: flex; |
| 994 | - gap: 10px; | 995 | + gap: 6px; |
| 995 | - justify-content: flex-start; | ||
| 996 | - align-items: center; | ||
| 997 | - margin: 0; | ||
| 998 | - padding: 0; | ||
| 999 | } | 996 | } |
| 1000 | 997 | ||
| 1001 | .action-btn { | 998 | .action-btn { |
| 1002 | - padding: 6px 16px; | 999 | + padding: 4px 8px; |
| 1003 | border: none; | 1000 | border: none; |
| 1004 | - border-radius: 4px; | 1001 | + border-radius: 3px; |
| 1002 | + font-size: 11px; | ||
| 1005 | cursor: pointer; | 1003 | cursor: pointer; |
| 1006 | - font-size: 14px; | 1004 | + transition: all 0.2s; |
| 1007 | - height: 32px; | ||
| 1008 | - font-weight: 500; | ||
| 1009 | } | 1005 | } |
| 1010 | 1006 | ||
| 1011 | .action-btn.primary { | 1007 | .action-btn.primary { |
| 1012 | - background: #007bff; | 1008 | + background: #1890ff; |
| 1013 | color: white; | 1009 | color: white; |
| 1014 | } | 1010 | } |
| 1015 | 1011 | ||
| 1012 | +.action-btn.primary:hover { | ||
| 1013 | + background: #40a9ff; | ||
| 1014 | +} | ||
| 1015 | + | ||
| 1016 | .action-btn.success { | 1016 | .action-btn.success { |
| 1017 | - background: #28a745; | 1017 | + background: #52c41a; |
| 1018 | color: white; | 1018 | color: white; |
| 1019 | } | 1019 | } |
| 1020 | 1020 | ||
| 1021 | +.action-btn.success:hover { | ||
| 1022 | + background: #73d13d; | ||
| 1023 | +} | ||
| 1024 | + | ||
| 1021 | .action-btn.secondary { | 1025 | .action-btn.secondary { |
| 1022 | - background: #6c757d; | 1026 | + background: #52c41a; |
| 1023 | color: white; | 1027 | color: white; |
| 1024 | } | 1028 | } |
| 1025 | 1029 | ||
| 1026 | -/* 表格区域样式 */ | 1030 | +.action-btn.secondary:hover { |
| 1031 | + background: #73d13d; | ||
| 1032 | +} | ||
| 1033 | + | ||
| 1034 | +/* 表格区域 */ | ||
| 1027 | .table-section { | 1035 | .table-section { |
| 1028 | background: white; | 1036 | background: white; |
| 1029 | - margin-bottom: 0; | 1037 | + margin: 16px; |
| 1038 | + border-radius: 6px; | ||
| 1039 | + overflow: hidden; | ||
| 1040 | + box-shadow: 0 2px 8px rgba(0,0,0,0.1); | ||
| 1030 | } | 1041 | } |
| 1031 | 1042 | ||
| 1032 | .table-header { | 1043 | .table-header { |
| 1033 | - padding: 10px 20px; | 1044 | + padding: 8px 16px; |
| 1034 | - border-bottom: 1px solid #eee; | 1045 | + background: #fafafa; |
| 1046 | + border-bottom: 1px solid #e0e0e0; | ||
| 1035 | display: flex; | 1047 | display: flex; |
| 1036 | justify-content: flex-end; | 1048 | justify-content: flex-end; |
| 1037 | } | 1049 | } |
| 1038 | 1050 | ||
| 1039 | .table-controls { | 1051 | .table-controls { |
| 1040 | display: flex; | 1052 | display: flex; |
| 1041 | - gap: 5px; | 1053 | + gap: 4px; |
| 1042 | } | 1054 | } |
| 1043 | 1055 | ||
| 1044 | .control-btn { | 1056 | .control-btn { |
| 1045 | - padding: 4px 8px; | ||
| 1046 | - border: 1px solid #ddd; | ||
| 1047 | background: white; | 1057 | background: white; |
| 1048 | - border-radius: 4px; | 1058 | + border: 1px solid #d9d9d9; |
| 1059 | + border-radius: 3px; | ||
| 1060 | + padding: 4px 8px; | ||
| 1061 | + font-size: 11px; | ||
| 1049 | cursor: pointer; | 1062 | cursor: pointer; |
| 1050 | - font-size: 12px; | 1063 | + transition: all 0.2s; |
| 1064 | +} | ||
| 1065 | + | ||
| 1066 | +.control-btn:hover { | ||
| 1067 | + background: #f0f8ff; | ||
| 1068 | + border-color: #1890ff; | ||
| 1069 | + color: #1890ff; | ||
| 1051 | } | 1070 | } |
| 1052 | 1071 | ||
| 1053 | .table-container { | 1072 | .table-container { |
| ... | @@ -1077,31 +1096,29 @@ onMounted(() => { | ... | @@ -1077,31 +1096,29 @@ onMounted(() => { |
| 1077 | .data-table { | 1096 | .data-table { |
| 1078 | width: 100%; | 1097 | width: 100%; |
| 1079 | border-collapse: collapse; | 1098 | border-collapse: collapse; |
| 1080 | - font-size: 14px; | ||
| 1081 | } | 1099 | } |
| 1082 | 1100 | ||
| 1083 | -.data-table th { | 1101 | +.data-table th, |
| 1084 | - background: #f8f9fa; | 1102 | +.data-table td { |
| 1085 | - border: 1px solid #ddd; | 1103 | + padding: 8px 12px; |
| 1086 | - padding: 12px 8px; | ||
| 1087 | text-align: left; | 1104 | text-align: left; |
| 1105 | + border-bottom: 1px solid #f0f0f0; | ||
| 1106 | + font-size: 12px; | ||
| 1107 | +} | ||
| 1108 | + | ||
| 1109 | +.data-table th { | ||
| 1110 | + background: #fafafa; | ||
| 1088 | font-weight: 600; | 1111 | font-weight: 600; |
| 1089 | color: #333; | 1112 | color: #333; |
| 1090 | white-space: nowrap; | 1113 | white-space: nowrap; |
| 1091 | } | 1114 | } |
| 1092 | 1115 | ||
| 1093 | -.data-table td { | 1116 | +.data-table th.action-header { |
| 1094 | - border: 1px solid #ddd; | 1117 | + text-align: center; |
| 1095 | - padding: 12px 8px; | ||
| 1096 | - vertical-align: middle; | ||
| 1097 | -} | ||
| 1098 | - | ||
| 1099 | -.data-table tbody tr:nth-child(even) { | ||
| 1100 | - background: #f8f9fa; | ||
| 1101 | } | 1118 | } |
| 1102 | 1119 | ||
| 1103 | .data-table tbody tr:hover { | 1120 | .data-table tbody tr:hover { |
| 1104 | - background: #e9ecef; | 1121 | + background: #f5f7fa; |
| 1105 | } | 1122 | } |
| 1106 | 1123 | ||
| 1107 | .sortable { | 1124 | .sortable { |
| ... | @@ -1109,26 +1126,31 @@ onMounted(() => { | ... | @@ -1109,26 +1126,31 @@ onMounted(() => { |
| 1109 | user-select: none; | 1126 | user-select: none; |
| 1110 | } | 1127 | } |
| 1111 | 1128 | ||
| 1129 | +.table-checkbox { | ||
| 1130 | + width: 14px; | ||
| 1131 | + height: 14px; | ||
| 1132 | + cursor: pointer; | ||
| 1133 | +} | ||
| 1134 | + | ||
| 1112 | .select-all, .row-select { | 1135 | .select-all, .row-select { |
| 1113 | margin: 0; | 1136 | margin: 0; |
| 1114 | } | 1137 | } |
| 1115 | 1138 | ||
| 1116 | .status-badge, .severity-badge { | 1139 | .status-badge, .severity-badge { |
| 1117 | - padding: 2px 8px; | 1140 | + padding: 2px 6px; |
| 1118 | - border-radius: 12px; | 1141 | + border-radius: 3px; |
| 1119 | - font-size: 12px; | 1142 | + font-size: 10px; |
| 1120 | - font-weight: 500; | ||
| 1121 | display: inline-block; | 1143 | display: inline-block; |
| 1122 | } | 1144 | } |
| 1123 | 1145 | ||
| 1124 | .status-badge.pending { | 1146 | .status-badge.pending { |
| 1125 | - background: #fff3cd; | 1147 | + background: #fff7e6; |
| 1126 | - color: #856404; | 1148 | + color: #fa8c16; |
| 1127 | } | 1149 | } |
| 1128 | 1150 | ||
| 1129 | .status-badge.processing { | 1151 | .status-badge.processing { |
| 1130 | - background: #d1ecf1; | 1152 | + background: #e6f7ff; |
| 1131 | - color: #0c5460; | 1153 | + color: #1890ff; |
| 1132 | } | 1154 | } |
| 1133 | 1155 | ||
| 1134 | .status-badge.resolved { | 1156 | .status-badge.resolved { |
| ... | @@ -1200,24 +1222,24 @@ onMounted(() => { | ... | @@ -1200,24 +1222,24 @@ onMounted(() => { |
| 1200 | /* 表格内操作按钮容器 */ | 1222 | /* 表格内操作按钮容器 */ |
| 1201 | .table-actions { | 1223 | .table-actions { |
| 1202 | display: flex; | 1224 | display: flex; |
| 1203 | - gap: 8px; | 1225 | + gap: 4px; |
| 1204 | justify-content: center; | 1226 | justify-content: center; |
| 1205 | align-items: center; | 1227 | align-items: center; |
| 1206 | flex-wrap: wrap; | 1228 | flex-wrap: wrap; |
| 1207 | padding: 0 4px; | 1229 | padding: 0 4px; |
| 1208 | } | 1230 | } |
| 1209 | 1231 | ||
| 1210 | -/* 操作按钮样式 */ | 1232 | +/* 表格内操作按钮样式 */ |
| 1211 | -.action-btn { | 1233 | +.table-actions .action-btn { |
| 1212 | - padding: 4px 8px; | 1234 | + padding: 2px 6px; |
| 1213 | - font-size: 12px; | 1235 | + margin-right: 4px; |
| 1214 | - border-radius: 4px; | 1236 | + border: none; |
| 1215 | - font-weight: 500; | 1237 | + border-radius: 3px; |
| 1216 | - min-width: 60px; | 1238 | + font-size: 10px; |
| 1217 | - height: 28px; | 1239 | + cursor: pointer; |
| 1218 | display: inline-flex; | 1240 | display: inline-flex; |
| 1219 | align-items: center; | 1241 | align-items: center; |
| 1220 | - justify-content: center; | 1242 | + gap: 2px; |
| 1221 | white-space: nowrap; | 1243 | white-space: nowrap; |
| 1222 | } | 1244 | } |
| 1223 | 1245 | ||
| ... | @@ -1228,49 +1250,52 @@ onMounted(() => { | ... | @@ -1228,49 +1250,52 @@ onMounted(() => { |
| 1228 | 1250 | ||
| 1229 | /* 分页区域样式 */ | 1251 | /* 分页区域样式 */ |
| 1230 | .pagination-section { | 1252 | .pagination-section { |
| 1231 | - background: white; | ||
| 1232 | - padding: 12px 20px; | ||
| 1233 | - border-top: 1px solid #e0e0e0; | ||
| 1234 | display: flex; | 1253 | display: flex; |
| 1235 | justify-content: space-between; | 1254 | justify-content: space-between; |
| 1236 | align-items: center; | 1255 | align-items: center; |
| 1256 | + padding: 8px 16px; | ||
| 1257 | + background: #fafafa; | ||
| 1258 | + border-top: 1px solid #e0e0e0; | ||
| 1237 | } | 1259 | } |
| 1238 | 1260 | ||
| 1239 | .pagination-info { | 1261 | .pagination-info { |
| 1240 | - display: flex; | 1262 | + font-size: 12px; |
| 1241 | - align-items: center; | ||
| 1242 | - gap: 10px; | ||
| 1243 | - font-size: 14px; | ||
| 1244 | color: #666; | 1263 | color: #666; |
| 1245 | } | 1264 | } |
| 1246 | 1265 | ||
| 1247 | .page-size-select { | 1266 | .page-size-select { |
| 1248 | - height: 28px; | 1267 | + padding: 4px 8px; |
| 1249 | - border: 1px solid #ddd; | 1268 | + border: 1px solid #d9d9d9; |
| 1250 | border-radius: 4px; | 1269 | border-radius: 4px; |
| 1251 | - padding: 0 8px; | 1270 | + font-size: 12px; |
| 1252 | - font-size: 14px; | 1271 | + background: white; |
| 1272 | + cursor: pointer; | ||
| 1253 | } | 1273 | } |
| 1254 | 1274 | ||
| 1255 | .pagination-controls { | 1275 | .pagination-controls { |
| 1256 | display: flex; | 1276 | display: flex; |
| 1257 | align-items: center; | 1277 | align-items: center; |
| 1258 | gap: 8px; | 1278 | gap: 8px; |
| 1259 | - font-size: 14px; | ||
| 1260 | } | 1279 | } |
| 1261 | 1280 | ||
| 1262 | .page-btn { | 1281 | .page-btn { |
| 1263 | - padding: 4px 12px; | 1282 | + padding: 4px 8px; |
| 1264 | - border: 1px solid #ddd; | 1283 | + border: 1px solid #d9d9d9; |
| 1284 | + border-radius: 3px; | ||
| 1265 | background: white; | 1285 | background: white; |
| 1266 | - border-radius: 4px; | ||
| 1267 | cursor: pointer; | 1286 | cursor: pointer; |
| 1268 | - font-size: 14px; | 1287 | + font-size: 11px; |
| 1288 | + transition: all 0.2s; | ||
| 1289 | +} | ||
| 1290 | + | ||
| 1291 | +.page-btn:hover:not(:disabled) { | ||
| 1292 | + background: #f0f8ff; | ||
| 1293 | + border-color: #1890ff; | ||
| 1294 | + color: #1890ff; | ||
| 1269 | } | 1295 | } |
| 1270 | 1296 | ||
| 1271 | .page-btn:disabled { | 1297 | .page-btn:disabled { |
| 1272 | - background: #f8f9fa; | 1298 | + opacity: 0.5; |
| 1273 | - color: #6c757d; | ||
| 1274 | cursor: not-allowed; | 1299 | cursor: not-allowed; |
| 1275 | } | 1300 | } |
| 1276 | 1301 | ... | ... |
| ... | @@ -136,6 +136,14 @@ | ... | @@ -136,6 +136,14 @@ |
| 136 | <table class="data-table"> | 136 | <table class="data-table"> |
| 137 | <thead> | 137 | <thead> |
| 138 | <tr> | 138 | <tr> |
| 139 | + <th class="checkbox-col"> | ||
| 140 | + <input | ||
| 141 | + type="checkbox" | ||
| 142 | + :checked="selectAll" | ||
| 143 | + @change="handleSelectAll" | ||
| 144 | + class="table-checkbox" | ||
| 145 | + /> | ||
| 146 | + </th> | ||
| 139 | <th>返利编号</th> | 147 | <th>返利编号</th> |
| 140 | <th>订单编号</th> | 148 | <th>订单编号</th> |
| 141 | <th>经销商代码</th> | 149 | <th>经销商代码</th> |
| ... | @@ -151,9 +159,17 @@ | ... | @@ -151,9 +159,17 @@ |
| 151 | </thead> | 159 | </thead> |
| 152 | <tbody> | 160 | <tbody> |
| 153 | <tr v-if="rebateList.length === 0"> | 161 | <tr v-if="rebateList.length === 0"> |
| 154 | - <td colspan="11" class="empty-data">暂无数据</td> | 162 | + <td colspan="12" class="empty-data">暂无数据</td> |
| 155 | </tr> | 163 | </tr> |
| 156 | <tr v-for="row in rebateList" :key="row.rebateId"> | 164 | <tr v-for="row in rebateList" :key="row.rebateId"> |
| 165 | + <td class="checkbox-col"> | ||
| 166 | + <input | ||
| 167 | + type="checkbox" | ||
| 168 | + :checked="selectedRebates.includes(row)" | ||
| 169 | + @change="handleSelectRebate(row)" | ||
| 170 | + class="table-checkbox" | ||
| 171 | + /> | ||
| 172 | + </td> | ||
| 157 | <td>{{ row.rebateNo }}</td> | 173 | <td>{{ row.rebateNo }}</td> |
| 158 | <td>{{ row.orderNo }}</td> | 174 | <td>{{ row.orderNo }}</td> |
| 159 | <td>{{ row.dealerCode }}</td> | 175 | <td>{{ row.dealerCode }}</td> |
| ... | @@ -173,7 +189,7 @@ | ... | @@ -173,7 +189,7 @@ |
| 173 | </td> | 189 | </td> |
| 174 | <td>{{ formatDate(row.updateTime || '') }}</td> | 190 | <td>{{ formatDate(row.updateTime || '') }}</td> |
| 175 | <td> | 191 | <td> |
| 176 | - <button @click="handleViewDetail(row)" class="action-link">详情</button> | 192 | + <button @click="handleViewDetail(row)" class="table-btn view">✏️ 详情</button> |
| 177 | </td> | 193 | </td> |
| 178 | </tr> | 194 | </tr> |
| 179 | </tbody> | 195 | </tbody> |
| ... | @@ -181,30 +197,46 @@ | ... | @@ -181,30 +197,46 @@ |
| 181 | </div> | 197 | </div> |
| 182 | 198 | ||
| 183 | <!-- 分页 --> | 199 | <!-- 分页 --> |
| 184 | - <div class="pagination-wrapper"> | 200 | + <div class="table-footer"> |
| 201 | + <div class="pagination-left"> | ||
| 185 | <div class="pagination-info"> | 202 | <div class="pagination-info"> |
| 186 | - 共 {{ pagination.total }} 条,{{ pagination.pageSize }}/页 | 203 | + 共 {{ pagination.total }} 条记录,第 {{ pagination.pageNum }} / {{ getTotalPages() }} 页 |
| 204 | + </div> | ||
| 205 | + <div class="page-size-selector"> | ||
| 206 | + <label>每页显示:</label> | ||
| 207 | + <select v-model="pagination.pageSize" @change="handleSizeChange($event)" class="page-size-select"> | ||
| 208 | + <option value="10">10条</option> | ||
| 209 | + <option value="20">20条</option> | ||
| 210 | + <option value="50">50条</option> | ||
| 211 | + <option value="100">100条</option> | ||
| 212 | + </select> | ||
| 213 | + </div> | ||
| 187 | </div> | 214 | </div> |
| 188 | - <div class="pagination-controls"> | 215 | + <div class="pagination-container"> |
| 189 | - <button @click="handlePrevPage" :disabled="pagination.pageNum <= 1" class="page-btn">‹</button> | 216 | + <button |
| 217 | + @click="handlePrevPage" | ||
| 218 | + :disabled="pagination.pageNum <= 1" | ||
| 219 | + class="pagination-btn prev-btn" | ||
| 220 | + > | ||
| 221 | + 上一页 | ||
| 222 | + </button> | ||
| 223 | + <div class="pagination-pages"> | ||
| 190 | <button | 224 | <button |
| 191 | v-for="page in getPageNumbers()" | 225 | v-for="page in getPageNumbers()" |
| 192 | :key="page" | 226 | :key="page" |
| 193 | @click="handlePageChange(page)" | 227 | @click="handlePageChange(page)" |
| 194 | - :class="['page-btn', { active: page === pagination.pageNum }]" | 228 | + :class="['page-number', { active: page === pagination.pageNum }]" |
| 195 | > | 229 | > |
| 196 | {{ page }} | 230 | {{ page }} |
| 197 | </button> | 231 | </button> |
| 198 | - <button @click="handleNextPage" :disabled="pagination.pageNum >= getTotalPages()" class="page-btn">›</button> | ||
| 199 | </div> | 232 | </div> |
| 200 | - <div class="pagination-jump"> | 233 | + <button |
| 201 | - 前往 | 234 | + @click="handleNextPage" |
| 202 | - <input | 235 | + :disabled="pagination.pageNum >= getTotalPages()" |
| 203 | - v-model="jumpPage" | 236 | + class="pagination-btn next-btn" |
| 204 | - class="jump-input" | 237 | + > |
| 205 | - @keyup.enter="handleJumpPage" | 238 | + 下一页 |
| 206 | - /> | 239 | + </button> |
| 207 | - 页 | ||
| 208 | </div> | 240 | </div> |
| 209 | </div> | 241 | </div> |
| 210 | </div> | 242 | </div> |
| ... | @@ -421,6 +453,7 @@ const exportLoading = ref(false) | ... | @@ -421,6 +453,7 @@ const exportLoading = ref(false) |
| 421 | const auditLoading = ref(false) | 453 | const auditLoading = ref(false) |
| 422 | const rebateList = ref<Rebate[]>([]) | 454 | const rebateList = ref<Rebate[]>([]) |
| 423 | const selectedRebates = ref<Rebate[]>([]) | 455 | const selectedRebates = ref<Rebate[]>([]) |
| 456 | +const selectAll = ref(false) | ||
| 424 | const rebateDetail = ref<Rebate | null>(null) | 457 | const rebateDetail = ref<Rebate | null>(null) |
| 425 | 458 | ||
| 426 | // 图表引用 | 459 | // 图表引用 |
| ... | @@ -872,8 +905,9 @@ const getCalcFlagClass = (flag: number) => { | ... | @@ -872,8 +905,9 @@ const getCalcFlagClass = (flag: number) => { |
| 872 | } | 905 | } |
| 873 | 906 | ||
| 874 | // 分页变化 | 907 | // 分页变化 |
| 875 | -const handleSizeChange = (size: number) => { | 908 | +const handleSizeChange = (event: Event) => { |
| 876 | - pagination.pageSize = size | 909 | + const target = event.target as HTMLSelectElement |
| 910 | + pagination.pageSize = parseInt(target.value) | ||
| 877 | pagination.pageNum = 1 | 911 | pagination.pageNum = 1 |
| 878 | getRebateList() | 912 | getRebateList() |
| 879 | } | 913 | } |
| ... | @@ -898,6 +932,27 @@ const handleSelectionChange = (selection: Rebate[]) => { | ... | @@ -898,6 +932,27 @@ const handleSelectionChange = (selection: Rebate[]) => { |
| 898 | selectedRebates.value = selection | 932 | selectedRebates.value = selection |
| 899 | } | 933 | } |
| 900 | 934 | ||
| 935 | +// 全选/取消全选 | ||
| 936 | +const handleSelectAll = () => { | ||
| 937 | + if (selectAll.value) { | ||
| 938 | + selectedRebates.value = [...rebateList.value] | ||
| 939 | + } else { | ||
| 940 | + selectedRebates.value = [] | ||
| 941 | + } | ||
| 942 | +} | ||
| 943 | + | ||
| 944 | +// 选择单个返利记录 | ||
| 945 | +const handleSelectRebate = (rebate: Rebate) => { | ||
| 946 | + const index = selectedRebates.value.findIndex(r => r.rebateId === rebate.rebateId) | ||
| 947 | + if (index > -1) { | ||
| 948 | + selectedRebates.value.splice(index, 1) | ||
| 949 | + } else { | ||
| 950 | + selectedRebates.value.push(rebate) | ||
| 951 | + } | ||
| 952 | + // 更新全选状态 | ||
| 953 | + selectAll.value = selectedRebates.value.length === rebateList.value.length && rebateList.value.length > 0 | ||
| 954 | +} | ||
| 955 | + | ||
| 901 | // 查看详情 | 956 | // 查看详情 |
| 902 | const handleViewDetail = async (row: Rebate) => { | 957 | const handleViewDetail = async (row: Rebate) => { |
| 903 | try { | 958 | try { |
| ... | @@ -1588,170 +1643,132 @@ onMounted(async () => { | ... | @@ -1588,170 +1643,132 @@ onMounted(async () => { |
| 1588 | 1643 | ||
| 1589 | <style scoped lang="scss"> | 1644 | <style scoped lang="scss"> |
| 1590 | .rebate-page { | 1645 | .rebate-page { |
| 1591 | - padding: 20px; | 1646 | + background: #f5f5f5; |
| 1592 | - background: #f5f7fa; | ||
| 1593 | min-height: 100vh; | 1647 | min-height: 100vh; |
| 1594 | - | ||
| 1595 | } | 1648 | } |
| 1596 | 1649 | ||
| 1650 | +/* 搜索区域 */ | ||
| 1597 | .search-section { | 1651 | .search-section { |
| 1598 | background: white; | 1652 | background: white; |
| 1599 | - padding: 16px 20px; | 1653 | + padding: 16px; |
| 1600 | - margin-bottom: 16px; | 1654 | + border-bottom: 1px solid #e0e0e0; |
| 1601 | - border-radius: 4px; | 1655 | +} |
| 1602 | - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); | ||
| 1603 | 1656 | ||
| 1604 | - .search-row { | 1657 | +.search-row { |
| 1605 | display: flex; | 1658 | display: flex; |
| 1659 | + gap: 16px; | ||
| 1606 | align-items: center; | 1660 | align-items: center; |
| 1607 | - gap: 24px; | ||
| 1608 | flex-wrap: wrap; | 1661 | flex-wrap: wrap; |
| 1609 | margin-bottom: 12px; | 1662 | margin-bottom: 12px; |
| 1663 | +} | ||
| 1610 | 1664 | ||
| 1611 | - &:last-child { | 1665 | +.search-row:last-child { |
| 1612 | margin-bottom: 0; | 1666 | margin-bottom: 0; |
| 1613 | - } | 1667 | +} |
| 1614 | 1668 | ||
| 1615 | - .search-item { | 1669 | +.search-item { |
| 1616 | display: flex; | 1670 | display: flex; |
| 1617 | align-items: center; | 1671 | align-items: center; |
| 1618 | gap: 8px; | 1672 | gap: 8px; |
| 1673 | +} | ||
| 1619 | 1674 | ||
| 1620 | - label { | 1675 | +.search-item label { |
| 1621 | - font-size: 14px; | 1676 | + font-size: 12px; |
| 1622 | color: #333; | 1677 | color: #333; |
| 1623 | white-space: nowrap; | 1678 | white-space: nowrap; |
| 1624 | - min-width: 80px; | 1679 | +} |
| 1625 | - } | ||
| 1626 | - | ||
| 1627 | - .search-input { | ||
| 1628 | - padding: 8px 12px; | ||
| 1629 | - border: 1px solid #ddd; | ||
| 1630 | - border-radius: 4px; | ||
| 1631 | - font-size: 14px; | ||
| 1632 | - width: 160px; | ||
| 1633 | - transition: border-color 0.3s; | ||
| 1634 | - | ||
| 1635 | - &:focus { | ||
| 1636 | - outline: none; | ||
| 1637 | - border-color: #409eff; | ||
| 1638 | - } | ||
| 1639 | - } | ||
| 1640 | 1680 | ||
| 1641 | - .search-select { | 1681 | +.search-input, .search-select { |
| 1642 | - padding: 8px 12px; | 1682 | + padding: 6px 8px; |
| 1643 | - border: 1px solid #ddd; | 1683 | + border: 1px solid #d9d9d9; |
| 1644 | border-radius: 4px; | 1684 | border-radius: 4px; |
| 1645 | - font-size: 14px; | 1685 | + font-size: 12px; |
| 1646 | width: 120px; | 1686 | width: 120px; |
| 1647 | - background: white; | 1687 | +} |
| 1648 | - cursor: pointer; | ||
| 1649 | - transition: border-color 0.3s; | ||
| 1650 | 1688 | ||
| 1651 | - &:focus { | 1689 | +.search-input:focus, .search-select:focus { |
| 1652 | outline: none; | 1690 | outline: none; |
| 1653 | - border-color: #409eff; | 1691 | + border-color: #1890ff; |
| 1654 | - } | 1692 | +} |
| 1655 | - } | ||
| 1656 | - } | ||
| 1657 | 1693 | ||
| 1658 | - .search-actions { | 1694 | +.search-actions { |
| 1659 | - margin-left: auto; | ||
| 1660 | display: flex; | 1695 | display: flex; |
| 1661 | gap: 8px; | 1696 | gap: 8px; |
| 1697 | +} | ||
| 1662 | 1698 | ||
| 1663 | - .search-btn, .reset-btn { | 1699 | +.search-btn { |
| 1664 | - padding: 8px 16px; | 1700 | + background: #1890ff; |
| 1665 | - border: 1px solid #ddd; | 1701 | + color: white; |
| 1666 | - border-radius: 4px; | 1702 | + border: none; |
| 1667 | - font-size: 14px; | 1703 | + padding: 4px 8px; |
| 1704 | + border-radius: 3px; | ||
| 1705 | + font-size: 11px; | ||
| 1668 | cursor: pointer; | 1706 | cursor: pointer; |
| 1669 | - transition: all 0.3s; | 1707 | +} |
| 1670 | - background: white; | ||
| 1671 | - | ||
| 1672 | - &:hover { | ||
| 1673 | - background: #f5f7fa; | ||
| 1674 | - } | ||
| 1675 | 1708 | ||
| 1676 | - &:disabled { | 1709 | +.search-btn:hover { |
| 1677 | - opacity: 0.6; | 1710 | + background: #40a9ff; |
| 1678 | - cursor: not-allowed; | 1711 | +} |
| 1679 | - } | ||
| 1680 | - } | ||
| 1681 | 1712 | ||
| 1682 | - .search-btn { | 1713 | +.reset-btn { |
| 1683 | - background: #409eff; | 1714 | + background: #ff7875; |
| 1684 | color: white; | 1715 | color: white; |
| 1685 | - border-color: #409eff; | 1716 | + border: none; |
| 1717 | + padding: 4px 8px; | ||
| 1718 | + border-radius: 3px; | ||
| 1719 | + font-size: 11px; | ||
| 1720 | + cursor: pointer; | ||
| 1721 | + height: 24px; | ||
| 1722 | + display: flex; | ||
| 1723 | + align-items: center; | ||
| 1724 | +} | ||
| 1686 | 1725 | ||
| 1687 | - &:hover { | 1726 | +.reset-btn:hover { |
| 1688 | - background: #66b1ff; | 1727 | + background: #ff9c99; |
| 1689 | - } | ||
| 1690 | - } | ||
| 1691 | - } | ||
| 1692 | - } | ||
| 1693 | } | 1728 | } |
| 1694 | 1729 | ||
| 1730 | +/* 操作区域 */ | ||
| 1695 | .action-section { | 1731 | .action-section { |
| 1696 | background: white; | 1732 | background: white; |
| 1697 | - padding: 12px 20px; | 1733 | + padding: 12px 16px; |
| 1698 | - margin-bottom: 16px; | 1734 | + border-bottom: 1px solid #e0e0e0; |
| 1699 | - border-radius: 4px; | 1735 | +} |
| 1700 | - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); | ||
| 1701 | 1736 | ||
| 1702 | - .action-buttons { | 1737 | +.action-buttons { |
| 1703 | display: flex; | 1738 | display: flex; |
| 1704 | - gap: 12px; | 1739 | + gap: 6px; |
| 1740 | +} | ||
| 1705 | 1741 | ||
| 1706 | - .action-btn { | 1742 | +.action-btn { |
| 1707 | - padding: 8px 16px; | 1743 | + padding: 4px 8px; |
| 1708 | - border: 1px solid #ddd; | 1744 | + border: none; |
| 1709 | - border-radius: 4px; | 1745 | + border-radius: 3px; |
| 1710 | - font-size: 14px; | 1746 | + font-size: 11px; |
| 1711 | cursor: pointer; | 1747 | cursor: pointer; |
| 1712 | - transition: all 0.3s; | 1748 | + transition: all 0.2s; |
| 1713 | - background: white; | 1749 | +} |
| 1714 | - | ||
| 1715 | - &:hover { | ||
| 1716 | - background: #f5f7fa; | ||
| 1717 | - } | ||
| 1718 | - | ||
| 1719 | - &:disabled { | ||
| 1720 | - opacity: 0.6; | ||
| 1721 | - cursor: not-allowed; | ||
| 1722 | - } | ||
| 1723 | 1750 | ||
| 1724 | - &.primary { | 1751 | +.action-btn.primary { |
| 1725 | - background: #409eff; | 1752 | + background: #1890ff; |
| 1726 | color: white; | 1753 | color: white; |
| 1727 | - border-color: #409eff; | 1754 | +} |
| 1728 | 1755 | ||
| 1729 | - &:hover { | 1756 | +.action-btn.primary:hover { |
| 1730 | - background: #66b1ff; | 1757 | + background: #40a9ff; |
| 1731 | - } | 1758 | +} |
| 1732 | - } | ||
| 1733 | 1759 | ||
| 1734 | - &.secondary { | 1760 | +.action-btn.secondary { |
| 1735 | - background: #67c23a; | 1761 | + background: #52c41a; |
| 1736 | color: white; | 1762 | color: white; |
| 1737 | - border-color: #67c23a; | 1763 | +} |
| 1738 | 1764 | ||
| 1739 | - &:hover { | 1765 | +.action-btn.secondary:hover { |
| 1740 | - background: #85ce61; | 1766 | + background: #73d13d; |
| 1741 | - } | 1767 | +} |
| 1742 | - } | ||
| 1743 | 1768 | ||
| 1744 | - &.danger { | 1769 | +.action-btn.danger { |
| 1745 | - background: #f56c6c; | 1770 | + background: #ff4d4f; |
| 1746 | color: white; | 1771 | color: white; |
| 1747 | - border-color: #f56c6c; | ||
| 1748 | - | ||
| 1749 | - &:hover { | ||
| 1750 | - background: #f78989; | ||
| 1751 | - } | ||
| 1752 | - } | ||
| 1753 | - } | ||
| 1754 | - } | ||
| 1755 | } | 1772 | } |
| 1756 | 1773 | ||
| 1757 | .charts-section { | 1774 | .charts-section { |
| ... | @@ -1825,42 +1842,45 @@ onMounted(async () => { | ... | @@ -1825,42 +1842,45 @@ onMounted(async () => { |
| 1825 | width: 100%; | 1842 | width: 100%; |
| 1826 | } | 1843 | } |
| 1827 | 1844 | ||
| 1845 | +/* 表格区域 */ | ||
| 1828 | .table-section { | 1846 | .table-section { |
| 1829 | background: white; | 1847 | background: white; |
| 1830 | - border-radius: 4px; | 1848 | + margin: 16px; |
| 1831 | - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); | 1849 | + border-radius: 6px; |
| 1832 | overflow: hidden; | 1850 | overflow: hidden; |
| 1851 | + box-shadow: 0 2px 8px rgba(0,0,0,0.1); | ||
| 1852 | +} | ||
| 1833 | 1853 | ||
| 1834 | - .table-header { | 1854 | +.table-header { |
| 1855 | + padding: 8px 16px; | ||
| 1856 | + background: #fafafa; | ||
| 1857 | + border-bottom: 1px solid #e0e0e0; | ||
| 1835 | display: flex; | 1858 | display: flex; |
| 1836 | justify-content: flex-end; | 1859 | justify-content: flex-end; |
| 1837 | - align-items: center; | 1860 | +} |
| 1838 | - padding: 12px 20px; | ||
| 1839 | - border-bottom: 1px solid #f0f0f0; | ||
| 1840 | - background: #fafafa; | ||
| 1841 | 1861 | ||
| 1842 | - .table-controls { | 1862 | +.table-controls { |
| 1843 | display: flex; | 1863 | display: flex; |
| 1844 | - gap: 8px; | 1864 | + gap: 4px; |
| 1865 | +} | ||
| 1845 | 1866 | ||
| 1846 | - .control-btn { | 1867 | +.control-btn { |
| 1847 | - padding: 6px 12px; | ||
| 1848 | - border: 1px solid #ddd; | ||
| 1849 | - border-radius: 4px; | ||
| 1850 | background: white; | 1868 | background: white; |
| 1869 | + border: 1px solid #d9d9d9; | ||
| 1870 | + border-radius: 3px; | ||
| 1871 | + padding: 4px 8px; | ||
| 1872 | + font-size: 11px; | ||
| 1851 | cursor: pointer; | 1873 | cursor: pointer; |
| 1852 | - font-size: 14px; | 1874 | + transition: all 0.2s; |
| 1853 | - transition: all 0.3s; | 1875 | +} |
| 1854 | 1876 | ||
| 1855 | - &:hover { | 1877 | +.control-btn:hover { |
| 1856 | - background: #f5f7fa; | 1878 | + background: #f0f8ff; |
| 1857 | - border-color: #409eff; | 1879 | + border-color: #1890ff; |
| 1858 | - } | 1880 | + color: #1890ff; |
| 1859 | - } | 1881 | +} |
| 1860 | - } | ||
| 1861 | - } | ||
| 1862 | 1882 | ||
| 1863 | - .table-container { | 1883 | +.table-container { |
| 1864 | position: relative; | 1884 | position: relative; |
| 1865 | 1885 | ||
| 1866 | .loading-overlay { | 1886 | .loading-overlay { |
| ... | @@ -1880,126 +1900,165 @@ onMounted(async () => { | ... | @@ -1880,126 +1900,165 @@ onMounted(async () => { |
| 1880 | color: #666; | 1900 | color: #666; |
| 1881 | } | 1901 | } |
| 1882 | } | 1902 | } |
| 1903 | + } | ||
| 1883 | 1904 | ||
| 1884 | - .data-table { | 1905 | +.data-table { |
| 1885 | width: 100%; | 1906 | width: 100%; |
| 1886 | border-collapse: collapse; | 1907 | border-collapse: collapse; |
| 1887 | - font-size: 14px; | 1908 | + font-size: 12px; |
| 1888 | - | 1909 | +} |
| 1889 | - thead { | ||
| 1890 | - background: #fafafa; | ||
| 1891 | 1910 | ||
| 1892 | - th { | 1911 | +.data-table th, |
| 1893 | - padding: 12px 16px; | 1912 | +.data-table td { |
| 1913 | + padding: 8px 12px; | ||
| 1894 | text-align: left; | 1914 | text-align: left; |
| 1895 | - font-weight: 500; | 1915 | + border-bottom: 1px solid #f0f0f0; |
| 1916 | + font-size: 12px; | ||
| 1917 | +} | ||
| 1918 | + | ||
| 1919 | +.data-table th { | ||
| 1920 | + background: #fafafa; | ||
| 1921 | + font-weight: 600; | ||
| 1896 | color: #333; | 1922 | color: #333; |
| 1897 | - border-bottom: 1px solid #e0e0e0; | ||
| 1898 | white-space: nowrap; | 1923 | white-space: nowrap; |
| 1899 | - } | 1924 | +} |
| 1900 | - } | ||
| 1901 | - | ||
| 1902 | - tbody { | ||
| 1903 | - tr { | ||
| 1904 | - transition: background-color 0.3s; | ||
| 1905 | 1925 | ||
| 1906 | - &:hover { | 1926 | +.data-table tbody tr:hover { |
| 1907 | background: #f5f7fa; | 1927 | background: #f5f7fa; |
| 1908 | - } | 1928 | +} |
| 1909 | 1929 | ||
| 1910 | - &:nth-child(even) { | 1930 | +/* 表格复选框 */ |
| 1911 | - background: #fafafa; | 1931 | +.table-checkbox { |
| 1912 | - } | 1932 | + width: 14px; |
| 1933 | + height: 14px; | ||
| 1934 | +} | ||
| 1913 | 1935 | ||
| 1914 | - &:nth-child(even):hover { | 1936 | +/* 表格操作按钮 */ |
| 1915 | - background: #f0f2f5; | 1937 | +.table-btn { |
| 1916 | - } | 1938 | + padding: 2px 6px; |
| 1939 | + margin-right: 4px; | ||
| 1940 | + border: none; | ||
| 1941 | + border-radius: 3px; | ||
| 1942 | + font-size: 10px; | ||
| 1943 | + cursor: pointer; | ||
| 1944 | + display: inline-flex; | ||
| 1945 | + align-items: center; | ||
| 1946 | + gap: 2px; | ||
| 1947 | +} | ||
| 1917 | 1948 | ||
| 1918 | - td { | 1949 | +.table-btn.view { |
| 1919 | - padding: 12px 16px; | 1950 | + background: #1890ff; |
| 1920 | - border-bottom: 1px solid #f0f0f0; | 1951 | + color: white; |
| 1921 | - vertical-align: middle; | 1952 | +} |
| 1922 | 1953 | ||
| 1923 | - &.empty-data { | 1954 | +.table-btn.edit { |
| 1924 | - text-align: center; | 1955 | + background: #1890ff; |
| 1925 | - color: #999; | 1956 | + color: white; |
| 1926 | - font-style: italic; | 1957 | +} |
| 1927 | - padding: 40px; | 1958 | + |
| 1928 | - } | 1959 | +.table-btn.delete { |
| 1929 | - } | 1960 | + background: #ff4d4f; |
| 1930 | - } | 1961 | + color: white; |
| 1931 | - } | 1962 | +} |
| 1932 | - } | ||
| 1933 | - } | ||
| 1934 | 1963 | ||
| 1935 | - .pagination-wrapper { | 1964 | +/* 分页样式 */ |
| 1965 | +.table-footer { | ||
| 1936 | display: flex; | 1966 | display: flex; |
| 1937 | justify-content: space-between; | 1967 | justify-content: space-between; |
| 1938 | align-items: center; | 1968 | align-items: center; |
| 1939 | - padding: 12px 20px; | 1969 | + padding: 8px 16px; |
| 1940 | - border-top: 1px solid #f0f0f0; | 1970 | + background: #f8f9fa; |
| 1941 | - background: #fafafa; | 1971 | + border-top: 1px solid #e0e0e0; |
| 1972 | +} | ||
| 1942 | 1973 | ||
| 1943 | - .pagination-info { | 1974 | +.pagination-left { |
| 1944 | - font-size: 14px; | 1975 | + display: flex; |
| 1976 | + align-items: center; | ||
| 1977 | + gap: 16px; | ||
| 1978 | +} | ||
| 1979 | + | ||
| 1980 | +.pagination-info { | ||
| 1981 | + font-size: 12px; | ||
| 1945 | color: #666; | 1982 | color: #666; |
| 1946 | - } | 1983 | +} |
| 1947 | 1984 | ||
| 1948 | - .pagination-controls { | 1985 | +.page-size-selector { |
| 1949 | display: flex; | 1986 | display: flex; |
| 1987 | + align-items: center; | ||
| 1950 | gap: 4px; | 1988 | gap: 4px; |
| 1989 | + font-size: 11px; | ||
| 1990 | + color: #666; | ||
| 1991 | +} | ||
| 1992 | + | ||
| 1993 | +.page-size-select { | ||
| 1994 | + padding: 2px 4px; | ||
| 1995 | + border: 1px solid #d9d9d9; | ||
| 1996 | + border-radius: 3px; | ||
| 1997 | + font-size: 10px; | ||
| 1998 | + background: white; | ||
| 1999 | +} | ||
| 2000 | + | ||
| 2001 | +.pagination-container { | ||
| 2002 | + display: flex; | ||
| 1951 | align-items: center; | 2003 | align-items: center; |
| 2004 | + background: #f8f9fa; | ||
| 2005 | + border: 1px solid #e9ecef; | ||
| 2006 | + border-radius: 8px; | ||
| 2007 | + overflow: hidden; | ||
| 2008 | + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); | ||
| 2009 | +} | ||
| 1952 | 2010 | ||
| 1953 | - .page-btn { | 2011 | +.pagination-btn { |
| 1954 | - padding: 6px 12px; | 2012 | + padding: 2px 6px; |
| 1955 | - border: 1px solid #ddd; | 2013 | + border: none; |
| 1956 | - border-radius: 4px; | ||
| 1957 | background: white; | 2014 | background: white; |
| 2015 | + color: #6c757d; | ||
| 2016 | + font-size: 11px; | ||
| 2017 | + font-weight: 500; | ||
| 1958 | cursor: pointer; | 2018 | cursor: pointer; |
| 1959 | - font-size: 14px; | 2019 | + transition: all 0.2s; |
| 1960 | - transition: all 0.3s; | 2020 | + border-right: 1px solid #e9ecef; |
| 1961 | - min-width: 32px; | 2021 | + height: 20px; |
| 2022 | +} | ||
| 1962 | 2023 | ||
| 1963 | - &:hover:not(:disabled) { | 2024 | +.pagination-btn:hover:not(:disabled) { |
| 1964 | - background: #f5f7fa; | 2025 | + background: #e9ecef; |
| 1965 | - border-color: #409eff; | 2026 | + color: #495057; |
| 1966 | - } | 2027 | +} |
| 1967 | 2028 | ||
| 1968 | - &:disabled { | 2029 | +.pagination-btn:disabled { |
| 1969 | opacity: 0.5; | 2030 | opacity: 0.5; |
| 1970 | cursor: not-allowed; | 2031 | cursor: not-allowed; |
| 1971 | - } | 2032 | +} |
| 1972 | - | ||
| 1973 | - &.active { | ||
| 1974 | - background: #409eff; | ||
| 1975 | - color: white; | ||
| 1976 | - border-color: #409eff; | ||
| 1977 | - } | ||
| 1978 | - } | ||
| 1979 | - } | ||
| 1980 | 2033 | ||
| 1981 | - .pagination-jump { | 2034 | +.pagination-pages { |
| 1982 | display: flex; | 2035 | display: flex; |
| 1983 | - align-items: center; | 2036 | +} |
| 1984 | - gap: 8px; | ||
| 1985 | - font-size: 14px; | ||
| 1986 | - color: #666; | ||
| 1987 | 2037 | ||
| 1988 | - .jump-input { | 2038 | +.page-number { |
| 1989 | - width: 50px; | 2039 | + padding: 2px 6px; |
| 1990 | - padding: 4px 8px; | 2040 | + border: none; |
| 1991 | - border: 1px solid #ddd; | 2041 | + background: white; |
| 1992 | - border-radius: 4px; | 2042 | + color: #6c757d; |
| 2043 | + font-size: 11px; | ||
| 2044 | + font-weight: 500; | ||
| 2045 | + cursor: pointer; | ||
| 2046 | + transition: all 0.2s; | ||
| 2047 | + border-right: 1px solid #e9ecef; | ||
| 2048 | + min-width: 28px; | ||
| 1993 | text-align: center; | 2049 | text-align: center; |
| 1994 | - font-size: 14px; | 2050 | + height: 20px; |
| 2051 | +} | ||
| 1995 | 2052 | ||
| 1996 | - &:focus { | 2053 | +.page-number:hover { |
| 1997 | - outline: none; | 2054 | + background: #e9ecef; |
| 1998 | - border-color: #409eff; | 2055 | + color: #495057; |
| 1999 | - } | 2056 | +} |
| 2000 | - } | 2057 | + |
| 2001 | - } | 2058 | +.page-number.active { |
| 2002 | - } | 2059 | + background: #1890ff; |
| 2060 | + color: white; | ||
| 2061 | + font-weight: 600; | ||
| 2003 | } | 2062 | } |
| 2004 | 2063 | ||
| 2005 | .status-tag { | 2064 | .status-tag { |
| ... | @@ -2049,7 +2108,7 @@ onMounted(async () => { | ... | @@ -2049,7 +2108,7 @@ onMounted(async () => { |
| 2049 | overflow-y: auto; | 2108 | overflow-y: auto; |
| 2050 | } | 2109 | } |
| 2051 | 2110 | ||
| 2052 | -// 响应式设计 | 2111 | +/* 响应式设计 */ |
| 2053 | @media (max-width: 1200px) { | 2112 | @media (max-width: 1200px) { |
| 2054 | .charts-container { | 2113 | .charts-container { |
| 2055 | grid-template-columns: 1fr; | 2114 | grid-template-columns: 1fr; |
| ... | @@ -2071,6 +2130,5 @@ onMounted(async () => { | ... | @@ -2071,6 +2130,5 @@ onMounted(async () => { |
| 2071 | margin-top: 12px; | 2130 | margin-top: 12px; |
| 2072 | } | 2131 | } |
| 2073 | } | 2132 | } |
| 2074 | - | ||
| 2075 | } | 2133 | } |
| 2076 | </style> | 2134 | </style> | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | <div class="log-management"> | 2 | <div class="log-management"> |
| 3 | - <!-- 页面标题 --> | 3 | + <!-- 搜索筛选区域 --> |
| 4 | - <div class="page-header"> | 4 | + <div class="search-section"> |
| 5 | - <h2>日志管理</h2> | 5 | + <div class="search-row"> |
| 6 | - <p>系统操作日志查询与管理</p> | 6 | + <div class="search-item"> |
| 7 | - </div> | 7 | + <label>日志类型:</label> |
| 8 | - | 8 | + <select v-model="searchForm.logType" class="search-select"> |
| 9 | - <!-- 搜索表单 --> | 9 | + <option value="">全部</option> |
| 10 | - <el-card class="search-card" shadow="never"> | 10 | + <option value="0">操作日志</option> |
| 11 | - <el-form :model="searchForm" :inline="true" class="search-form"> | 11 | + <option value="1">异常日志</option> |
| 12 | - <el-form-item label="日志类型"> | 12 | + <option value="2">登录日志</option> |
| 13 | - <el-select v-model="searchForm.logType" placeholder="请选择日志类型" clearable style="width: 200px"> | 13 | + </select> |
| 14 | - <el-option label="操作日志" value="0" /> | 14 | + </div> |
| 15 | - <el-option label="异常日志" value="1" /> | 15 | + <div class="search-item"> |
| 16 | - <el-option label="登录日志" value="2" /> | 16 | + <label>用户名:</label> |
| 17 | - </el-select> | 17 | + <input |
| 18 | - </el-form-item> | ||
| 19 | - | ||
| 20 | - <el-form-item label="用户名"> | ||
| 21 | - <el-input | ||
| 22 | v-model="searchForm.username" | 18 | v-model="searchForm.username" |
| 19 | + type="text" | ||
| 23 | placeholder="请输入用户名" | 20 | placeholder="请输入用户名" |
| 24 | - clearable | 21 | + class="search-input" |
| 25 | - style="width: 200px" | ||
| 26 | /> | 22 | /> |
| 27 | - </el-form-item> | 23 | + </div> |
| 28 | - | 24 | + <div class="search-item"> |
| 29 | - <el-form-item label="操作模块"> | 25 | + <label>操作模块:</label> |
| 30 | - <el-input | 26 | + <input |
| 31 | v-model="searchForm.module" | 27 | v-model="searchForm.module" |
| 28 | + type="text" | ||
| 32 | placeholder="请输入操作模块" | 29 | placeholder="请输入操作模块" |
| 33 | - clearable | 30 | + class="search-input" |
| 34 | - style="width: 200px" | ||
| 35 | /> | 31 | /> |
| 36 | - </el-form-item> | 32 | + </div> |
| 37 | - | 33 | + <div class="search-item"> |
| 38 | - <el-form-item label="操作结果"> | 34 | + <label>操作结果:</label> |
| 39 | - <el-select v-model="searchForm.status" placeholder="请选择操作结果" clearable style="width: 200px"> | 35 | + <select v-model="searchForm.status" class="search-select"> |
| 40 | - <el-option label="成功" :value="1" /> | 36 | + <option value="">全部</option> |
| 41 | - <el-option label="失败" :value="0" /> | 37 | + <option value="1">成功</option> |
| 42 | - </el-select> | 38 | + <option value="0">失败</option> |
| 43 | - </el-form-item> | 39 | + </select> |
| 44 | - | 40 | + </div> |
| 45 | - <el-form-item label="时间范围"> | 41 | + <div class="search-item"> |
| 46 | - <el-date-picker | 42 | + <label>时间范围:</label> |
| 47 | - v-model="dateRange" | 43 | + <input |
| 48 | - type="datetimerange" | 44 | + v-model="dateRangeText" |
| 49 | - range-separator="至" | 45 | + type="text" |
| 50 | - start-placeholder="开始时间" | 46 | + placeholder="请选择时间范围" |
| 51 | - end-placeholder="结束时间" | 47 | + class="search-input" |
| 52 | - format="YYYY-MM-DD HH:mm:ss" | 48 | + @click="showDatePicker = true" |
| 53 | - value-format="YYYY-MM-DD HH:mm:ss" | 49 | + readonly |
| 54 | - @change="handleDateRangeChange" | ||
| 55 | - style="width: 350px" | ||
| 56 | /> | 50 | /> |
| 57 | - </el-form-item> | 51 | + </div> |
| 58 | - | 52 | + <div class="search-actions"> |
| 59 | - <el-form-item> | 53 | + <button @click="handleSearch" class="search-btn">🔍 搜索</button> |
| 60 | - <el-button type="primary" @click="handleSearch" :loading="loading"> | 54 | + <button @click="handleReset" class="reset-btn">🔄 重置</button> |
| 61 | - <el-icon><Search /></el-icon> | 55 | + </div> |
| 62 | - 搜索 | 56 | + </div> |
| 63 | - </el-button> | 57 | + </div> |
| 64 | - <el-button @click="handleReset"> | 58 | + |
| 65 | - <el-icon><Refresh /></el-icon> | 59 | + <!-- 操作按钮区域 --> |
| 66 | - 重置 | 60 | + <div class="action-section"> |
| 67 | - </el-button> | 61 | + <div class="action-buttons"> |
| 68 | - </el-form-item> | 62 | + <button |
| 69 | - </el-form> | ||
| 70 | - </el-card> | ||
| 71 | - | ||
| 72 | - <!-- 操作按钮 --> | ||
| 73 | - <el-card class="toolbar-card" shadow="never"> | ||
| 74 | - <div class="toolbar"> | ||
| 75 | - <div class="toolbar-left"> | ||
| 76 | - <el-button | ||
| 77 | - type="danger" | ||
| 78 | - :disabled="selectedLogs.length === 0" | ||
| 79 | @click="handleBatchDelete" | 63 | @click="handleBatchDelete" |
| 64 | + :disabled="selectedLogs.length === 0" | ||
| 65 | + class="action-btn danger" | ||
| 80 | > | 66 | > |
| 81 | - <el-icon><Delete /></el-icon> | 67 | + 🗑️ 批量删除 |
| 82 | - 批量删除 | 68 | + </button> |
| 83 | - </el-button> | 69 | + <button @click="handleCleanExpired" class="action-btn warning"> |
| 84 | - <el-button type="warning" @click="handleCleanExpired"> | 70 | + 🧹 清理过期日志 |
| 85 | - <el-icon><Delete /></el-icon> | 71 | + </button> |
| 86 | - 清理过期日志 | 72 | + <button @click="handleExport" class="action-btn info"> |
| 87 | - </el-button> | 73 | + 📋 导出日志 |
| 88 | - <el-button type="info" @click="handleExport"> | 74 | + </button> |
| 89 | - <el-icon><Download /></el-icon> | 75 | + </div> |
| 90 | - 导出日志 | 76 | + </div> |
| 91 | - </el-button> | ||
| 92 | - </div> | ||
| 93 | - <div class="toolbar-right"> | ||
| 94 | - <el-button @click="handleRefresh" :loading="loading"> | ||
| 95 | - <el-icon><Refresh /></el-icon> | ||
| 96 | - 刷新 | ||
| 97 | - </el-button> | ||
| 98 | - </div> | ||
| 99 | - </div> | ||
| 100 | - </el-card> | ||
| 101 | - | ||
| 102 | - <!-- 日志列表 --> | ||
| 103 | - <el-card class="table-card" shadow="never"> | ||
| 104 | - | ||
| 105 | - <el-table | ||
| 106 | - v-loading="loading" | ||
| 107 | - :data="logList" | ||
| 108 | - @selection-change="handleSelectionChange" | ||
| 109 | - stripe | ||
| 110 | - border | ||
| 111 | - style="width: 100%" | ||
| 112 | - > | ||
| 113 | - <el-table-column type="selection" width="50" align="center" /> | ||
| 114 | - | ||
| 115 | - <el-table-column prop="logId" label="日志ID" width="80" align="center" /> | ||
| 116 | - | ||
| 117 | - <el-table-column prop="logTypeText" label="日志类型" width="100" align="center"> | ||
| 118 | - <template #default="{ row }"> | ||
| 119 | - <el-tag :type="getLogTypeTagType(row.logType)" size="small"> | ||
| 120 | - {{ row.logTypeText }} | ||
| 121 | - </el-tag> | ||
| 122 | - </template> | ||
| 123 | - </el-table-column> | ||
| 124 | - | ||
| 125 | - <el-table-column prop="username" label="用户名" width="100" align="center" /> | ||
| 126 | - | ||
| 127 | - <el-table-column prop="module" label="操作模块" width="120" align="center" /> | ||
| 128 | - | ||
| 129 | - <el-table-column prop="operation" label="操作内容" min-width="180" show-overflow-tooltip /> | ||
| 130 | - | ||
| 131 | - <el-table-column prop="ip" label="IP地址" width="120" align="center" /> | ||
| 132 | - | ||
| 133 | - <el-table-column prop="logTime" label="操作时间" width="160" align="center"> | ||
| 134 | - <template #default="{ row }"> | ||
| 135 | - {{ formatDate(row.logTime) }} | ||
| 136 | - </template> | ||
| 137 | - </el-table-column> | ||
| 138 | - | ||
| 139 | - <el-table-column prop="statusText" label="操作结果" width="100" align="center"> | ||
| 140 | - <template #default="{ row }"> | ||
| 141 | - <el-tag :type="row.status === 1 ? 'success' : 'danger'" size="small"> | ||
| 142 | - {{ row.statusText }} | ||
| 143 | - </el-tag> | ||
| 144 | - </template> | ||
| 145 | - </el-table-column> | ||
| 146 | - | ||
| 147 | - <el-table-column label="操作" width="140" fixed="right" align="center"> | ||
| 148 | - <template #default="{ row }"> | ||
| 149 | - <el-button | ||
| 150 | - type="primary" | ||
| 151 | - size="small" | ||
| 152 | - @click="handleViewDetail(row)" | ||
| 153 | - link | ||
| 154 | - > | ||
| 155 | - 详情 | ||
| 156 | - </el-button> | ||
| 157 | - <el-button | ||
| 158 | - type="danger" | ||
| 159 | - size="small" | ||
| 160 | - @click="handleDelete(row)" | ||
| 161 | - link | ||
| 162 | - > | ||
| 163 | - 删除 | ||
| 164 | - </el-button> | ||
| 165 | - </template> | ||
| 166 | - </el-table-column> | ||
| 167 | - </el-table> | ||
| 168 | 77 | ||
| 169 | - <!-- 分页 --> | 78 | + <!-- 数据表格 --> |
| 170 | - <div class="pagination-container"> | 79 | + <div class="table-section"> |
| 171 | - <el-pagination | 80 | + <div class="table-header"> |
| 172 | - v-model:current-page="pagination.pageNum" | 81 | + <div class="table-controls"> |
| 173 | - v-model:page-size="pagination.pageSize" | 82 | + <button @click="handleTableSearch" class="control-btn" title="搜索">🔍</button> |
| 174 | - :page-sizes="[10, 20, 50, 100]" | 83 | + <button @click="handleTableRefresh" class="control-btn" title="刷新">🔄</button> |
| 175 | - :total="pagination.total" | 84 | + <button @click="handleTableExport" class="control-btn" title="导出">📋</button> |
| 176 | - layout="total, sizes, prev, pager, next, jumper" | 85 | + <button @click="handleTableViewToggle" class="control-btn" title="视图切换">⊞</button> |
| 177 | - @size-change="handleSizeChange" | 86 | + </div> |
| 178 | - @current-change="handleCurrentChange" | 87 | + </div> |
| 88 | + | ||
| 89 | + <div class="table-container"> | ||
| 90 | + <table class="data-table"> | ||
| 91 | + <thead> | ||
| 92 | + <tr> | ||
| 93 | + <th class="checkbox-col"> | ||
| 94 | + <input | ||
| 95 | + type="checkbox" | ||
| 96 | + :checked="selectAll" | ||
| 97 | + @change="handleSelectAll" | ||
| 98 | + class="table-checkbox" | ||
| 179 | /> | 99 | /> |
| 100 | + </th> | ||
| 101 | + <th>日志ID</th> | ||
| 102 | + <th>日志类型</th> | ||
| 103 | + <th>用户名</th> | ||
| 104 | + <th>操作模块</th> | ||
| 105 | + <th>操作内容</th> | ||
| 106 | + <th>IP地址</th> | ||
| 107 | + <th>操作时间</th> | ||
| 108 | + <th>操作结果</th> | ||
| 109 | + <th>操作</th> | ||
| 110 | + </tr> | ||
| 111 | + </thead> | ||
| 112 | + <tbody> | ||
| 113 | + <tr v-for="log in logList" :key="log.logId"> | ||
| 114 | + <td class="checkbox-col"> | ||
| 115 | + <input | ||
| 116 | + type="checkbox" | ||
| 117 | + :checked="selectedLogs.includes(log.logId)" | ||
| 118 | + @change="handleSelectLog(log.logId)" | ||
| 119 | + class="table-checkbox" | ||
| 120 | + /> | ||
| 121 | + </td> | ||
| 122 | + <td>{{ log.logId }}</td> | ||
| 123 | + <td> | ||
| 124 | + <span :class="['status-badge', getLogTypeClass(log.logType)]"> | ||
| 125 | + {{ log.logTypeText }} | ||
| 126 | + </span> | ||
| 127 | + </td> | ||
| 128 | + <td>{{ log.username }}</td> | ||
| 129 | + <td>{{ log.module }}</td> | ||
| 130 | + <td class="operation-cell" :title="log.operation">{{ log.operation }}</td> | ||
| 131 | + <td>{{ log.ip }}</td> | ||
| 132 | + <td>{{ formatDate(log.logTime) }}</td> | ||
| 133 | + <td> | ||
| 134 | + <span :class="['status-badge', log.status === 1 ? 'success' : 'error']"> | ||
| 135 | + {{ log.statusText }} | ||
| 136 | + </span> | ||
| 137 | + </td> | ||
| 138 | + <td class="action-col"> | ||
| 139 | + <button @click="handleViewDetail(log)" class="action-btn primary" title="查看">👁️</button> | ||
| 140 | + <button @click="handleDelete(log)" class="action-btn danger" title="删除">🗑️</button> | ||
| 141 | + </td> | ||
| 142 | + </tr> | ||
| 143 | + </tbody> | ||
| 144 | + </table> | ||
| 145 | + </div> | ||
| 180 | </div> | 146 | </div> |
| 181 | - </el-card> | ||
| 182 | 147 | ||
| 183 | - <!-- 日志详情对话框 --> | 148 | + <!-- 分页 --> |
| 184 | - <el-dialog | 149 | + <div class="pagination-section"> |
| 185 | - v-model="detailDialogVisible" | 150 | + <div class="pagination-info"> |
| 186 | - title="日志详情" | 151 | + 共 {{ pagination.total }} 条记录,第 {{ pagination.pageNum }} / {{ Math.ceil(pagination.total / pagination.pageSize) }} 页 |
| 187 | - width="800px" | 152 | + </div> |
| 188 | - :close-on-click-modal="false" | 153 | + <div class="pagination-controls"> |
| 154 | + <button | ||
| 155 | + @click="handlePrevPage" | ||
| 156 | + :disabled="pagination.pageNum <= 1" | ||
| 157 | + class="pagination-btn" | ||
| 158 | + > | ||
| 159 | + ← 上一页 | ||
| 160 | + </button> | ||
| 161 | + <span class="page-info">{{ pagination.pageNum }} / {{ Math.ceil(pagination.total / pagination.pageSize) }}</span> | ||
| 162 | + <button | ||
| 163 | + @click="handleNextPage" | ||
| 164 | + :disabled="pagination.pageNum >= Math.ceil(pagination.total / pagination.pageSize)" | ||
| 165 | + class="pagination-btn" | ||
| 189 | > | 166 | > |
| 190 | - <div v-if="logDetail" class="log-detail"> | 167 | + 下一页 → |
| 191 | - <el-descriptions :column="2" border> | 168 | + </button> |
| 192 | - <el-descriptions-item label="日志ID"> | 169 | + <select v-model="pagination.pageSize" @change="handleSizeChange($event)" class="page-size-select"> |
| 193 | - {{ logDetail.logId }} | 170 | + <option value="10">10条/页</option> |
| 194 | - </el-descriptions-item> | 171 | + <option value="20">20条/页</option> |
| 195 | - <el-descriptions-item label="日志类型"> | 172 | + <option value="50">50条/页</option> |
| 196 | - <el-tag :type="getLogTypeTagType(logDetail.logType)"> | 173 | + <option value="100">100条/页</option> |
| 197 | - {{ logDetail.logTypeText }} | 174 | + </select> |
| 198 | - </el-tag> | 175 | + </div> |
| 199 | - </el-descriptions-item> | 176 | + </div> |
| 200 | - <el-descriptions-item label="用户名"> | 177 | + |
| 201 | - {{ logDetail.username }} | 178 | + <!-- 日志详情对话框 --> |
| 202 | - </el-descriptions-item> | 179 | + <div v-if="detailDialogVisible" class="dialog-overlay" @click="closeDetailDialog"> |
| 203 | - <el-descriptions-item label="用户ID"> | 180 | + <div class="dialog-content" @click.stop> |
| 204 | - {{ logDetail.userId }} | 181 | + <div class="dialog-header"> |
| 205 | - </el-descriptions-item> | 182 | + <h3>日志详情</h3> |
| 206 | - <el-descriptions-item label="操作模块"> | 183 | + <button @click="closeDetailDialog" class="close-btn">×</button> |
| 207 | - {{ logDetail.module }} | 184 | + </div> |
| 208 | - </el-descriptions-item> | 185 | + <div class="dialog-body"> |
| 209 | - <el-descriptions-item label="操作内容"> | 186 | + <div class="log-detail"> |
| 210 | - {{ logDetail.operation }} | 187 | + <div class="detail-grid"> |
| 211 | - </el-descriptions-item> | 188 | + <div class="detail-item"> |
| 212 | - <el-descriptions-item label="IP地址"> | 189 | + <label>日志ID:</label> |
| 213 | - {{ logDetail.ip }} | 190 | + <span>{{ logDetail?.logId }}</span> |
| 214 | - </el-descriptions-item> | 191 | + </div> |
| 215 | - <el-descriptions-item label="操作时间"> | 192 | + <div class="detail-item"> |
| 216 | - {{ formatDate(logDetail.logTime) }} | 193 | + <label>日志类型:</label> |
| 217 | - </el-descriptions-item> | 194 | + <span :class="['status-badge', getLogTypeClass(logDetail?.logType || '')]"> |
| 218 | - <el-descriptions-item label="操作结果"> | 195 | + {{ logDetail?.logTypeText }} |
| 219 | - <el-tag :type="logDetail.status === 1 ? 'success' : 'danger'"> | 196 | + </span> |
| 220 | - {{ logDetail.statusText }} | 197 | + </div> |
| 221 | - </el-tag> | 198 | + <div class="detail-item"> |
| 222 | - </el-descriptions-item> | 199 | + <label>用户ID:</label> |
| 223 | - <el-descriptions-item label="错误信息" v-if="logDetail.errorMsg"> | 200 | + <span>{{ logDetail?.userId }}</span> |
| 224 | - {{ logDetail.errorMsg }} | 201 | + </div> |
| 225 | - </el-descriptions-item> | 202 | + <div class="detail-item"> |
| 226 | - </el-descriptions> | 203 | + <label>用户名:</label> |
| 227 | - | 204 | + <span>{{ logDetail?.username }}</span> |
| 228 | - <div v-if="logDetail.requestParam" class="request-param"> | 205 | + </div> |
| 206 | + <div class="detail-item"> | ||
| 207 | + <label>操作模块:</label> | ||
| 208 | + <span>{{ logDetail?.module }}</span> | ||
| 209 | + </div> | ||
| 210 | + <div class="detail-item"> | ||
| 211 | + <label>操作内容:</label> | ||
| 212 | + <span>{{ logDetail?.operation }}</span> | ||
| 213 | + </div> | ||
| 214 | + <div class="detail-item"> | ||
| 215 | + <label>IP地址:</label> | ||
| 216 | + <span>{{ logDetail?.ip }}</span> | ||
| 217 | + </div> | ||
| 218 | + <div class="detail-item"> | ||
| 219 | + <label>操作时间:</label> | ||
| 220 | + <span>{{ formatDate(logDetail?.logTime || '') }}</span> | ||
| 221 | + </div> | ||
| 222 | + <div class="detail-item"> | ||
| 223 | + <label>操作结果:</label> | ||
| 224 | + <span :class="['status-badge', logDetail?.status === 1 ? 'success' : 'error']"> | ||
| 225 | + {{ logDetail?.statusText }} | ||
| 226 | + </span> | ||
| 227 | + </div> | ||
| 228 | + <div v-if="logDetail?.errorMsg" class="detail-item full-width"> | ||
| 229 | + <label>错误信息:</label> | ||
| 230 | + <span>{{ logDetail.errorMsg }}</span> | ||
| 231 | + </div> | ||
| 232 | + </div> | ||
| 233 | + | ||
| 234 | + <div v-if="logDetail?.requestParam" class="request-param"> | ||
| 229 | <h4>请求参数</h4> | 235 | <h4>请求参数</h4> |
| 230 | - <el-input | 236 | + <textarea |
| 231 | - v-model="logDetail.requestParam" | 237 | + :value="logDetail.requestParam" |
| 232 | - type="textarea" | ||
| 233 | - :rows="6" | ||
| 234 | readonly | 238 | readonly |
| 235 | placeholder="无请求参数" | 239 | placeholder="无请求参数" |
| 236 | - /> | 240 | + class="request-textarea" |
| 241 | + rows="6" | ||
| 242 | + ></textarea> | ||
| 243 | + </div> | ||
| 244 | + </div> | ||
| 245 | + </div> | ||
| 246 | + <div class="dialog-footer"> | ||
| 247 | + <button @click="closeDetailDialog" class="action-btn primary">关闭</button> | ||
| 248 | + </div> | ||
| 237 | </div> | 249 | </div> |
| 238 | </div> | 250 | </div> |
| 239 | - | ||
| 240 | - <template #footer> | ||
| 241 | - <el-button @click="detailDialogVisible = false">关闭</el-button> | ||
| 242 | - </template> | ||
| 243 | - </el-dialog> | ||
| 244 | 251 | ||
| 245 | <!-- 清理过期日志对话框 --> | 252 | <!-- 清理过期日志对话框 --> |
| 246 | - <el-dialog | 253 | + <div v-if="cleanDialogVisible" class="dialog-overlay" @click="closeCleanDialog"> |
| 247 | - v-model="cleanDialogVisible" | 254 | + <div class="dialog-content" @click.stop> |
| 248 | - title="清理过期日志" | 255 | + <div class="dialog-header"> |
| 249 | - width="400px" | 256 | + <h3>清理过期日志</h3> |
| 250 | - :close-on-click-modal="false" | 257 | + <button @click="closeCleanDialog" class="close-btn">×</button> |
| 251 | - > | 258 | + </div> |
| 252 | - <el-form :model="cleanForm" label-width="100px"> | 259 | + <div class="dialog-body"> |
| 253 | - <el-form-item label="过期天数"> | 260 | + <form @submit.prevent="handleConfirmClean" class="clean-form"> |
| 254 | - <el-input-number | 261 | + <div class="form-group"> |
| 255 | - v-model="cleanForm.expireDays" | 262 | + <label class="required">过期天数:</label> |
| 256 | - :min="1" | 263 | + <input |
| 257 | - :max="365" | 264 | + v-model.number="cleanForm.expireDays" |
| 265 | + type="number" | ||
| 266 | + min="1" | ||
| 267 | + max="365" | ||
| 258 | placeholder="请输入过期天数" | 268 | placeholder="请输入过期天数" |
| 259 | - style="width: 100%" | 269 | + class="form-input" |
| 270 | + required | ||
| 260 | /> | 271 | /> |
| 261 | - </el-form-item> | 272 | + </div> |
| 262 | - <el-form-item> | 273 | + <div class="warning-text"> |
| 263 | - <el-text type="warning"> | 274 | + ⚠️ 将删除指定天数之前的所有日志记录,此操作不可恢复! |
| 264 | - 将删除指定天数之前的所有日志记录,此操作不可恢复! | 275 | + </div> |
| 265 | - </el-text> | 276 | + </form> |
| 266 | - </el-form-item> | 277 | + </div> |
| 267 | - </el-form> | 278 | + <div class="dialog-footer"> |
| 268 | - | 279 | + <button @click="closeCleanDialog" class="action-btn">取消</button> |
| 269 | - <template #footer> | 280 | + <button @click="handleConfirmClean" :disabled="cleanLoading" class="action-btn danger"> |
| 270 | - <el-button @click="cleanDialogVisible = false">取消</el-button> | 281 | + {{ cleanLoading ? '清理中...' : '确认清理' }} |
| 271 | - <el-button type="danger" @click="handleConfirmClean" :loading="cleanLoading"> | 282 | + </button> |
| 272 | - 确认清理 | 283 | + </div> |
| 273 | - </el-button> | 284 | + </div> |
| 274 | - </template> | 285 | + </div> |
| 275 | - </el-dialog> | ||
| 276 | </div> | 286 | </div> |
| 277 | </template> | 287 | </template> |
| 278 | 288 | ||
| ... | @@ -286,12 +296,15 @@ import { formatDate } from '@/utils/index' | ... | @@ -286,12 +296,15 @@ import { formatDate } from '@/utils/index' |
| 286 | // 响应式数据 | 296 | // 响应式数据 |
| 287 | const loading = ref(false) | 297 | const loading = ref(false) |
| 288 | const logList = ref<Log[]>([]) | 298 | const logList = ref<Log[]>([]) |
| 289 | -const selectedLogs = ref<Log[]>([]) | 299 | +const selectedLogs = ref<number[]>([]) |
| 290 | const detailDialogVisible = ref(false) | 300 | const detailDialogVisible = ref(false) |
| 291 | const logDetail = ref<LogDetail | null>(null) | 301 | const logDetail = ref<LogDetail | null>(null) |
| 292 | const cleanDialogVisible = ref(false) | 302 | const cleanDialogVisible = ref(false) |
| 293 | const cleanLoading = ref(false) | 303 | const cleanLoading = ref(false) |
| 294 | const dateRange = ref<[string, string] | null>(null) | 304 | const dateRange = ref<[string, string] | null>(null) |
| 305 | +const dateRangeText = ref('') | ||
| 306 | +const showDatePicker = ref(false) | ||
| 307 | +const selectAll = ref(false) | ||
| 295 | 308 | ||
| 296 | // 搜索表单 | 309 | // 搜索表单 |
| 297 | const searchForm = reactive<LogSearchParams>({ | 310 | const searchForm = reactive<LogSearchParams>({ |
| ... | @@ -337,6 +350,70 @@ const handleExport = () => { | ... | @@ -337,6 +350,70 @@ const handleExport = () => { |
| 337 | ElMessage.info('导出功能开发中...') | 350 | ElMessage.info('导出功能开发中...') |
| 338 | } | 351 | } |
| 339 | 352 | ||
| 353 | +// 全选/取消全选 | ||
| 354 | +const handleSelectAll = () => { | ||
| 355 | + selectAll.value = !selectAll.value | ||
| 356 | + if (selectAll.value) { | ||
| 357 | + selectedLogs.value = logList.value.map(log => log.logId) | ||
| 358 | + } else { | ||
| 359 | + selectedLogs.value = [] | ||
| 360 | + } | ||
| 361 | +} | ||
| 362 | + | ||
| 363 | +// 选择单个日志 | ||
| 364 | +const handleSelectLog = (logId: number) => { | ||
| 365 | + const index = selectedLogs.value.indexOf(logId) | ||
| 366 | + if (index > -1) { | ||
| 367 | + selectedLogs.value.splice(index, 1) | ||
| 368 | + } else { | ||
| 369 | + selectedLogs.value.push(logId) | ||
| 370 | + } | ||
| 371 | + selectAll.value = selectedLogs.value.length === logList.value.length | ||
| 372 | +} | ||
| 373 | + | ||
| 374 | +// 获取日志类型样式类 | ||
| 375 | +const getLogTypeClass = (logType: string) => { | ||
| 376 | + switch (logType) { | ||
| 377 | + case '0': return 'info' | ||
| 378 | + case '1': return 'warning' | ||
| 379 | + case '2': return 'success' | ||
| 380 | + default: return 'default' | ||
| 381 | + } | ||
| 382 | +} | ||
| 383 | + | ||
| 384 | +// 表格控制按钮 | ||
| 385 | +const handleTableSearch = () => { | ||
| 386 | + handleSearch() | ||
| 387 | +} | ||
| 388 | + | ||
| 389 | +const handleTableRefresh = () => { | ||
| 390 | + handleRefresh() | ||
| 391 | +} | ||
| 392 | + | ||
| 393 | +const handleTableExport = () => { | ||
| 394 | + handleExport() | ||
| 395 | +} | ||
| 396 | + | ||
| 397 | +const handleTableViewToggle = () => { | ||
| 398 | + ElMessage.info('视图切换功能开发中...') | ||
| 399 | +} | ||
| 400 | + | ||
| 401 | +// 分页控制 | ||
| 402 | +const handlePrevPage = () => { | ||
| 403 | + if (pagination.pageNum > 1) { | ||
| 404 | + pagination.pageNum-- | ||
| 405 | + getLogList() | ||
| 406 | + } | ||
| 407 | +} | ||
| 408 | + | ||
| 409 | +const handleNextPage = () => { | ||
| 410 | + const totalPages = Math.ceil(pagination.total / pagination.pageSize) | ||
| 411 | + if (pagination.pageNum < totalPages) { | ||
| 412 | + pagination.pageNum++ | ||
| 413 | + getLogList() | ||
| 414 | + } | ||
| 415 | +} | ||
| 416 | + | ||
| 340 | // 获取日志列表 | 417 | // 获取日志列表 |
| 341 | const getLogList = async () => { | 418 | const getLogList = async () => { |
| 342 | try { | 419 | try { |
| ... | @@ -402,8 +479,9 @@ const handleDateRangeChange = (value: [string, string] | null) => { | ... | @@ -402,8 +479,9 @@ const handleDateRangeChange = (value: [string, string] | null) => { |
| 402 | } | 479 | } |
| 403 | 480 | ||
| 404 | // 分页大小变化 | 481 | // 分页大小变化 |
| 405 | -const handleSizeChange = (size: number) => { | 482 | +const handleSizeChange = (event: Event) => { |
| 406 | - pagination.pageSize = size | 483 | + const target = event.target as HTMLSelectElement |
| 484 | + pagination.pageSize = parseInt(target.value) | ||
| 407 | pagination.pageNum = 1 | 485 | pagination.pageNum = 1 |
| 408 | getLogList() | 486 | getLogList() |
| 409 | } | 487 | } |
| ... | @@ -416,7 +494,7 @@ const handleCurrentChange = (page: number) => { | ... | @@ -416,7 +494,7 @@ const handleCurrentChange = (page: number) => { |
| 416 | 494 | ||
| 417 | // 选择变化 | 495 | // 选择变化 |
| 418 | const handleSelectionChange = (selection: Log[]) => { | 496 | const handleSelectionChange = (selection: Log[]) => { |
| 419 | - selectedLogs.value = selection | 497 | + selectedLogs.value = selection.map(log => log.logId) |
| 420 | } | 498 | } |
| 421 | 499 | ||
| 422 | // 查看详情 | 500 | // 查看详情 |
| ... | @@ -481,7 +559,7 @@ const handleBatchDelete = async () => { | ... | @@ -481,7 +559,7 @@ const handleBatchDelete = async () => { |
| 481 | } | 559 | } |
| 482 | ) | 560 | ) |
| 483 | 561 | ||
| 484 | - const logIds = selectedLogs.value.map(log => log.logId) | 562 | + const logIds = selectedLogs.value |
| 485 | const response = await logApi.deleteLogs(logIds) | 563 | const response = await logApi.deleteLogs(logIds) |
| 486 | if (response.code === 200) { | 564 | if (response.code === 200) { |
| 487 | ElMessage.success('批量删除成功') | 565 | ElMessage.success('批量删除成功') |
| ... | @@ -502,6 +580,16 @@ const handleCleanExpired = () => { | ... | @@ -502,6 +580,16 @@ const handleCleanExpired = () => { |
| 502 | cleanDialogVisible.value = true | 580 | cleanDialogVisible.value = true |
| 503 | } | 581 | } |
| 504 | 582 | ||
| 583 | +// 关闭详情对话框 | ||
| 584 | +const closeDetailDialog = () => { | ||
| 585 | + detailDialogVisible.value = false | ||
| 586 | +} | ||
| 587 | + | ||
| 588 | +// 关闭清理对话框 | ||
| 589 | +const closeCleanDialog = () => { | ||
| 590 | + cleanDialogVisible.value = false | ||
| 591 | +} | ||
| 592 | + | ||
| 505 | // 确认清理 | 593 | // 确认清理 |
| 506 | const handleConfirmClean = async () => { | 594 | const handleConfirmClean = async () => { |
| 507 | if (!cleanForm.expireDays || cleanForm.expireDays <= 0) { | 595 | if (!cleanForm.expireDays || cleanForm.expireDays <= 0) { |
| ... | @@ -524,7 +612,7 @@ const handleConfirmClean = async () => { | ... | @@ -524,7 +612,7 @@ const handleConfirmClean = async () => { |
| 524 | const response = await logApi.cleanExpiredLogs(cleanForm.expireDays) | 612 | const response = await logApi.cleanExpiredLogs(cleanForm.expireDays) |
| 525 | if (response.code === 200) { | 613 | if (response.code === 200) { |
| 526 | ElMessage.success(response.data) | 614 | ElMessage.success(response.data) |
| 527 | - cleanDialogVisible.value = false | 615 | + closeCleanDialog() |
| 528 | getLogList() | 616 | getLogList() |
| 529 | } else { | 617 | } else { |
| 530 | ElMessage.error(response.message || '清理失败') | 618 | ElMessage.error(response.message || '清理失败') |
| ... | @@ -545,99 +633,505 @@ onMounted(() => { | ... | @@ -545,99 +633,505 @@ onMounted(() => { |
| 545 | }) | 633 | }) |
| 546 | </script> | 634 | </script> |
| 547 | 635 | ||
| 548 | -<style scoped> | 636 | +<style scoped lang="scss"> |
| 549 | .log-management { | 637 | .log-management { |
| 550 | - padding: 20px; | 638 | + background: #f5f5f5; |
| 639 | + min-height: 100vh; | ||
| 551 | } | 640 | } |
| 552 | 641 | ||
| 553 | -.page-header { | 642 | +// 搜索区域 |
| 554 | - margin-bottom: 20px; | 643 | +.search-section { |
| 644 | + background: white; | ||
| 645 | + padding: 16px; | ||
| 646 | + border-bottom: 1px solid #e0e0e0; | ||
| 555 | } | 647 | } |
| 556 | 648 | ||
| 557 | -.page-header h2 { | 649 | +.search-row { |
| 558 | - margin: 0 0 8px 0; | 650 | + display: flex; |
| 559 | - color: #303133; | 651 | + gap: 16px; |
| 560 | - font-size: 20px; | 652 | + align-items: center; |
| 653 | + flex-wrap: wrap; | ||
| 654 | +} | ||
| 655 | + | ||
| 656 | +.search-item { | ||
| 657 | + display: flex; | ||
| 658 | + align-items: center; | ||
| 659 | + gap: 8px; | ||
| 660 | +} | ||
| 661 | + | ||
| 662 | +.search-item label { | ||
| 663 | + font-size: 12px; | ||
| 664 | + color: #333; | ||
| 665 | + white-space: nowrap; | ||
| 666 | +} | ||
| 667 | + | ||
| 668 | +.search-input, .search-select { | ||
| 669 | + padding: 6px 8px; | ||
| 670 | + border: 1px solid #d9d9d9; | ||
| 671 | + border-radius: 4px; | ||
| 672 | + font-size: 12px; | ||
| 673 | + width: 120px; | ||
| 674 | +} | ||
| 675 | + | ||
| 676 | +.search-input:focus, .search-select:focus { | ||
| 677 | + outline: none; | ||
| 678 | + border-color: #1890ff; | ||
| 679 | +} | ||
| 680 | + | ||
| 681 | +.search-actions { | ||
| 682 | + display: flex; | ||
| 683 | + gap: 8px; | ||
| 684 | +} | ||
| 685 | + | ||
| 686 | +.search-btn { | ||
| 687 | + background: #1890ff; | ||
| 688 | + color: white; | ||
| 689 | + border: none; | ||
| 690 | + padding: 4px 8px; | ||
| 691 | + border-radius: 3px; | ||
| 692 | + font-size: 11px; | ||
| 693 | + cursor: pointer; | ||
| 694 | +} | ||
| 695 | + | ||
| 696 | +.search-btn:hover { | ||
| 697 | + background: #40a9ff; | ||
| 698 | +} | ||
| 699 | + | ||
| 700 | +.reset-btn { | ||
| 701 | + background: #ff7875; | ||
| 702 | + color: white; | ||
| 703 | + border: none; | ||
| 704 | + padding: 4px 8px; | ||
| 705 | + border-radius: 3px; | ||
| 706 | + font-size: 11px; | ||
| 707 | + cursor: pointer; | ||
| 708 | + height: 24px; | ||
| 709 | + display: flex; | ||
| 710 | + align-items: center; | ||
| 711 | +} | ||
| 712 | + | ||
| 713 | +.reset-btn:hover { | ||
| 714 | + background: #ff9c99; | ||
| 715 | +} | ||
| 716 | + | ||
| 717 | +// 操作区域 | ||
| 718 | +.action-section { | ||
| 719 | + background: white; | ||
| 720 | + padding: 12px 16px; | ||
| 721 | + border-bottom: 1px solid #e0e0e0; | ||
| 722 | +} | ||
| 723 | + | ||
| 724 | +.action-buttons { | ||
| 725 | + display: flex; | ||
| 726 | + gap: 6px; | ||
| 727 | +} | ||
| 728 | + | ||
| 729 | +.action-btn { | ||
| 730 | + padding: 4px 8px; | ||
| 731 | + border: none; | ||
| 732 | + border-radius: 3px; | ||
| 733 | + font-size: 11px; | ||
| 734 | + cursor: pointer; | ||
| 735 | + transition: all 0.2s; | ||
| 736 | +} | ||
| 737 | + | ||
| 738 | +.action-btn.primary { | ||
| 739 | + background: #1890ff; | ||
| 740 | + color: white; | ||
| 741 | +} | ||
| 742 | + | ||
| 743 | +.action-btn.primary:hover { | ||
| 744 | + background: #40a9ff; | ||
| 745 | +} | ||
| 746 | + | ||
| 747 | +.action-btn.danger { | ||
| 748 | + background: #ff4d4f; | ||
| 749 | + color: white; | ||
| 750 | +} | ||
| 751 | + | ||
| 752 | +.action-btn.danger:hover { | ||
| 753 | + background: #ff7875; | ||
| 754 | +} | ||
| 755 | + | ||
| 756 | +.action-btn.warning { | ||
| 757 | + background: #faad14; | ||
| 758 | + color: white; | ||
| 759 | +} | ||
| 760 | + | ||
| 761 | +.action-btn.warning:hover { | ||
| 762 | + background: #ffc53d; | ||
| 763 | +} | ||
| 764 | + | ||
| 765 | +.action-btn.info { | ||
| 766 | + background: #52c41a; | ||
| 767 | + color: white; | ||
| 768 | +} | ||
| 769 | + | ||
| 770 | +.action-btn.info:hover { | ||
| 771 | + background: #73d13d; | ||
| 772 | +} | ||
| 773 | + | ||
| 774 | +.action-btn:disabled { | ||
| 775 | + opacity: 0.5; | ||
| 776 | + cursor: not-allowed; | ||
| 777 | +} | ||
| 778 | + | ||
| 779 | +// 表格区域 | ||
| 780 | +.table-section { | ||
| 781 | + background: white; | ||
| 782 | + margin: 16px; | ||
| 783 | + border-radius: 6px; | ||
| 784 | + overflow: hidden; | ||
| 785 | + box-shadow: 0 2px 8px rgba(0,0,0,0.1); | ||
| 786 | +} | ||
| 787 | + | ||
| 788 | +.table-header { | ||
| 789 | + padding: 8px 16px; | ||
| 790 | + background: #fafafa; | ||
| 791 | + border-bottom: 1px solid #e0e0e0; | ||
| 792 | + display: flex; | ||
| 793 | + justify-content: flex-end; | ||
| 794 | +} | ||
| 795 | + | ||
| 796 | +.table-controls { | ||
| 797 | + display: flex; | ||
| 798 | + gap: 4px; | ||
| 799 | +} | ||
| 800 | + | ||
| 801 | +.control-btn { | ||
| 802 | + background: white; | ||
| 803 | + border: 1px solid #d9d9d9; | ||
| 804 | + border-radius: 3px; | ||
| 805 | + padding: 4px 8px; | ||
| 806 | + font-size: 11px; | ||
| 807 | + cursor: pointer; | ||
| 808 | + transition: all 0.2s; | ||
| 809 | +} | ||
| 810 | + | ||
| 811 | +.control-btn:hover { | ||
| 812 | + background: #f0f8ff; | ||
| 813 | + border-color: #1890ff; | ||
| 814 | + color: #1890ff; | ||
| 815 | +} | ||
| 816 | + | ||
| 817 | +.table-container { | ||
| 818 | + overflow-x: auto; | ||
| 819 | +} | ||
| 820 | + | ||
| 821 | +.data-table { | ||
| 822 | + width: 100%; | ||
| 823 | + border-collapse: collapse; | ||
| 824 | +} | ||
| 825 | + | ||
| 826 | +.data-table th, | ||
| 827 | +.data-table td { | ||
| 828 | + padding: 8px 12px; | ||
| 829 | + text-align: left; | ||
| 830 | + border-bottom: 1px solid #f0f0f0; | ||
| 831 | + font-size: 12px; | ||
| 832 | +} | ||
| 833 | + | ||
| 834 | +.data-table th { | ||
| 835 | + background: #fafafa; | ||
| 561 | font-weight: 600; | 836 | font-weight: 600; |
| 837 | + color: #333; | ||
| 838 | + white-space: nowrap; | ||
| 562 | } | 839 | } |
| 563 | 840 | ||
| 564 | -.page-header p { | 841 | +.data-table tbody tr:hover { |
| 565 | - margin: 0; | 842 | + background: #f5f7fa; |
| 566 | - color: #909399; | 843 | +} |
| 567 | - font-size: 14px; | 844 | + |
| 845 | +.checkbox-col { | ||
| 846 | + width: 50px; | ||
| 847 | + text-align: center; | ||
| 848 | +} | ||
| 849 | + | ||
| 850 | +.table-checkbox { | ||
| 851 | + width: 14px; | ||
| 852 | + height: 14px; | ||
| 853 | + cursor: pointer; | ||
| 854 | +} | ||
| 855 | + | ||
| 856 | +.operation-cell { | ||
| 857 | + max-width: 200px; | ||
| 858 | + overflow: hidden; | ||
| 859 | + text-overflow: ellipsis; | ||
| 860 | + white-space: nowrap; | ||
| 861 | +} | ||
| 862 | + | ||
| 863 | +.action-col { | ||
| 864 | + width: 120px; | ||
| 865 | + text-align: center; | ||
| 866 | +} | ||
| 867 | + | ||
| 868 | +.action-col .action-btn { | ||
| 869 | + padding: 2px 6px; | ||
| 870 | + margin-right: 4px; | ||
| 871 | + border: none; | ||
| 872 | + border-radius: 3px; | ||
| 873 | + font-size: 10px; | ||
| 874 | + cursor: pointer; | ||
| 875 | + display: inline-flex; | ||
| 876 | + align-items: center; | ||
| 877 | + gap: 2px; | ||
| 878 | +} | ||
| 879 | + | ||
| 880 | +// 状态标签 | ||
| 881 | +.status-badge { | ||
| 882 | + padding: 2px 6px; | ||
| 883 | + border-radius: 3px; | ||
| 884 | + font-size: 10px; | ||
| 885 | +} | ||
| 886 | + | ||
| 887 | +.status-badge.info { | ||
| 888 | + background: #e6f7ff; | ||
| 889 | + color: #1890ff; | ||
| 890 | +} | ||
| 891 | + | ||
| 892 | +.status-badge.warning { | ||
| 893 | + background: #fff7e6; | ||
| 894 | + color: #fa8c16; | ||
| 568 | } | 895 | } |
| 569 | 896 | ||
| 570 | -.search-card { | 897 | +.status-badge.success { |
| 571 | - margin-bottom: 16px; | 898 | + background: #f6ffed; |
| 899 | + color: #52c41a; | ||
| 572 | } | 900 | } |
| 573 | 901 | ||
| 574 | -.search-form { | 902 | +.status-badge.error { |
| 575 | - margin-bottom: 0; | 903 | + background: #fff2f0; |
| 904 | + color: #ff4d4f; | ||
| 576 | } | 905 | } |
| 577 | 906 | ||
| 578 | -.toolbar-card { | 907 | +.status-badge.default { |
| 579 | - margin-bottom: 16px; | 908 | + background: #f5f5f5; |
| 909 | + color: #8c8c8c; | ||
| 580 | } | 910 | } |
| 581 | 911 | ||
| 582 | -.toolbar { | 912 | +// 分页区域 |
| 913 | +.pagination-section { | ||
| 583 | display: flex; | 914 | display: flex; |
| 584 | justify-content: space-between; | 915 | justify-content: space-between; |
| 585 | align-items: center; | 916 | align-items: center; |
| 917 | + padding: 8px 16px; | ||
| 918 | + background: #fafafa; | ||
| 919 | + border-top: 1px solid #e0e0e0; | ||
| 920 | +} | ||
| 921 | + | ||
| 922 | +.pagination-info { | ||
| 923 | + font-size: 12px; | ||
| 924 | + color: #666; | ||
| 586 | } | 925 | } |
| 587 | 926 | ||
| 588 | -.toolbar-left, | 927 | +.pagination-controls { |
| 589 | -.toolbar-right { | ||
| 590 | display: flex; | 928 | display: flex; |
| 929 | + align-items: center; | ||
| 591 | gap: 8px; | 930 | gap: 8px; |
| 592 | } | 931 | } |
| 593 | 932 | ||
| 594 | -.table-card { | 933 | +.pagination-btn { |
| 595 | - margin-bottom: 16px; | 934 | + padding: 4px 8px; |
| 935 | + border: 1px solid #d9d9d9; | ||
| 936 | + border-radius: 3px; | ||
| 937 | + background: white; | ||
| 938 | + cursor: pointer; | ||
| 939 | + font-size: 11px; | ||
| 940 | + transition: all 0.2s; | ||
| 941 | +} | ||
| 942 | + | ||
| 943 | +.pagination-btn:hover:not(:disabled) { | ||
| 944 | + background: #f0f8ff; | ||
| 945 | + border-color: #1890ff; | ||
| 946 | + color: #1890ff; | ||
| 947 | +} | ||
| 948 | + | ||
| 949 | +.pagination-btn:disabled { | ||
| 950 | + opacity: 0.5; | ||
| 951 | + cursor: not-allowed; | ||
| 596 | } | 952 | } |
| 597 | 953 | ||
| 598 | -.pagination-container { | 954 | +.page-info { |
| 955 | + font-size: 12px; | ||
| 956 | + color: #666; | ||
| 957 | + padding: 0 8px; | ||
| 958 | +} | ||
| 959 | + | ||
| 960 | +.page-size-select { | ||
| 961 | + padding: 4px 8px; | ||
| 962 | + border: 1px solid #d9d9d9; | ||
| 963 | + border-radius: 4px; | ||
| 964 | + font-size: 12px; | ||
| 965 | + background: white; | ||
| 966 | + cursor: pointer; | ||
| 967 | +} | ||
| 968 | + | ||
| 969 | +// 对话框样式 | ||
| 970 | +.dialog-overlay { | ||
| 971 | + position: fixed; | ||
| 972 | + top: 0; | ||
| 973 | + left: 0; | ||
| 974 | + right: 0; | ||
| 975 | + bottom: 0; | ||
| 976 | + background: rgba(0, 0, 0, 0.5); | ||
| 599 | display: flex; | 977 | display: flex; |
| 978 | + align-items: center; | ||
| 600 | justify-content: center; | 979 | justify-content: center; |
| 601 | - margin-top: 20px; | 980 | + z-index: 1000; |
| 981 | +} | ||
| 982 | + | ||
| 983 | +.dialog-content { | ||
| 984 | + background: white; | ||
| 985 | + border-radius: 6px; | ||
| 986 | + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); | ||
| 987 | + width: 90%; | ||
| 988 | + max-width: 600px; | ||
| 989 | + max-height: 80vh; | ||
| 990 | + overflow-y: auto; | ||
| 991 | +} | ||
| 992 | + | ||
| 993 | +.dialog-header { | ||
| 994 | + padding: 12px 16px; | ||
| 995 | + border-bottom: 1px solid #e0e0e0; | ||
| 996 | + display: flex; | ||
| 997 | + justify-content: space-between; | ||
| 998 | + align-items: center; | ||
| 999 | +} | ||
| 1000 | + | ||
| 1001 | +.dialog-header h3 { | ||
| 1002 | + margin: 0; | ||
| 1003 | + font-size: 14px; | ||
| 1004 | + color: #333; | ||
| 1005 | +} | ||
| 1006 | + | ||
| 1007 | +.close-btn { | ||
| 1008 | + background: none; | ||
| 1009 | + border: none; | ||
| 1010 | + font-size: 16px; | ||
| 1011 | + cursor: pointer; | ||
| 1012 | + color: #999; | ||
| 1013 | + padding: 0; | ||
| 1014 | + width: 20px; | ||
| 1015 | + height: 20px; | ||
| 1016 | + display: flex; | ||
| 1017 | + align-items: center; | ||
| 1018 | + justify-content: center; | ||
| 1019 | +} | ||
| 1020 | + | ||
| 1021 | +.close-btn:hover { | ||
| 1022 | + color: #666; | ||
| 1023 | +} | ||
| 1024 | + | ||
| 1025 | +.dialog-body { | ||
| 1026 | + padding: 16px; | ||
| 1027 | +} | ||
| 1028 | + | ||
| 1029 | +.dialog-footer { | ||
| 1030 | + padding: 12px 16px; | ||
| 1031 | + border-top: 1px solid #e0e0e0; | ||
| 1032 | + display: flex; | ||
| 1033 | + justify-content: flex-end; | ||
| 1034 | + gap: 6px; | ||
| 1035 | +} | ||
| 1036 | + | ||
| 1037 | +// 详情网格布局 | ||
| 1038 | +.detail-grid { | ||
| 1039 | + display: grid; | ||
| 1040 | + grid-template-columns: 1fr 1fr; | ||
| 1041 | + gap: 12px; | ||
| 1042 | +} | ||
| 1043 | + | ||
| 1044 | +.detail-item { | ||
| 1045 | + display: flex; | ||
| 1046 | + flex-direction: column; | ||
| 1047 | + gap: 4px; | ||
| 1048 | +} | ||
| 1049 | + | ||
| 1050 | +.detail-item.full-width { | ||
| 1051 | + grid-column: 1 / -1; | ||
| 1052 | +} | ||
| 1053 | + | ||
| 1054 | +.detail-item label { | ||
| 1055 | + font-weight: 600; | ||
| 1056 | + color: #333; | ||
| 1057 | + font-size: 12px; | ||
| 1058 | +} | ||
| 1059 | + | ||
| 1060 | +.detail-item span { | ||
| 1061 | + color: #666; | ||
| 1062 | + font-size: 12px; | ||
| 602 | } | 1063 | } |
| 603 | 1064 | ||
| 604 | .log-detail { | 1065 | .log-detail { |
| 605 | - max-height: 600px; | 1066 | + max-height: 400px; |
| 606 | overflow-y: auto; | 1067 | overflow-y: auto; |
| 607 | } | 1068 | } |
| 608 | 1069 | ||
| 609 | .request-param { | 1070 | .request-param { |
| 610 | - margin-top: 20px; | 1071 | + margin-top: 16px; |
| 611 | } | 1072 | } |
| 612 | 1073 | ||
| 613 | .request-param h4 { | 1074 | .request-param h4 { |
| 614 | - margin: 0 0 10px 0; | 1075 | + margin: 0 0 8px 0; |
| 615 | - color: #303133; | 1076 | + color: #333; |
| 616 | - font-size: 14px; | 1077 | + font-size: 12px; |
| 617 | font-weight: 600; | 1078 | font-weight: 600; |
| 618 | } | 1079 | } |
| 619 | 1080 | ||
| 620 | -/* 表格优化 */ | 1081 | +.request-textarea { |
| 621 | -.el-table { | 1082 | + width: 100%; |
| 622 | - font-size: 14px; | 1083 | + padding: 6px 8px; |
| 1084 | + border: 1px solid #d9d9d9; | ||
| 1085 | + border-radius: 3px; | ||
| 1086 | + font-size: 11px; | ||
| 1087 | + font-family: monospace; | ||
| 1088 | + resize: vertical; | ||
| 1089 | + background: #f9f9f9; | ||
| 623 | } | 1090 | } |
| 624 | 1091 | ||
| 625 | -.el-table .cell { | 1092 | +// 表单样式 |
| 626 | - padding: 8px 12px; | 1093 | +.clean-form { |
| 627 | - line-height: 1.5; | 1094 | + display: flex; |
| 1095 | + flex-direction: column; | ||
| 1096 | + gap: 12px; | ||
| 628 | } | 1097 | } |
| 629 | 1098 | ||
| 630 | -.el-table th.el-table__cell { | 1099 | +.form-group { |
| 631 | - background: #fafafa; | 1100 | + display: flex; |
| 632 | - color: #606266; | 1101 | + flex-direction: column; |
| 1102 | + gap: 4px; | ||
| 1103 | +} | ||
| 1104 | + | ||
| 1105 | +.form-group label { | ||
| 633 | font-weight: 600; | 1106 | font-weight: 600; |
| 1107 | + color: #333; | ||
| 1108 | + font-size: 12px; | ||
| 1109 | +} | ||
| 1110 | + | ||
| 1111 | +.form-group label.required::after { | ||
| 1112 | + content: ' *'; | ||
| 1113 | + color: #ff4d4f; | ||
| 1114 | +} | ||
| 1115 | + | ||
| 1116 | +.form-input { | ||
| 1117 | + padding: 6px 8px; | ||
| 1118 | + border: 1px solid #d9d9d9; | ||
| 1119 | + border-radius: 3px; | ||
| 1120 | + font-size: 12px; | ||
| 1121 | + transition: border-color 0.2s; | ||
| 634 | } | 1122 | } |
| 635 | 1123 | ||
| 636 | -.el-table td.el-table__cell { | 1124 | +.form-input:focus { |
| 637 | - padding: 12px 0; | 1125 | + outline: none; |
| 1126 | + border-color: #1890ff; | ||
| 638 | } | 1127 | } |
| 639 | 1128 | ||
| 640 | -.el-table .el-button + .el-button { | 1129 | +.warning-text { |
| 641 | - margin-left: 8px; | 1130 | + padding: 8px; |
| 1131 | + background: #fff7e6; | ||
| 1132 | + border: 1px solid #ffd591; | ||
| 1133 | + border-radius: 3px; | ||
| 1134 | + color: #fa8c16; | ||
| 1135 | + font-size: 11px; | ||
| 642 | } | 1136 | } |
| 643 | </style> | 1137 | </style> | ... | ... |
-
Please register or login to post a comment