CheckUpdates.vue 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <template>
  2. <slot></slot>
  3. </template>
  4. <script setup lang="tsx">
  5. import { onMounted, onUnmounted, ref } from 'vue'
  6. import {ElButton, ElNotification} from 'element-plus'
  7. import { t } from '@/utils'
  8. const props = defineProps({
  9. checkUpdatesInterval: {
  10. type: Number,
  11. default: 5
  12. }
  13. })
  14. const lastVersionTag = ref('')
  15. let isCheckingUpdates = false
  16. let timer: ReturnType<typeof setInterval>
  17. const entrance = import.meta.env.VITE_PUBLIC_PATH
  18. let last_notify = null
  19. async function getVersionTag() {
  20. try {
  21. if (location.hostname === 'localhost' || location.hostname === '127.0.0.1') {
  22. return null
  23. }
  24. const response = await fetch(entrance, {
  25. cache: 'no-cache',
  26. method: 'HEAD'
  27. })
  28. return response.headers.get('etag') || response.headers.get('last-modified')
  29. } catch {
  30. console.error('Failed to fetch version tag')
  31. return null
  32. }
  33. }
  34. async function checkForUpdates() {
  35. const versionTag = await getVersionTag()
  36. if (!versionTag) {
  37. return
  38. }
  39. // 首次运行时不提示更新
  40. if (!lastVersionTag.value) {
  41. lastVersionTag.value = versionTag
  42. return
  43. }
  44. if (lastVersionTag.value !== versionTag && versionTag) {
  45. clearInterval(timer)
  46. handleNotice(versionTag)
  47. }
  48. }
  49. const handleNotice = versionTag => {
  50. const onOk = () => {
  51. lastVersionTag.value = versionTag
  52. window.location.reload()
  53. }
  54. if (last_notify) return
  55. const notify = ElNotification({
  56. title: t('le.layout.checkUpdatesTitle'),
  57. customClass: 'le-notification--check-app',
  58. message: (
  59. <div>
  60. {t('le.layout.checkUpdatesDescription')}
  61. <div class="text-right mt-[12px]">
  62. <ElButton onClick={onClose}>{t('le.btn.cancel')}</ElButton>
  63. <ElButton type="primary" onClick={onOk}>
  64. {t('le.refresh')}
  65. </ElButton>
  66. </div>
  67. </div>
  68. ),
  69. position: 'bottom-right',
  70. duration: 0,
  71. onClose
  72. })
  73. last_notify = notify
  74. function onClose() {
  75. notify.close()
  76. last_notify = null
  77. }
  78. }
  79. function start() {
  80. if (props.checkUpdatesInterval <= 0) {
  81. return
  82. }
  83. // 每 checkUpdatesInterval(默认值为5) 分钟检查一次
  84. timer = setInterval(checkForUpdates, props.checkUpdatesInterval * 60 * 1000)
  85. }
  86. function stop() {
  87. clearInterval(timer)
  88. }
  89. function handleVisibilitychange() {
  90. // console.error(isCheckingUpdates, 'isCheckingUpdates')
  91. if (document.hidden) {
  92. stop()
  93. } else {
  94. if (!isCheckingUpdates) {
  95. isCheckingUpdates = true
  96. checkForUpdates().finally(() => {
  97. isCheckingUpdates = false
  98. start()
  99. })
  100. }
  101. }
  102. }
  103. onMounted(() => {
  104. start()
  105. document.addEventListener('visibilitychange', handleVisibilitychange)
  106. })
  107. onUnmounted(() => {
  108. stop()
  109. document.removeEventListener('visibilitychange', handleVisibilitychange)
  110. })
  111. </script>
  112. <style lang="scss">
  113. //.le-notification--check-app {
  114. .#{$prefix}notification--check-app {
  115. .el-notification {
  116. &__group {
  117. margin: 0;
  118. width: 100%;
  119. }
  120. &__content {
  121. margin-right: -12px;
  122. }
  123. }
  124. }
  125. </style>