XrayStatusCard.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <script setup>
  2. import { useI18n } from 'vue-i18n';
  3. import {
  4. BarsOutlined,
  5. PoweroffOutlined,
  6. ReloadOutlined,
  7. ToolOutlined,
  8. } from '@ant-design/icons-vue';
  9. const { t } = useI18n();
  10. defineProps({
  11. status: { type: Object, required: true },
  12. isMobile: { type: Boolean, default: false },
  13. ipLimitEnable: { type: Boolean, default: false },
  14. });
  15. defineEmits(['stop-xray', 'restart-xray', 'open-logs', 'open-xray-logs', 'open-version-switch']);
  16. // Map xray.color → which animation class to apply on the badge dot.
  17. // The legacy .xray-*-animation classes only override the badge ring
  18. // color; the actual pulsing comes from .xray-processing-animation
  19. // (which animates .ant-badge-status-dot via @keyframes runningAnimation).
  20. function badgeAnimationClass(color) {
  21. if (color === 'green') return 'xray-running-animation';
  22. if (color === 'orange') return 'xray-stop-animation';
  23. if (color === 'red') return 'xray-error-animation';
  24. return 'xray-processing-animation';
  25. }
  26. </script>
  27. <template>
  28. <a-card hoverable>
  29. <template #title>
  30. <a-space direction="horizontal">
  31. <span>{{ t('pages.index.xrayStatus') }}</span>
  32. <a-tag v-if="isMobile && status.xray.version && status.xray.version !== 'Unknown'" color="green">
  33. v{{ status.xray.version }}
  34. </a-tag>
  35. </a-space>
  36. </template>
  37. <template #extra>
  38. <template v-if="status.xray.state !== 'error'">
  39. <a-badge status="processing" :class="['xray-processing-animation', badgeAnimationClass(status.xray.color)]"
  40. :text="status.xray.stateMsg" :color="status.xray.color" />
  41. </template>
  42. <template v-else>
  43. <a-popover>
  44. <template #title>
  45. <a-row type="flex" align="middle" justify="space-between">
  46. <a-col><span>{{ t('pages.index.xrayStatusError') }}</span></a-col>
  47. <a-col>
  48. <BarsOutlined class="cursor-pointer" @click="$emit('open-logs')" />
  49. </a-col>
  50. </a-row>
  51. </template>
  52. <template #content>
  53. <span v-for="(line, i) in (status.xray.errorMsg || '').split('\n')" :key="i" class="error-line">
  54. {{ line }}
  55. </span>
  56. </template>
  57. <a-badge status="processing" :text="status.xray.stateMsg" :color="status.xray.color"
  58. :class="['xray-processing-animation', 'xray-error-animation']" />
  59. </a-popover>
  60. </template>
  61. </template>
  62. <template #actions>
  63. <a-space v-if="ipLimitEnable" direction="horizontal" class="action" @click="$emit('open-xray-logs')">
  64. <BarsOutlined />
  65. <span v-if="!isMobile">{{ t('pages.index.logs') }}</span>
  66. </a-space>
  67. <a-space direction="horizontal" class="action" @click="$emit('stop-xray')">
  68. <PoweroffOutlined />
  69. <span v-if="!isMobile">{{ t('pages.index.stopXray') }}</span>
  70. </a-space>
  71. <a-space direction="horizontal" class="action" @click="$emit('restart-xray')">
  72. <ReloadOutlined />
  73. <span v-if="!isMobile">{{ t('pages.index.restartXray') }}</span>
  74. </a-space>
  75. <a-space direction="horizontal" class="action" @click="$emit('open-version-switch')">
  76. <ToolOutlined />
  77. <span v-if="!isMobile">
  78. {{ status.xray.version && status.xray.version !== 'Unknown'
  79. ? `v${status.xray.version}`
  80. : t('pages.index.xraySwitch') }}
  81. </span>
  82. </a-space>
  83. </template>
  84. </a-card>
  85. </template>
  86. <style scoped>
  87. .action {
  88. cursor: pointer;
  89. justify-content: center;
  90. }
  91. .error-line {
  92. display: block;
  93. max-width: 400px;
  94. white-space: pre-wrap;
  95. }
  96. .cursor-pointer {
  97. cursor: pointer;
  98. }
  99. </style>
  100. <style>
  101. /* Legacy xray-*-animation classes — they need to be global so they
  102. * pierce the AD-Vue badge's internal DOM (.ant-badge-status-*). */
  103. .xray-processing-animation .ant-badge-status-dot {
  104. animation: xray-pulse 1.2s linear infinite;
  105. }
  106. .xray-running-animation .ant-badge-status-processing::after {
  107. border-color: #1677ff;
  108. }
  109. .xray-stop-animation .ant-badge-status-processing::after {
  110. border-color: #fa8c16;
  111. }
  112. .xray-error-animation .ant-badge-status-processing::after {
  113. border-color: #f5222d;
  114. }
  115. @keyframes xray-pulse {
  116. 0%,
  117. 50%,
  118. 100% {
  119. transform: scale(1);
  120. opacity: 1;
  121. }
  122. 10% {
  123. transform: scale(1.5);
  124. opacity: 0.2;
  125. }
  126. }
  127. </style>