index.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <script setup lang="ts">
  2. import { computed, ref } from 'vue'
  3. import { IconButton } from '@repo/ui'
  4. import { useRunnerStore, type NodeStatus, type RunnerStatus } from '@/store/modules/runner.store'
  5. import { useI18n } from '@/composables/useI18n'
  6. const emit = defineEmits<{
  7. toggle: [open: boolean]
  8. }>()
  9. const open = ref(false)
  10. const runnerStore = useRunnerStore()
  11. const { t } = useI18n()
  12. const executions = computed(() => runnerStore.executions)
  13. const onClick = () => {
  14. open.value = !open.value
  15. emit('toggle', open.value)
  16. }
  17. const formatJson = (value: unknown) => {
  18. if (value === null || value === undefined) return '-'
  19. try {
  20. return JSON.stringify(value, null, 2)
  21. } catch {
  22. return String(value)
  23. }
  24. }
  25. const statusText = (status: NodeStatus | RunnerStatus) => {
  26. if (status === 'running') return t('pages.editorFooter.running')
  27. if (status === 'success') return t('pages.editorFooter.success')
  28. if (status === 'finished') return t('pages.editorFooter.finished')
  29. if (status === 'failed') return t('pages.editorFooter.failed')
  30. if (status === 'error') return t('pages.editorFooter.error')
  31. if (status === 'suspended') return t('pages.runWorkflow.suspended')
  32. return t('pages.editorFooter.ready')
  33. }
  34. const statusTagType = (
  35. status: NodeStatus | RunnerStatus
  36. ): 'info' | 'success' | 'warning' | 'danger' => {
  37. if (status === 'running') return 'warning'
  38. if (status === 'suspended') return 'warning'
  39. if (status === 'success' || status === 'finished') return 'success'
  40. if (status === 'failed' || status === 'error') return 'danger'
  41. return 'info'
  42. }
  43. </script>
  44. <template>
  45. <div class="flex w-full h-full overflow-hidden">
  46. <div class="flex-1 flex flex-col">
  47. <div
  48. class="h-32px shrink-0 px-12px flex items-center justify-between border border-solid border-gray-200"
  49. @click="onClick"
  50. >
  51. <span class="text-12px">{{ t('pages.editorFooter.logs') }}</span>
  52. <IconButton :icon="open ? 'lucide:chevron-down' : 'lucide:chevron-up'" link />
  53. </div>
  54. <div class="flex-1 text-12px p-12px overflow-auto">
  55. <div v-if="executions.length === 0" class="text-gray-400">
  56. {{ t('pages.editorFooter.empty') }}
  57. </div>
  58. <el-table v-else :data="executions" row-key="runnerKey" size="small" border class="w-full">
  59. <el-table-column type="expand" width="48">
  60. <template #default="scope">
  61. <div class="p-8px">
  62. <el-table
  63. :data="scope.row.nodes"
  64. row-key="nodeId"
  65. size="small"
  66. border
  67. class="w-full"
  68. >
  69. <el-table-column
  70. prop="nodeName"
  71. :label="t('pages.editorFooter.nodeName')"
  72. min-width="160"
  73. />
  74. <el-table-column
  75. prop="nodeType"
  76. :label="t('pages.editorFooter.type')"
  77. width="120"
  78. />
  79. <el-table-column :label="t('pages.editorFooter.status')" width="100">
  80. <template #default="{ row }">
  81. <el-tag :type="statusTagType(row.status)" size="small">
  82. {{ statusText(row.status) }}
  83. </el-tag>
  84. </template>
  85. </el-table-column>
  86. <el-table-column
  87. prop="lastUpdateTime"
  88. :label="t('pages.editorFooter.lastTime')"
  89. width="180"
  90. />
  91. <el-table-column
  92. :label="t('pages.editorFooter.detail')"
  93. min-width="260"
  94. >
  95. <template #default="{ row }">
  96. <el-tabs type="border-card" class="w-full">
  97. <el-tab-pane :label="t('pages.editorFooter.input')">
  98. <pre class="bg-#f7f7f7 rounded p-6px whitespace-pre-wrap break-all max-h-160px overflow-auto">{{ formatJson(row.track?.input_variable) }}</pre>
  99. </el-tab-pane>
  100. <el-tab-pane :label="t('pages.editorFooter.output')">
  101. <pre class="bg-#f7f7f7 rounded p-6px whitespace-pre-wrap break-all max-h-160px overflow-auto">{{ formatJson(row.track?.output_variable) }}</pre>
  102. </el-tab-pane>
  103. </el-tabs>
  104. </template>
  105. </el-table-column>
  106. </el-table>
  107. </div>
  108. </template>
  109. </el-table-column>
  110. <el-table-column type="index" label="#" width="48" />
  111. <el-table-column prop="runnerKey" :label="t('pages.editorFooter.runId')" min-width="220" />
  112. <el-table-column :label="t('pages.editorFooter.status')" width="100">
  113. <template #default="{ row }">
  114. <el-tag :type="statusTagType(row.status)" size="small">
  115. {{ statusText(row.status) }}
  116. </el-tag>
  117. </template>
  118. </el-table-column>
  119. <el-table-column prop="startedAt" :label="t('pages.editorFooter.startedAt')" width="180" />
  120. <el-table-column prop="finishedAt" :label="t('pages.editorFooter.finishedAt')" width="180" />
  121. <el-table-column :label="t('pages.editorFooter.nodeCount')" width="80">
  122. <template #default="{ row }">
  123. {{ row.nodes?.length || 0 }}
  124. </template>
  125. </el-table-column>
  126. </el-table>
  127. </div>
  128. </div>
  129. </div>
  130. </template>