LogStream.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. <template>
  2. <div class="log-stream-container">
  3. <div class="header">
  4. <h1>Agent 日志跟踪</h1>
  5. <div class="header-actions">
  6. <el-input
  7. v-model="searchKeyword"
  8. placeholder="搜索日志..."
  9. :prefix-icon="Search"
  10. style="width: 300px"
  11. />
  12. <el-button type="primary" @click="refreshLogs">
  13. <el-icon>
  14. <RefreshRight />
  15. </el-icon>
  16. 刷新
  17. </el-button>
  18. </div>
  19. </div>
  20. <div class="filters">
  21. <el-select v-model="selectedLevel" placeholder="日志级别" style="width: 150px">
  22. <el-option label="全部" value="" />
  23. <el-option label="信息" value="info" />
  24. <el-option label="警告" value="warning" />
  25. <el-option label="错误" value="error" />
  26. </el-select>
  27. <el-date-picker
  28. v-model="dateRange"
  29. type="daterange"
  30. range-separator="至"
  31. start-placeholder="开始日期"
  32. end-placeholder="结束日期"
  33. />
  34. </div>
  35. <div class="logs-container">
  36. <div v-for="log in filteredLogs" :key="log.id" :class="['log-item', `log-${log.level}`]">
  37. <div class="log-header">
  38. <span :class="['log-level', `level-${log.level}`]">{{ log.level.toUpperCase() }}</span>
  39. <span class="log-time">{{ log.timestamp }}</span>
  40. <span class="log-workflow">{{ log.workflowName }}</span>
  41. </div>
  42. <div class="log-message">{{ log.message }}</div>
  43. <div v-if="log.details" class="log-details">
  44. <pre>{{ JSON.stringify(log.details, null, 2) }}</pre>
  45. </div>
  46. </div>
  47. </div>
  48. </div>
  49. </template>
  50. <script setup lang="ts">
  51. import { Search, RefreshRight } from '@element-plus/icons-vue'
  52. import { ref, computed } from 'vue'
  53. const searchKeyword = ref('')
  54. const selectedLevel = ref('')
  55. const dateRange = ref([])
  56. const logs = ref([
  57. {
  58. id: 1,
  59. level: 'info',
  60. timestamp: '2026-01-28 14:23:45',
  61. workflowName: '数据同步工作流',
  62. message: '工作流执行成功',
  63. details: { executionTime: '2.5s', nodesExecuted: 5 }
  64. },
  65. {
  66. id: 2,
  67. level: 'warning',
  68. timestamp: '2026-01-28 14:20:12',
  69. workflowName: 'API 调用工作流',
  70. message: '请求响应时间超过阈值',
  71. details: { responseTime: '3200ms', threshold: '3000ms' }
  72. },
  73. {
  74. id: 3,
  75. level: 'error',
  76. timestamp: '2026-01-28 14:15:33',
  77. workflowName: '邮件发送工作流',
  78. message: '邮件发送失败: SMTP 连接超时',
  79. details: { error: 'Connection timeout', host: 'xxxxx.com' }
  80. },
  81. {
  82. id: 4,
  83. level: 'info',
  84. timestamp: '2026-01-28 14:10:08',
  85. workflowName: '数据处理工作流',
  86. message: '处理了 1000 条数据记录',
  87. details: { processed: 1000, failed: 0 }
  88. },
  89. {
  90. id: 5,
  91. level: 'info',
  92. timestamp: '2026-01-28 14:05:22',
  93. workflowName: '定时任务工作流',
  94. message: '定时任务触发执行',
  95. details: { schedule: '*/5 * * * *', triggeredBy: 'cron' }
  96. }
  97. ])
  98. const filteredLogs = computed(() => {
  99. return logs.value.filter((log) => {
  100. const matchesKeyword =
  101. !searchKeyword.value ||
  102. log.message.toLowerCase().includes(searchKeyword.value.toLowerCase()) ||
  103. log.workflowName.toLowerCase().includes(searchKeyword.value.toLowerCase())
  104. const matchesLevel = !selectedLevel.value || log.level === selectedLevel.value
  105. return matchesKeyword && matchesLevel
  106. })
  107. })
  108. const refreshLogs = () => {
  109. console.log('Refreshing logs...')
  110. }
  111. </script>
  112. <style lang="less" scoped>
  113. .log-stream-container {
  114. max-width: 1400px;
  115. margin: 0 auto;
  116. padding: 20px;
  117. .header {
  118. display: flex;
  119. justify-content: space-between;
  120. align-items: center;
  121. margin-bottom: 24px;
  122. h1 {
  123. font-size: 28px;
  124. font-weight: 600;
  125. color: #333;
  126. }
  127. .header-actions {
  128. display: flex;
  129. gap: 12px;
  130. align-items: center;
  131. }
  132. }
  133. .filters {
  134. display: flex;
  135. gap: 16px;
  136. margin-bottom: 24px;
  137. }
  138. .logs-container {
  139. display: flex;
  140. flex-direction: column;
  141. gap: 12px;
  142. .log-item {
  143. background: #fff;
  144. border-radius: 8px;
  145. padding: 16px;
  146. border-left: 4px solid #e5e7eb;
  147. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  148. transition: all 0.3s;
  149. &:hover {
  150. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  151. }
  152. &.log-info {
  153. border-left-color: #3b82f6;
  154. }
  155. &.log-warning {
  156. border-left-color: #f59e0b;
  157. }
  158. &.log-error {
  159. border-left-color: #ef4444;
  160. }
  161. .log-header {
  162. display: flex;
  163. align-items: center;
  164. gap: 12px;
  165. margin-bottom: 8px;
  166. .log-level {
  167. font-size: 11px;
  168. font-weight: 700;
  169. padding: 4px 8px;
  170. border-radius: 4px;
  171. text-transform: uppercase;
  172. &.level-info {
  173. background: #dbeafe;
  174. color: #1e40af;
  175. }
  176. &.level-warning {
  177. background: #fef3c7;
  178. color: #92400e;
  179. }
  180. &.level-error {
  181. background: #fee2e2;
  182. color: #991b1b;
  183. }
  184. }
  185. .log-time {
  186. font-size: 13px;
  187. color: #666;
  188. }
  189. .log-workflow {
  190. font-size: 13px;
  191. color: var(--el-color-primary);
  192. font-weight: 500;
  193. }
  194. }
  195. .log-message {
  196. font-size: 14px;
  197. color: #333;
  198. line-height: 1.6;
  199. margin-bottom: 8px;
  200. }
  201. .log-details {
  202. background: #f8f9fa;
  203. border-radius: 4px;
  204. padding: 12px;
  205. margin-top: 12px;
  206. pre {
  207. margin: 0;
  208. font-size: 12px;
  209. color: #666;
  210. line-height: 1.6;
  211. overflow-x: auto;
  212. }
  213. }
  214. }
  215. }
  216. }
  217. </style>