| | |
| | | </view> |
| | | <view class="update-btn" @click="handleVersionUpdate"> |
| | | <image :src="updateBtnIcon" class="banner-btn-icon" mode="aspectFit"></image> |
| | | <text class="update-btn-text">{{ isUpdatingVersion ? '下载中...' : '立即更新' }}</text> |
| | | <text class="update-btn-text">{{ updateButtonText }}</text> |
| | | </view> |
| | | </view> |
| | | <view class="update-banner" v-else> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { onMounted, ref } from 'vue'; |
| | | import { computed, onMounted, ref } from 'vue'; |
| | | import configPageIcon from '../../static/admin-pages/L2Nja.png'; |
| | | import machineIcon from '../../static/admin-pages/n83u2.png'; |
| | | import systemInfoIcon from '../../static/admin-pages/tLgNe.png'; |
| | |
| | | import { syncEmployeeVerifyList } from '../../api/employee'; |
| | | import { deactivateVisitMachine } from '../../api/device'; |
| | | import { getLatestVersion } from '../../api/visitorRecord'; |
| | | import { clearRequestServerConfig, getRequestServerConfig, setRequestServerConfig } from '../../utils/request'; |
| | | import { buildBaseUrl, clearRequestServerConfig, getRequestServerConfig, setRequestServerConfig } from '../../utils/request'; |
| | | import { syncAllPendingRecords } from '../../utils/syncEngine'; |
| | | import { downloadRemoteFileToLocal } from '../../utils/fs'; |
| | | |
| | |
| | | const isLoggingOut = ref(false); |
| | | const isManualSyncing = ref(false); |
| | | const isUpdatingVersion = ref(false); |
| | | const updateDownloadProgress = ref(-1); |
| | | const isInstallingVersion = ref(false); |
| | | const logoutHoldTimer = ref(null); |
| | | |
| | | const currentVersion = ref('v1.0.0'); |
| | | const latestVersion = ref(''); |
| | | const hasNewVersion = ref(false); |
| | | const latestVersionInfo = ref(null); |
| | | |
| | | const updateButtonText = computed(() => { |
| | | if (!isUpdatingVersion.value) { |
| | | return '立即更新'; |
| | | } |
| | | |
| | | if (isInstallingVersion.value) { |
| | | return '安装中...'; |
| | | } |
| | | |
| | | if (updateDownloadProgress.value >= 0) { |
| | | return `下载中 ${updateDownloadProgress.value}%`; |
| | | } |
| | | |
| | | return '下载中...'; |
| | | }); |
| | | |
| | | const normalizeVersionNumber = (version = '') => { |
| | | return String(version || '').trim().replace(/^[vV]/, ''); |
| | |
| | | }); |
| | | }; |
| | | |
| | | const resolvePackageDownloadUrl = (packageUrl = '') => { |
| | | const normalizedPackageUrl = String(packageUrl || '').trim(); |
| | | if (!normalizedPackageUrl) { |
| | | return ''; |
| | | } |
| | | |
| | | if (/^https?:\/\//.test(normalizedPackageUrl)) { |
| | | return normalizedPackageUrl; |
| | | } |
| | | |
| | | const baseUrl = buildBaseUrl(getRequestServerConfig()); |
| | | if (!baseUrl) { |
| | | return ''; |
| | | } |
| | | |
| | | return `${baseUrl}${normalizedPackageUrl.startsWith('/') ? normalizedPackageUrl : `/${normalizedPackageUrl}`}`; |
| | | }; |
| | | |
| | | const handleVersionUpdate = async () => { |
| | | if (isUpdatingVersion.value) { |
| | | return; |
| | |
| | | |
| | | const packageUrl = latestVersionInfo.value?.packageUrl || ''; |
| | | const packageName = latestVersionInfo.value?.packageName || 'visitor-update.apk'; |
| | | const packageDownloadUrl = resolvePackageDownloadUrl(packageUrl); |
| | | |
| | | if (!hasNewVersion.value || !packageUrl) { |
| | | if (!hasNewVersion.value || !packageDownloadUrl) { |
| | | uni.showToast({ |
| | | title: '未获取到更新包地址', |
| | | icon: 'none', |
| | |
| | | |
| | | try { |
| | | isUpdatingVersion.value = true; |
| | | uni.showLoading({ |
| | | title: '更新包下载中...', |
| | | mask: true, |
| | | }); |
| | | updateDownloadProgress.value = 0; |
| | | isInstallingVersion.value = false; |
| | | let lastProgress = -1; |
| | | |
| | | const fileNamePrefix = String(packageName || 'visitor-update') |
| | | .replace(/\.[^.]+$/, '') |
| | | .replace(/[^a-zA-Z0-9_-]/g, '-') || 'visitor-update'; |
| | | const localPackagePath = await downloadRemoteFileToLocal(packageUrl, { |
| | | const localPackagePath = await downloadRemoteFileToLocal(packageDownloadUrl, { |
| | | directoryName: 'apk-updates', |
| | | fileNamePrefix, |
| | | fallbackExtension: 'apk', |
| | | onProgress: ({ progress }) => { |
| | | const safeProgress = Math.max(0, Math.min(100, Number(progress || 0))); |
| | | if (safeProgress === lastProgress) { |
| | | return; |
| | | } |
| | | lastProgress = safeProgress; |
| | | updateDownloadProgress.value = safeProgress; |
| | | }, |
| | | }); |
| | | |
| | | uni.hideLoading(); |
| | | updateDownloadProgress.value = 100; |
| | | isInstallingVersion.value = true; |
| | | uni.showToast({ |
| | | title: '下载完成,正在拉起安装', |
| | | icon: 'none', |
| | | }); |
| | | |
| | | plus.runtime.install(localPackagePath, {}, () => { |
| | | uni.showToast({ |
| | | title: '安装包已打开,请按提示完成更新', |
| | | icon: 'none', |
| | | }); |
| | | }, (error) => { |
| | | console.error('安装更新包失败:', error); |
| | | uni.showToast({ |
| | | title: error?.message || '安装更新包失败', |
| | | icon: 'none', |
| | | await new Promise((resolve, reject) => { |
| | | plus.runtime.install(localPackagePath, {}, () => { |
| | | uni.showToast({ |
| | | title: '安装包已打开,请按提示完成更新', |
| | | icon: 'none', |
| | | }); |
| | | resolve(); |
| | | }, (error) => { |
| | | reject(error); |
| | | }); |
| | | }); |
| | | } catch (error) { |
| | | console.error('下载更新包失败:', error); |
| | | const actionText = isInstallingVersion.value ? '安装更新包失败' : '下载更新包失败'; |
| | | console.error(`${actionText}:`, error); |
| | | uni.showToast({ |
| | | title: error?.message || '下载更新包失败', |
| | | title: error?.message || actionText, |
| | | icon: 'none', |
| | | }); |
| | | } finally { |
| | | uni.hideLoading(); |
| | | isUpdatingVersion.value = false; |
| | | updateDownloadProgress.value = -1; |
| | | isInstallingVersion.value = false; |
| | | } |
| | | }; |
| | | |
| | |
| | | |
| | | try { |
| | | isManualSyncing.value = true; |
| | | |
| | | |
| | | // 强制触发员工档案全量同步,绕过缓存有效期 |
| | | try { |
| | | await syncEmployeeVerifyList(true); |