refactor(log):重构日志管理页面组件和样式
- 将 Element Plus 组件替换为原生 HTML 元素 - 重新设计搜索区域布局和样式 - 使用原生表格替换 el-table 组件 - 自定义分页控件实现 - 重构对话框组件样式和交互 - 添加全选和单选功能 - 优化响应式设计和移动端适配 - 更新组件类型声明文件 - 改进状态标签和操作按钮样式 - 实现表格视图切换功能
Showing
2 changed files
with
801 additions
and
305 deletions
| ... | @@ -8,6 +8,7 @@ export {} | ... | @@ -8,6 +8,7 @@ export {} |
| 8 | declare module 'vue' { | 8 | declare module 'vue' { |
| 9 | export interface GlobalComponents { | 9 | export interface GlobalComponents { |
| 10 | ElButton: typeof import('element-plus/es')['ElButton'] | 10 | ElButton: typeof import('element-plus/es')['ElButton'] |
| 11 | + ElCard: typeof import('element-plus/es')['ElCard'] | ||
| 11 | ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] | 12 | ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] |
| 12 | ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] | 13 | ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] |
| 13 | ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] | 14 | ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] |
| ... | @@ -25,6 +26,7 @@ declare module 'vue' { | ... | @@ -25,6 +26,7 @@ declare module 'vue' { |
| 25 | ElTable: typeof import('element-plus/es')['ElTable'] | 26 | ElTable: typeof import('element-plus/es')['ElTable'] |
| 26 | ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] | 27 | ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] |
| 27 | ElTag: typeof import('element-plus/es')['ElTag'] | 28 | ElTag: typeof import('element-plus/es')['ElTag'] |
| 29 | + ElText: typeof import('element-plus/es')['ElText'] | ||
| 28 | Header: typeof import('./src/components/layout/Header.vue')['default'] | 30 | Header: typeof import('./src/components/layout/Header.vue')['default'] |
| 29 | RouterLink: typeof import('vue-router')['RouterLink'] | 31 | RouterLink: typeof import('vue-router')['RouterLink'] |
| 30 | RouterView: typeof import('vue-router')['RouterView'] | 32 | RouterView: typeof import('vue-router')['RouterView'] | ... | ... |
| 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> | 18 | + v-model="searchForm.username" |
| 19 | - | 19 | + type="text" |
| 20 | - <el-form-item label="用户名"> | ||
| 21 | - <el-input | ||
| 22 | - v-model="searchForm.username" | ||
| 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> | ||
| 58 | - | ||
| 59 | - <el-form-item> | ||
| 60 | - <el-button type="primary" @click="handleSearch" :loading="loading"> | ||
| 61 | - <el-icon><Search /></el-icon> | ||
| 62 | - 搜索 | ||
| 63 | - </el-button> | ||
| 64 | - <el-button @click="handleReset"> | ||
| 65 | - <el-icon><Refresh /></el-icon> | ||
| 66 | - 重置 | ||
| 67 | - </el-button> | ||
| 68 | - </el-form-item> | ||
| 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" | ||
| 80 | - > | ||
| 81 | - <el-icon><Delete /></el-icon> | ||
| 82 | - 批量删除 | ||
| 83 | - </el-button> | ||
| 84 | - <el-button type="warning" @click="handleCleanExpired"> | ||
| 85 | - <el-icon><Delete /></el-icon> | ||
| 86 | - 清理过期日志 | ||
| 87 | - </el-button> | ||
| 88 | - <el-button type="info" @click="handleExport"> | ||
| 89 | - <el-icon><Download /></el-icon> | ||
| 90 | - 导出日志 | ||
| 91 | - </el-button> | ||
| 92 | </div> | 51 | </div> |
| 93 | - <div class="toolbar-right"> | 52 | + <div class="search-actions"> |
| 94 | - <el-button @click="handleRefresh" :loading="loading"> | 53 | + <button @click="handleSearch" class="search-btn">🔍 搜索</button> |
| 95 | - <el-icon><Refresh /></el-icon> | 54 | + <button @click="handleReset" class="reset-btn">🔄 重置</button> |
| 96 | - 刷新 | ||
| 97 | - </el-button> | ||
| 98 | </div> | 55 | </div> |
| 99 | </div> | 56 | </div> |
| 100 | - </el-card> | 57 | + </div> |
| 58 | + | ||
| 59 | + <!-- 操作按钮区域 --> | ||
| 60 | + <div class="action-section"> | ||
| 61 | + <div class="action-buttons"> | ||
| 62 | + <button | ||
| 63 | + @click="handleBatchDelete" | ||
| 64 | + :disabled="selectedLogs.length === 0" | ||
| 65 | + class="action-btn danger" | ||
| 66 | + > | ||
| 67 | + 🗑️ 批量删除 | ||
| 68 | + </button> | ||
| 69 | + <button @click="handleCleanExpired" class="action-btn warning"> | ||
| 70 | + 🧹 清理过期日志 | ||
| 71 | + </button> | ||
| 72 | + <button @click="handleExport" class="action-btn info"> | ||
| 73 | + 📋 导出日志 | ||
| 74 | + </button> | ||
| 75 | + </div> | ||
| 76 | + </div> | ||
| 101 | 77 | ||
| 102 | - <!-- 日志列表 --> | 78 | + <!-- 数据表格 --> |
| 103 | - <el-card class="table-card" shadow="never"> | 79 | + <div class="table-section"> |
| 80 | + <div class="table-header"> | ||
| 81 | + <div class="table-controls"> | ||
| 82 | + <button @click="handleTableSearch" class="control-btn" title="搜索">🔍</button> | ||
| 83 | + <button @click="handleTableRefresh" class="control-btn" title="刷新">🔄</button> | ||
| 84 | + <button @click="handleTableExport" class="control-btn" title="导出">📋</button> | ||
| 85 | + <button @click="handleTableViewToggle" class="control-btn" title="视图切换">⊞</button> | ||
| 86 | + </div> | ||
| 87 | + </div> | ||
| 104 | 88 | ||
| 105 | - <el-table | 89 | + <div class="table-container"> |
| 106 | - v-loading="loading" | 90 | + <table class="data-table"> |
| 107 | - :data="logList" | 91 | + <thead> |
| 108 | - @selection-change="handleSelectionChange" | 92 | + <tr> |
| 109 | - stripe | 93 | + <th class="checkbox-col"> |
| 110 | - border | 94 | + <input |
| 111 | - style="width: 100%" | 95 | + type="checkbox" |
| 112 | - > | 96 | + :checked="selectAll" |
| 113 | - <el-table-column type="selection" width="50" align="center" /> | 97 | + @change="handleSelectAll" |
| 114 | - | 98 | + class="table-checkbox" |
| 115 | - <el-table-column prop="logId" label="日志ID" width="80" align="center" /> | 99 | + /> |
| 116 | - | 100 | + </th> |
| 117 | - <el-table-column prop="logTypeText" label="日志类型" width="100" align="center"> | 101 | + <th>日志ID</th> |
| 118 | - <template #default="{ row }"> | 102 | + <th>日志类型</th> |
| 119 | - <el-tag :type="getLogTypeTagType(row.logType)" size="small"> | 103 | + <th>用户名</th> |
| 120 | - {{ row.logTypeText }} | 104 | + <th>操作模块</th> |
| 121 | - </el-tag> | 105 | + <th>操作内容</th> |
| 122 | - </template> | 106 | + <th>IP地址</th> |
| 123 | - </el-table-column> | 107 | + <th>操作时间</th> |
| 124 | - | 108 | + <th>操作结果</th> |
| 125 | - <el-table-column prop="username" label="用户名" width="100" align="center" /> | 109 | + <th>操作</th> |
| 126 | - | 110 | + </tr> |
| 127 | - <el-table-column prop="module" label="操作模块" width="120" align="center" /> | 111 | + </thead> |
| 128 | - | 112 | + <tbody> |
| 129 | - <el-table-column prop="operation" label="操作内容" min-width="180" show-overflow-tooltip /> | 113 | + <tr v-for="log in logList" :key="log.logId"> |
| 130 | - | 114 | + <td class="checkbox-col"> |
| 131 | - <el-table-column prop="ip" label="IP地址" width="120" align="center" /> | 115 | + <input |
| 132 | - | 116 | + type="checkbox" |
| 133 | - <el-table-column prop="logTime" label="操作时间" width="160" align="center"> | 117 | + :checked="selectedLogs.includes(log.logId)" |
| 134 | - <template #default="{ row }"> | 118 | + @change="handleSelectLog(log.logId)" |
| 135 | - {{ formatDate(row.logTime) }} | 119 | + class="table-checkbox" |
| 136 | - </template> | 120 | + /> |
| 137 | - </el-table-column> | 121 | + </td> |
| 138 | - | 122 | + <td>{{ log.logId }}</td> |
| 139 | - <el-table-column prop="statusText" label="操作结果" width="100" align="center"> | 123 | + <td> |
| 140 | - <template #default="{ row }"> | 124 | + <span :class="['status-badge', getLogTypeClass(log.logType)]"> |
| 141 | - <el-tag :type="row.status === 1 ? 'success' : 'danger'" size="small"> | 125 | + {{ log.logTypeText }} |
| 142 | - {{ row.statusText }} | 126 | + </span> |
| 143 | - </el-tag> | 127 | + </td> |
| 144 | - </template> | 128 | + <td>{{ log.username }}</td> |
| 145 | - </el-table-column> | 129 | + <td>{{ log.module }}</td> |
| 146 | - | 130 | + <td class="operation-cell" :title="log.operation">{{ log.operation }}</td> |
| 147 | - <el-table-column label="操作" width="140" fixed="right" align="center"> | 131 | + <td>{{ log.ip }}</td> |
| 148 | - <template #default="{ row }"> | 132 | + <td>{{ formatDate(log.logTime) }}</td> |
| 149 | - <el-button | 133 | + <td> |
| 150 | - type="primary" | 134 | + <span :class="['status-badge', log.status === 1 ? 'success' : 'error']"> |
| 151 | - size="small" | 135 | + {{ log.statusText }} |
| 152 | - @click="handleViewDetail(row)" | 136 | + </span> |
| 153 | - link | 137 | + </td> |
| 154 | - > | 138 | + <td class="action-col"> |
| 155 | - 详情 | 139 | + <button @click="handleViewDetail(log)" class="action-btn primary" title="查看">👁️</button> |
| 156 | - </el-button> | 140 | + <button @click="handleDelete(log)" class="action-btn danger" title="删除">🗑️</button> |
| 157 | - <el-button | 141 | + </td> |
| 158 | - type="danger" | 142 | + </tr> |
| 159 | - size="small" | 143 | + </tbody> |
| 160 | - @click="handleDelete(row)" | 144 | + </table> |
| 161 | - link | 145 | + </div> |
| 162 | - > | 146 | + </div> |
| 163 | - 删除 | 147 | + |
| 164 | - </el-button> | 148 | + <!-- 分页 --> |
| 165 | - </template> | 149 | + <div class="pagination-section"> |
| 166 | - </el-table-column> | 150 | + <div class="pagination-info"> |
| 167 | - </el-table> | 151 | + 共 {{ pagination.total }} 条记录,第 {{ pagination.pageNum }} / {{ Math.ceil(pagination.total / pagination.pageSize) }} 页 |
| 168 | - | ||
| 169 | - <!-- 分页 --> | ||
| 170 | - <div class="pagination-container"> | ||
| 171 | - <el-pagination | ||
| 172 | - v-model:current-page="pagination.pageNum" | ||
| 173 | - v-model:page-size="pagination.pageSize" | ||
| 174 | - :page-sizes="[10, 20, 50, 100]" | ||
| 175 | - :total="pagination.total" | ||
| 176 | - layout="total, sizes, prev, pager, next, jumper" | ||
| 177 | - @size-change="handleSizeChange" | ||
| 178 | - @current-change="handleCurrentChange" | ||
| 179 | - /> | ||
| 180 | </div> | 152 | </div> |
| 181 | - </el-card> | 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" | ||
| 166 | + > | ||
| 167 | + 下一页 → | ||
| 168 | + </button> | ||
| 169 | + <select v-model="pagination.pageSize" @change="handleSizeChange($event)" class="page-size-select"> | ||
| 170 | + <option value="10">10条/页</option> | ||
| 171 | + <option value="20">20条/页</option> | ||
| 172 | + <option value="50">50条/页</option> | ||
| 173 | + <option value="100">100条/页</option> | ||
| 174 | + </select> | ||
| 175 | + </div> | ||
| 176 | + </div> | ||
| 182 | 177 | ||
| 183 | <!-- 日志详情对话框 --> | 178 | <!-- 日志详情对话框 --> |
| 184 | - <el-dialog | 179 | + <div v-if="detailDialogVisible" class="dialog-overlay" @click="closeDetailDialog"> |
| 185 | - v-model="detailDialogVisible" | 180 | + <div class="dialog-content" @click.stop> |
| 186 | - title="日志详情" | 181 | + <div class="dialog-header"> |
| 187 | - width="800px" | 182 | + <h3>日志详情</h3> |
| 188 | - :close-on-click-modal="false" | 183 | + <button @click="closeDetailDialog" class="close-btn">×</button> |
| 189 | - > | 184 | + </div> |
| 190 | - <div v-if="logDetail" class="log-detail"> | 185 | + <div class="dialog-body"> |
| 191 | - <el-descriptions :column="2" border> | 186 | + <div class="log-detail"> |
| 192 | - <el-descriptions-item label="日志ID"> | 187 | + <div class="detail-grid"> |
| 193 | - {{ logDetail.logId }} | 188 | + <div class="detail-item"> |
| 194 | - </el-descriptions-item> | 189 | + <label>日志ID:</label> |
| 195 | - <el-descriptions-item label="日志类型"> | 190 | + <span>{{ logDetail?.logId }}</span> |
| 196 | - <el-tag :type="getLogTypeTagType(logDetail.logType)"> | 191 | + </div> |
| 197 | - {{ logDetail.logTypeText }} | 192 | + <div class="detail-item"> |
| 198 | - </el-tag> | 193 | + <label>日志类型:</label> |
| 199 | - </el-descriptions-item> | 194 | + <span :class="['status-badge', getLogTypeClass(logDetail?.logType || '')]"> |
| 200 | - <el-descriptions-item label="用户名"> | 195 | + {{ logDetail?.logTypeText }} |
| 201 | - {{ logDetail.username }} | 196 | + </span> |
| 202 | - </el-descriptions-item> | 197 | + </div> |
| 203 | - <el-descriptions-item label="用户ID"> | 198 | + <div class="detail-item"> |
| 204 | - {{ logDetail.userId }} | 199 | + <label>用户ID:</label> |
| 205 | - </el-descriptions-item> | 200 | + <span>{{ logDetail?.userId }}</span> |
| 206 | - <el-descriptions-item label="操作模块"> | 201 | + </div> |
| 207 | - {{ logDetail.module }} | 202 | + <div class="detail-item"> |
| 208 | - </el-descriptions-item> | 203 | + <label>用户名:</label> |
| 209 | - <el-descriptions-item label="操作内容"> | 204 | + <span>{{ logDetail?.username }}</span> |
| 210 | - {{ logDetail.operation }} | 205 | + </div> |
| 211 | - </el-descriptions-item> | 206 | + <div class="detail-item"> |
| 212 | - <el-descriptions-item label="IP地址"> | 207 | + <label>操作模块:</label> |
| 213 | - {{ logDetail.ip }} | 208 | + <span>{{ logDetail?.module }}</span> |
| 214 | - </el-descriptions-item> | 209 | + </div> |
| 215 | - <el-descriptions-item label="操作时间"> | 210 | + <div class="detail-item"> |
| 216 | - {{ formatDate(logDetail.logTime) }} | 211 | + <label>操作内容:</label> |
| 217 | - </el-descriptions-item> | 212 | + <span>{{ logDetail?.operation }}</span> |
| 218 | - <el-descriptions-item label="操作结果"> | 213 | + </div> |
| 219 | - <el-tag :type="logDetail.status === 1 ? 'success' : 'danger'"> | 214 | + <div class="detail-item"> |
| 220 | - {{ logDetail.statusText }} | 215 | + <label>IP地址:</label> |
| 221 | - </el-tag> | 216 | + <span>{{ logDetail?.ip }}</span> |
| 222 | - </el-descriptions-item> | 217 | + </div> |
| 223 | - <el-descriptions-item label="错误信息" v-if="logDetail.errorMsg"> | 218 | + <div class="detail-item"> |
| 224 | - {{ logDetail.errorMsg }} | 219 | + <label>操作时间:</label> |
| 225 | - </el-descriptions-item> | 220 | + <span>{{ formatDate(logDetail?.logTime || '') }}</span> |
| 226 | - </el-descriptions> | 221 | + </div> |
| 227 | - | 222 | + <div class="detail-item"> |
| 228 | - <div v-if="logDetail.requestParam" class="request-param"> | 223 | + <label>操作结果:</label> |
| 229 | - <h4>请求参数</h4> | 224 | + <span :class="['status-badge', logDetail?.status === 1 ? 'success' : 'error']"> |
| 230 | - <el-input | 225 | + {{ logDetail?.statusText }} |
| 231 | - v-model="logDetail.requestParam" | 226 | + </span> |
| 232 | - type="textarea" | 227 | + </div> |
| 233 | - :rows="6" | 228 | + <div v-if="logDetail?.errorMsg" class="detail-item full-width"> |
| 234 | - readonly | 229 | + <label>错误信息:</label> |
| 235 | - placeholder="无请求参数" | 230 | + <span>{{ logDetail.errorMsg }}</span> |
| 236 | - /> | 231 | + </div> |
| 232 | + </div> | ||
| 233 | + | ||
| 234 | + <div v-if="logDetail?.requestParam" class="request-param"> | ||
| 235 | + <h4>请求参数</h4> | ||
| 236 | + <textarea | ||
| 237 | + :value="logDetail.requestParam" | ||
| 238 | + readonly | ||
| 239 | + placeholder="无请求参数" | ||
| 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> | ||
| 237 | </div> | 248 | </div> |
| 238 | </div> | 249 | </div> |
| 239 | - | 250 | + </div> |
| 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" |
| 258 | - placeholder="请输入过期天数" | 265 | + type="number" |
| 259 | - style="width: 100%" | 266 | + min="1" |
| 260 | - /> | 267 | + max="365" |
| 261 | - </el-form-item> | 268 | + placeholder="请输入过期天数" |
| 262 | - <el-form-item> | 269 | + class="form-input" |
| 263 | - <el-text type="warning"> | 270 | + required |
| 264 | - 将删除指定天数之前的所有日志记录,此操作不可恢复! | 271 | + /> |
| 265 | - </el-text> | 272 | + </div> |
| 266 | - </el-form-item> | 273 | + <div class="warning-text"> |
| 267 | - </el-form> | 274 | + ⚠️ 将删除指定天数之前的所有日志记录,此操作不可恢复! |
| 268 | - | 275 | + </div> |
| 269 | - <template #footer> | 276 | + </form> |
| 270 | - <el-button @click="cleanDialogVisible = false">取消</el-button> | 277 | + </div> |
| 271 | - <el-button type="danger" @click="handleConfirmClean" :loading="cleanLoading"> | 278 | + <div class="dialog-footer"> |
| 272 | - 确认清理 | 279 | + <button @click="closeCleanDialog" class="action-btn">取消</button> |
| 273 | - </el-button> | 280 | + <button @click="handleConfirmClean" :disabled="cleanLoading" class="action-btn danger"> |
| 274 | - </template> | 281 | + {{ cleanLoading ? '清理中...' : '确认清理' }} |
| 275 | - </el-dialog> | 282 | + </button> |
| 283 | + </div> | ||
| 284 | + </div> | ||
| 285 | + </div> | ||
| 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; | ||
| 568 | } | 866 | } |
| 569 | 867 | ||
| 570 | -.search-card { | 868 | +.action-col .action-btn { |
| 571 | - margin-bottom: 16px; | 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; | ||
| 572 | } | 878 | } |
| 573 | 879 | ||
| 574 | -.search-form { | 880 | +// 状态标签 |
| 575 | - margin-bottom: 0; | 881 | +.status-badge { |
| 882 | + padding: 2px 6px; | ||
| 883 | + border-radius: 3px; | ||
| 884 | + font-size: 10px; | ||
| 576 | } | 885 | } |
| 577 | 886 | ||
| 578 | -.toolbar-card { | 887 | +.status-badge.info { |
| 579 | - margin-bottom: 16px; | 888 | + background: #e6f7ff; |
| 889 | + color: #1890ff; | ||
| 580 | } | 890 | } |
| 581 | 891 | ||
| 582 | -.toolbar { | 892 | +.status-badge.warning { |
| 893 | + background: #fff7e6; | ||
| 894 | + color: #fa8c16; | ||
| 895 | +} | ||
| 896 | + | ||
| 897 | +.status-badge.success { | ||
| 898 | + background: #f6ffed; | ||
| 899 | + color: #52c41a; | ||
| 900 | +} | ||
| 901 | + | ||
| 902 | +.status-badge.error { | ||
| 903 | + background: #fff2f0; | ||
| 904 | + color: #ff4d4f; | ||
| 905 | +} | ||
| 906 | + | ||
| 907 | +.status-badge.default { | ||
| 908 | + background: #f5f5f5; | ||
| 909 | + color: #8c8c8c; | ||
| 910 | +} | ||
| 911 | + | ||
| 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; | ||
| 586 | } | 920 | } |
| 587 | 921 | ||
| 588 | -.toolbar-left, | 922 | +.pagination-info { |
| 589 | -.toolbar-right { | 923 | + font-size: 12px; |
| 924 | + color: #666; | ||
| 925 | +} | ||
| 926 | + | ||
| 927 | +.pagination-controls { | ||
| 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