fix: 自动更新提示bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m55s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 4m55s
This commit is contained in:
67
src/App.vue
67
src/App.vue
@@ -27,6 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const VERSION_CHECK_INTERVAL = 5 * 60 * 1000
|
const VERSION_CHECK_INTERVAL = 5 * 60 * 1000
|
||||||
|
const VERSION_PROMPT_STORAGE_KEY = 'admin-xm-iot:confirmed-version-update'
|
||||||
let versionCheckTimer: number | undefined
|
let versionCheckTimer: number | undefined
|
||||||
let currentEntrySignature = ''
|
let currentEntrySignature = ''
|
||||||
let upgradePromptVisible = false
|
let upgradePromptVisible = false
|
||||||
@@ -78,18 +79,69 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getLoadedEntrySignature = () => {
|
const getLoadedEntrySignature = () => {
|
||||||
return Array.from(document.querySelectorAll<HTMLScriptElement | HTMLLinkElement>('script[src],link[href]'))
|
return Array.from(
|
||||||
|
document.querySelectorAll<HTMLScriptElement | HTMLLinkElement>('script[src],link[href]')
|
||||||
|
)
|
||||||
.map((element) => ('src' in element ? element.src : element.href))
|
.map((element) => ('src' in element ? element.src : element.href))
|
||||||
.filter((url) => /\/assets\/.*\.(js|css)(\?|$)/.test(url))
|
.filter((url) => /\/assets\/.*\.(js|css)(\?|$)/.test(url))
|
||||||
.sort()
|
.sort()
|
||||||
.join('|')
|
.join('|')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getStringHash = (value: string) => {
|
||||||
|
let hash = 0
|
||||||
|
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
hash = (hash << 5) - hash + value.charCodeAt(i)
|
||||||
|
hash |= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.abs(hash).toString(36)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBrowserFingerprint = () => {
|
||||||
|
return getStringHash(
|
||||||
|
[
|
||||||
|
navigator.userAgent,
|
||||||
|
navigator.language,
|
||||||
|
navigator.platform,
|
||||||
|
Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
|
`${screen.width}x${screen.height}x${screen.colorDepth}`
|
||||||
|
].join('|')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getConfirmedVersionKey = (signature: string) => {
|
||||||
|
return `${getBrowserFingerprint()}:${getStringHash(signature)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const getConfirmedVersionUpdate = () => {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(VERSION_PROMPT_STORAGE_KEY)
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setConfirmedVersionUpdate = (signature: string) => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(VERSION_PROMPT_STORAGE_KEY, getConfirmedVersionKey(signature))
|
||||||
|
} catch {
|
||||||
|
// localStorage may be unavailable in private mode.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const reloadWithCacheBuster = () => {
|
||||||
|
const url = new URL(window.location.href)
|
||||||
|
url.searchParams.set('_t', Date.now().toString())
|
||||||
|
window.location.replace(url.toString())
|
||||||
|
}
|
||||||
|
|
||||||
const checkVersionUpdate = async () => {
|
const checkVersionUpdate = async () => {
|
||||||
if (upgradePromptVisible || document.hidden) return
|
if (upgradePromptVisible || document.hidden) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${window.location.origin}${import.meta.env.BASE_URL}`, {
|
const response = await fetch(import.meta.env.BASE_URL, {
|
||||||
cache: 'no-store'
|
cache: 'no-store'
|
||||||
})
|
})
|
||||||
const html = await response.text()
|
const html = await response.text()
|
||||||
@@ -103,14 +155,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentEntrySignature !== latestSignature) {
|
if (currentEntrySignature !== latestSignature) {
|
||||||
|
if (getConfirmedVersionUpdate() === getConfirmedVersionKey(latestSignature)) {
|
||||||
|
currentEntrySignature = latestSignature
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
upgradePromptVisible = true
|
upgradePromptVisible = true
|
||||||
await ElMessageBox.alert('系统已发布新版本,请刷新页面后继续使用。', '发现新版本', {
|
await ElMessageBox.alert('系统已发布新版本,请刷新页面后继续使用。', '发现新版本', {
|
||||||
confirmButtonText: '立即刷新',
|
confirmButtonText: '立即更新',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
closeOnClickModal: false,
|
closeOnClickModal: false,
|
||||||
closeOnPressEscape: false
|
closeOnPressEscape: false
|
||||||
})
|
})
|
||||||
window.location.reload()
|
setConfirmedVersionUpdate(latestSignature)
|
||||||
|
currentEntrySignature = latestSignature
|
||||||
|
reloadWithCacheBuster()
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('检查版本更新失败:', error)
|
console.warn('检查版本更新失败:', error)
|
||||||
|
|||||||
162
src/types/components.d.ts
vendored
162
src/types/components.d.ts
vendored
@@ -1,162 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
// Generated by unplugin-vue-components
|
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
|
||||||
export {}
|
|
||||||
|
|
||||||
/* prettier-ignore */
|
|
||||||
declare module 'vue' {
|
|
||||||
export interface GlobalComponents {
|
|
||||||
AgentSelector: typeof import('./../components/business/AgentSelector.vue')['default']
|
|
||||||
ArtBackToTop: typeof import('./../components/core/base/ArtBackToTop.vue')['default']
|
|
||||||
ArtBarChart: typeof import('./../components/core/charts/ArtBarChart.vue')['default']
|
|
||||||
ArtBarChartCard: typeof import('./../components/core/cards/ArtBarChartCard.vue')['default']
|
|
||||||
ArtBasicBanner: typeof import('./../components/core/banners/ArtBasicBanner.vue')['default']
|
|
||||||
ArtBreadcrumb: typeof import('./../components/core/layouts/art-breadcrumb/index.vue')['default']
|
|
||||||
ArtButtonMore: typeof import('./../components/core/forms/ArtButtonMore.vue')['default']
|
|
||||||
ArtButtonTable: typeof import('./../components/core/forms/ArtButtonTable.vue')['default']
|
|
||||||
ArtCardBanner: typeof import('./../components/core/banners/ArtCardBanner.vue')['default']
|
|
||||||
ArtChatWindow: typeof import('./../components/core/layouts/art-chat-window/index.vue')['default']
|
|
||||||
ArtCutterImg: typeof import('./../components/core/media/ArtCutterImg.vue')['default']
|
|
||||||
ArtDataListCard: typeof import('./../components/core/cards/ArtDataListCard.vue')['default']
|
|
||||||
ArtDataViewer: typeof import('./../components/core/views/ArtDataViewer.vue')['default']
|
|
||||||
ArtDonutChartCard: typeof import('./../components/core/cards/ArtDonutChartCard.vue')['default']
|
|
||||||
ArtDragVerify: typeof import('./../components/core/forms/ArtDragVerify.vue')['default']
|
|
||||||
ArtDualBarCompareChart: typeof import('./../components/core/charts/ArtDualBarCompareChart.vue')['default']
|
|
||||||
ArtExcelExport: typeof import('./../components/core/forms/ArtExcelExport.vue')['default']
|
|
||||||
ArtExcelImport: typeof import('./../components/core/forms/ArtExcelImport.vue')['default']
|
|
||||||
ArtException: typeof import('./../components/core/views/exception/ArtException.vue')['default']
|
|
||||||
ArtFastEnter: typeof import('./../components/core/layouts/art-fast-enter/index.vue')['default']
|
|
||||||
ArtFestivalTextScroll: typeof import('./../components/core/text-effect/ArtFestivalTextScroll.vue')['default']
|
|
||||||
ArtFireworksEffect: typeof import('./../components/core/layouts/art-fireworks-effect/index.vue')['default']
|
|
||||||
ArtGlobalSearch: typeof import('./../components/core/layouts/art-global-search/index.vue')['default']
|
|
||||||
ArtHBarChart: typeof import('./../components/core/charts/ArtHBarChart.vue')['default']
|
|
||||||
ArtHeaderBar: typeof import('./../components/core/layouts/art-header-bar/index.vue')['default']
|
|
||||||
ArtHorizontalMenu: typeof import('./../components/core/layouts/art-menus/art-horizontal-menu/index.vue')['default']
|
|
||||||
ArtIconSelector: typeof import('./../components/core/base/ArtIconSelector.vue')['default']
|
|
||||||
ArtImageCard: typeof import('./../components/core/cards/ArtImageCard.vue')['default']
|
|
||||||
ArtKLineChart: typeof import('./../components/core/charts/ArtKLineChart.vue')['default']
|
|
||||||
ArtLayouts: typeof import('./../components/core/layouts/art-layouts/index.vue')['default']
|
|
||||||
ArtLineChart: typeof import('./../components/core/charts/ArtLineChart.vue')['default']
|
|
||||||
ArtLineChartCard: typeof import('./../components/core/cards/ArtLineChartCard.vue')['default']
|
|
||||||
ArtLogo: typeof import('./../components/core/base/ArtLogo.vue')['default']
|
|
||||||
ArtMapChart: typeof import('./../components/core/charts/ArtMapChart.vue')['default']
|
|
||||||
ArtMenuRight: typeof import('./../components/core/others/ArtMenuRight.vue')['default']
|
|
||||||
ArtMixedMenu: typeof import('./../components/core/layouts/art-menus/art-mixed-menu/index.vue')['default']
|
|
||||||
ArtNotification: typeof import('./../components/core/layouts/art-notification/index.vue')['default']
|
|
||||||
ArtPageContent: typeof import('./../components/core/layouts/art-page-content/index.vue')['default']
|
|
||||||
ArtProgressCard: typeof import('./../components/core/cards/ArtProgressCard.vue')['default']
|
|
||||||
ArtRadarChart: typeof import('./../components/core/charts/ArtRadarChart.vue')['default']
|
|
||||||
ArtResultPage: typeof import('./../components/core/views/result/ArtResultPage.vue')['default']
|
|
||||||
ArtRingChart: typeof import('./../components/core/charts/ArtRingChart.vue')['default']
|
|
||||||
ArtScatterChart: typeof import('./../components/core/charts/ArtScatterChart.vue')['default']
|
|
||||||
ArtScreenLock: typeof import('./../components/core/layouts/art-screen-lock/index.vue')['default']
|
|
||||||
ArtSearchBar: typeof import('./../components/core/forms/art-search-bar/index.vue')['default']
|
|
||||||
ArtSearchDate: typeof import('./../components/core/forms/art-search-bar/widget/art-search-date/index.vue')['default']
|
|
||||||
ArtSearchInput: typeof import('./../components/core/forms/art-search-bar/widget/art-search-input/index.vue')['default']
|
|
||||||
ArtSearchRadio: typeof import('./../components/core/forms/art-search-bar/widget/art-search-radio/index.vue')['default']
|
|
||||||
ArtSearchSelect: typeof import('./../components/core/forms/art-search-bar/widget/art-search-select/index.vue')['default']
|
|
||||||
ArtSearchTreeSelect: typeof import('./../components/core/forms/art-search-bar/widget/art-search-tree-select/index.vue')['default']
|
|
||||||
ArtSettingsPanel: typeof import('./../components/core/layouts/art-settings-panel/index.vue')['default']
|
|
||||||
ArtSidebarMenu: typeof import('./../components/core/layouts/art-menus/art-sidebar-menu/index.vue')['default']
|
|
||||||
ArtStatsCard: typeof import('./../components/core/cards/ArtStatsCard.vue')['default']
|
|
||||||
ArtTable: typeof import('./../components/core/tables/ArtTable.vue')['default']
|
|
||||||
ArtTableFullScreen: typeof import('./../components/core/tables/ArtTableFullScreen.vue')['default']
|
|
||||||
ArtTableHeader: typeof import('./../components/core/tables/ArtTableHeader.vue')['default']
|
|
||||||
ArtTextScroll: typeof import('./../components/core/text-effect/ArtTextScroll.vue')['default']
|
|
||||||
ArtTimelineListCard: typeof import('./../components/core/cards/ArtTimelineListCard.vue')['default']
|
|
||||||
ArtVideoPlayer: typeof import('./../components/core/media/ArtVideoPlayer.vue')['default']
|
|
||||||
ArtWangEditor: typeof import('./../components/core/forms/ArtWangEditor.vue')['default']
|
|
||||||
ArtWatermark: typeof import('./../components/core/others/ArtWatermark.vue')['default']
|
|
||||||
ArtWorkTab: typeof import('./../components/core/layouts/art-work-tab/index.vue')['default']
|
|
||||||
BasicSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/BasicSettings.vue')['default']
|
|
||||||
BatchOperationDialog: typeof import('./../components/business/BatchOperationDialog.vue')['default']
|
|
||||||
BoxStyleSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/BoxStyleSettings.vue')['default']
|
|
||||||
CardOperationDialog: typeof import('./../components/business/CardOperationDialog.vue')['default']
|
|
||||||
CardStatusTag: typeof import('./../components/business/CardStatusTag.vue')['default']
|
|
||||||
ChunkErrorBoundary: typeof import('./../components/core/others/ChunkErrorBoundary.vue')['default']
|
|
||||||
CodeGeneratorButton: typeof import('./../components/business/CodeGeneratorButton.vue')['default']
|
|
||||||
ColorSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/ColorSettings.vue')['default']
|
|
||||||
CommentItem: typeof import('./../components/custom/comment-widget/widget/CommentItem.vue')['default']
|
|
||||||
CommentWidget: typeof import('./../components/custom/comment-widget/index.vue')['default']
|
|
||||||
CommissionDisplay: typeof import('./../components/business/CommissionDisplay.vue')['default']
|
|
||||||
ContainerSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/ContainerSettings.vue')['default']
|
|
||||||
CreateRefundDialog: typeof import('./../components/business/CreateRefundDialog.vue')['default']
|
|
||||||
CustomerAccountDialog: typeof import('./../components/business/CustomerAccountDialog.vue')['default']
|
|
||||||
DetailPage: typeof import('./../components/common/DetailPage.vue')['default']
|
|
||||||
ElAlert: typeof import('element-plus/es')['ElAlert']
|
|
||||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
|
||||||
ElCalendar: typeof import('element-plus/es')['ElCalendar']
|
|
||||||
ElCard: typeof import('element-plus/es')['ElCard']
|
|
||||||
ElCascader: typeof import('element-plus/es')['ElCascader']
|
|
||||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
|
||||||
ElCol: typeof import('element-plus/es')['ElCol']
|
|
||||||
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
|
||||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
|
||||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
|
||||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
|
||||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
|
||||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
|
||||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
|
||||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
|
||||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
|
||||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
|
||||||
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
|
||||||
ElForm: typeof import('element-plus/es')['ElForm']
|
|
||||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
|
||||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
|
||||||
ElInput: typeof import('element-plus/es')['ElInput']
|
|
||||||
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
|
||||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
|
||||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
|
||||||
ElOption: typeof import('element-plus/es')['ElOption']
|
|
||||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
|
||||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
|
||||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
|
||||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
|
||||||
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
|
||||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
|
||||||
ElRow: typeof import('element-plus/es')['ElRow']
|
|
||||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
|
||||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
|
||||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
|
||||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
|
||||||
ElTable: typeof import('element-plus/es')['ElTable']
|
|
||||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
|
||||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
|
||||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
|
||||||
ElTag: typeof import('element-plus/es')['ElTag']
|
|
||||||
ElTimeline: typeof import('element-plus/es')['ElTimeline']
|
|
||||||
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
|
|
||||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
|
||||||
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
|
|
||||||
ElUpload: typeof import('element-plus/es')['ElUpload']
|
|
||||||
ElWatermark: typeof import('element-plus/es')['ElWatermark']
|
|
||||||
HorizontalSubmenu: typeof import('./../components/core/layouts/art-menus/art-horizontal-menu/widget/HorizontalSubmenu.vue')['default']
|
|
||||||
ImportDialog: typeof import('./../components/business/ImportDialog.vue')['default']
|
|
||||||
LoginLeftView: typeof import('./../components/core/views/login/LoginLeftView.vue')['default']
|
|
||||||
MenuLayoutSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/MenuLayoutSettings.vue')['default']
|
|
||||||
MenuStyleSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/MenuStyleSettings.vue')['default']
|
|
||||||
OperationLogsDialog: typeof import('./../components/business/OperationLogsDialog.vue')['default']
|
|
||||||
OperatorSelect: typeof import('./../components/business/OperatorSelect.vue')['default']
|
|
||||||
PackageSelector: typeof import('./../components/business/PackageSelector.vue')['default']
|
|
||||||
PaymentVoucherDialog: typeof import('./../components/business/PaymentVoucherDialog.vue')['default']
|
|
||||||
RealnamePolicyDialog: typeof import('./../components/device/RealnamePolicyDialog.vue')['default']
|
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
|
||||||
SectionTitle: typeof import('./../components/core/layouts/art-settings-panel/widget/SectionTitle.vue')['default']
|
|
||||||
SettingDrawer: typeof import('./../components/core/layouts/art-settings-panel/widget/SettingDrawer.vue')['default']
|
|
||||||
SettingHeader: typeof import('./../components/core/layouts/art-settings-panel/widget/SettingHeader.vue')['default']
|
|
||||||
SettingItem: typeof import('./../components/core/layouts/art-settings-panel/widget/SettingItem.vue')['default']
|
|
||||||
SidebarSubmenu: typeof import('./../components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue')['default']
|
|
||||||
SpeedLimitDialog: typeof import('./../components/device/SpeedLimitDialog.vue')['default']
|
|
||||||
SwitchCardDialog: typeof import('./../components/device/SwitchCardDialog.vue')['default']
|
|
||||||
TableContextMenuHint: typeof import('./../components/core/others/TableContextMenuHint.vue')['default']
|
|
||||||
ThemeSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/ThemeSettings.vue')['default']
|
|
||||||
UpdateRealnameStatusDialog: typeof import('./../components/business/UpdateRealnameStatusDialog.vue')['default']
|
|
||||||
}
|
|
||||||
export interface ComponentCustomProperties {
|
|
||||||
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user