Environments.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. <script setup lang="ts">
  2. import {useSettingsStore} from '@/pinia'
  3. import {useGettext} from 'vue3-gettext'
  4. import {computed, onMounted, onUnmounted, ref} from 'vue'
  5. import environment from '@/api/environment'
  6. import Icon, {LinkOutlined, SendOutlined, ThunderboltOutlined} from '@ant-design/icons-vue'
  7. import logo from '@/assets/img/logo.png'
  8. import pulse from '@/assets/svg/pulse.svg'
  9. import {formatDateTime} from '@/lib/helper'
  10. import ws from '@/lib/websocket'
  11. import ReconnectingWebSocket from 'reconnecting-websocket'
  12. import NodeAnalyticItem from '@/views/dashboard/components/NodeAnalyticItem.vue'
  13. const settingsStore = useSettingsStore()
  14. const {$gettext} = useGettext()
  15. const data = ref([])
  16. const node_map = computed(() => {
  17. const o = {}
  18. data.value.forEach(v => {
  19. o[v.id] = v
  20. })
  21. return o
  22. })
  23. environment.get_list().then(r => {
  24. data.value = r.data
  25. })
  26. let websocket: ReconnectingWebSocket | WebSocket
  27. onMounted(() => {
  28. websocket = ws('/api/analytic/nodes')
  29. websocket.onmessage = m => {
  30. const nodes = JSON.parse(m.data)
  31. for (let key in nodes) {
  32. // update node online status
  33. if (node_map.value[key]) {
  34. Object.assign(node_map.value[key], nodes[key])
  35. node_map.value[key].response_at = new Date()
  36. }
  37. }
  38. }
  39. })
  40. onUnmounted(() => {
  41. websocket.close()
  42. })
  43. export interface Node {
  44. id: number
  45. name: string
  46. token: string
  47. }
  48. const {environment: env} = settingsStore
  49. function link_start(node: Node) {
  50. env.id = node.id
  51. env.name = node.name
  52. }
  53. const visible = computed(() => {
  54. if (env.id > 0) {
  55. return false
  56. } else {
  57. return data.value?.length
  58. }
  59. })
  60. </script>
  61. <template>
  62. <a-card class="env-list-card" :title="$gettext('Environments')" v-if="visible">
  63. <a-list item-layout="horizontal" :data-source="data">
  64. <template #renderItem="{ item }">
  65. <a-list-item>
  66. <template #actions>
  67. <a-button type="primary" @click="link_start(item)" :disabled="env.id===item.id" ghost>
  68. <send-outlined/>
  69. {{ env.id !== item.id ? $gettext('Link Start') : $gettext('Connected') }}
  70. </a-button>
  71. </template>
  72. <a-list-item-meta>
  73. <template #title>
  74. {{ item.name }}
  75. <a-tag color="blue" v-if="item.status">{{ $gettext('Online') }}</a-tag>
  76. <a-tag color="error" v-else>{{ $gettext('Offline') }}</a-tag>
  77. <div class="runtime-meta">
  78. <template v-if="item.status">
  79. <span><Icon :component="pulse"/> {{ formatDateTime(item.response_at) }}</span>
  80. <span><thunderbolt-outlined/>{{ item.version }}</span>
  81. </template>
  82. <span><link-outlined/>{{ item.url }}</span>
  83. </div>
  84. </template>
  85. <template #avatar>
  86. <a-avatar :src="logo"/>
  87. </template>
  88. <template #description>
  89. <node-analytic-item :item="item"/>
  90. </template>
  91. </a-list-item-meta>
  92. </a-list-item>
  93. </template>
  94. </a-list>
  95. </a-card>
  96. </template>
  97. <style scoped lang="less">
  98. .env-list-card {
  99. margin-top: 16px;
  100. .runtime-meta {
  101. display: inline-flex;
  102. span {
  103. font-weight: 400;
  104. font-size: 13px;
  105. margin-right: 16px;
  106. color: #9b9b9b;
  107. &.anticon {
  108. margin-right: 4px;
  109. }
  110. }
  111. }
  112. }
  113. </style>