SharedLogsTable.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <template>
  2. <div class="logs-table scroll overflow-x-auto">
  3. <transition-expand>
  4. <div v-if="state.selected.length > 0" class="border-x border-t px-4 py-2">
  5. <ui-button @click="stopSelectedWorkflow"> Stop selected </ui-button>
  6. </div>
  7. </transition-expand>
  8. <table class="w-full">
  9. <tbody class="divide-y dark:divide-gray-800">
  10. <template v-if="running && running[0]?.state">
  11. <tr v-for="item in running" :key="item.id" class="border p-2">
  12. <td v-if="!hideSelect" class="w-8">
  13. <ui-checkbox
  14. :model-value="state.selected.includes(item.id)"
  15. class="align-text-bottom"
  16. @change="toggleSelectedLog($event, item.id)"
  17. />
  18. </td>
  19. <td class="w-4/12">
  20. <p
  21. v-if="modal"
  22. class="log-link text-overflow"
  23. @click="$emit('select', { type: 'running', id: item.id })"
  24. >
  25. {{ item.state.name }}
  26. </p>
  27. <router-link
  28. v-else
  29. :to="`/logs/${item.id}/running`"
  30. class="log-link text-overflow"
  31. >
  32. {{ item.state.name }}
  33. </router-link>
  34. </td>
  35. <td
  36. :title="t('log.duration')"
  37. class="log-time w-2/12 dark:text-gray-200"
  38. >
  39. <v-remixicon name="riTimerLine"></v-remixicon>
  40. <span>{{
  41. countDuration(item.state?.startedTimestamp, Date.now())
  42. }}</span>
  43. </td>
  44. <td title="Executing block" class="text-overflow">
  45. <ui-spinner color="text-accent" size="20" />
  46. <span class="text-overflow ml-3 inline-block align-middle">
  47. {{
  48. getTranslation(
  49. `workflow.blocks.${item.state.currentBlock[0].name}.name`,
  50. item.state.currentBlock[0].name
  51. )
  52. }}
  53. </span>
  54. </td>
  55. <td class="text-right">
  56. <span
  57. class="inline-block w-16 rounded-md bg-blue-300 py-1 text-center text-sm dark:text-black"
  58. >
  59. {{ t('common.running') }}
  60. </span>
  61. </td>
  62. <td class="text-right">
  63. <ui-button small class="text-sm" @click="stopWorkflow(item.id)">
  64. {{ t('common.stop') }}
  65. </ui-button>
  66. </td>
  67. </tr>
  68. </template>
  69. <tr v-for="log in logs" :key="log.id" class="hoverable">
  70. <slot name="item-prepend" :log="log" />
  71. <td
  72. class="text-overflow w-4/12"
  73. style="min-width: 140px; max-width: 330px"
  74. >
  75. <p
  76. v-if="modal"
  77. class="log-link text-overflow"
  78. @click="$emit('select', { type: 'log', id: log.id })"
  79. >
  80. {{ log.name }}
  81. </p>
  82. <router-link
  83. v-else
  84. :to="`/logs/${log.id}`"
  85. class="log-link text-overflow"
  86. >
  87. {{ log.name }}
  88. </router-link>
  89. </td>
  90. <td
  91. class="log-time w-3/12 dark:text-gray-200"
  92. style="min-width: 200px"
  93. >
  94. <v-remixicon
  95. :title="t('log.startedDate')"
  96. name="riCalendarLine"
  97. class="mr-2 inline-block align-middle"
  98. />
  99. <span :title="formatDate(log.startedAt, 'DD MMM YYYY, hh:mm A')">
  100. {{ formatDate(log.startedAt, 'relative') }}
  101. </span>
  102. </td>
  103. <td
  104. :title="t('log.duration')"
  105. class="log-time w-2/12 dark:text-gray-200"
  106. style="min-width: 85px"
  107. >
  108. <v-remixicon name="riTimerLine"></v-remixicon>
  109. <span>{{ countDuration(log.startedAt, log.endedAt) }}</span>
  110. </td>
  111. <td class="text-right">
  112. <span
  113. :class="statusColors[log.status]"
  114. :title="log.status === 'error' ? getErrorMessage(log) : null"
  115. class="inline-block w-24 rounded-md py-1 text-center text-sm dark:text-black"
  116. >
  117. {{ t(`logStatus.${log.status}`) }}
  118. </span>
  119. </td>
  120. <slot name="item-append" :log="log" />
  121. </tr>
  122. <slot name="table:append" />
  123. </tbody>
  124. </table>
  125. </div>
  126. </template>
  127. <script setup>
  128. import { reactive } from 'vue';
  129. import { useI18n } from 'vue-i18n';
  130. import { countDuration } from '@/utils/helper';
  131. import { stopWorkflowExec } from '@/workflowEngine';
  132. import dayjs from '@/lib/dayjs';
  133. defineProps({
  134. logs: {
  135. type: Array,
  136. default: () => [],
  137. },
  138. running: {
  139. type: Array,
  140. default: () => [],
  141. },
  142. modal: Boolean,
  143. hideSelect: Boolean,
  144. });
  145. defineEmits(['select']);
  146. const { t, te } = useI18n();
  147. const statusColors = {
  148. error: 'bg-red-200 dark:bg-red-300',
  149. success: 'bg-green-200 dark:bg-green-300',
  150. stopped: 'bg-yellow-200 dark:bg-yellow-300',
  151. };
  152. const state = reactive({
  153. selected: [],
  154. });
  155. function getTranslation(key, defText = '') {
  156. return te(key) ? t(key) : defText;
  157. }
  158. function stopWorkflow(stateId) {
  159. stopWorkflowExec(stateId);
  160. }
  161. function toggleSelectedLog(selected, id) {
  162. if (selected) {
  163. state.selected.push(id);
  164. return;
  165. }
  166. const index = state.selected.indexOf(id);
  167. if (index !== -1) state.selected.splice(index, 1);
  168. }
  169. function formatDate(date, format) {
  170. if (format === 'relative') return dayjs(date).fromNow();
  171. return dayjs(date).format(format);
  172. }
  173. function getErrorMessage({ message }) {
  174. const messagePath = `log.messages.${message}`;
  175. if (message && te(messagePath)) {
  176. return t(messagePath);
  177. }
  178. return '';
  179. }
  180. function stopSelectedWorkflow() {
  181. state.selected.forEach((id) => {
  182. stopWorkflow(id);
  183. });
  184. state.selected = [];
  185. }
  186. </script>
  187. <style scoped>
  188. .log-time svg {
  189. @apply mr-2;
  190. }
  191. .log-time svg,
  192. .log-time span {
  193. display: inline-block;
  194. vertical-align: middle;
  195. }
  196. .log-link {
  197. @apply inline-block w-full align-middle;
  198. cursor: pointer;
  199. min-height: 28px;
  200. }
  201. </style>