iOS / 安卓通吃!Uniapp 版本检测 + 外部浏览器跳转方案

实现方案

我将为你创建一个完整的版本检查和强制更新功能,包括:

  1. 版本检查工具类 - 获取当前App版本号和系统信息

  2. 版本更新服务 - 请求后端接口检查版本

  3. 强制更新组件 - 显示更新提示并跳转浏览器

  4. 使用示例 - 在App.vue中集成

1. 版本检查工具类 (utils/version.js)

/**
 * 版本检查和更新工具类
 */
class VersionUtils {
  /**
   * 获取当前App版本号
   * @returns {string} 版本号
   */
  static getCurrentVersion() {
    // #ifdef APP-PLUS
    const appInfo = plus.runtime.getProperty('version');
    return appInfo;
    // #endif

    // #ifdef H5
    // H5环境返回默认版本号
    return '1.0.0';
    // #endif

    // #ifdef MP
    // 小程序环境返回默认版本号
    return '1.0.0';
    // #endif
  }

  /**
   * 获取当前系统信息
   * @returns {object} 系统信息 {platform: 'ios'|'android', version: string}
   */
  static getSystemInfo() {
    // #ifdef APP-PLUS
    const systemInfo = uni.getSystemInfoSync();
    const platform = systemInfo.platform.toLowerCase();
    return {
      platform: platform === 'ios' ? 'ios' : 'android',
      version: systemInfo.system
    };
    // #endif

    // #ifdef H5
    const userAgent = navigator.userAgent.toLowerCase();
    let platform = 'unknown';
    if (userAgent.includes('iphone') || userAgent.includes('ipad') || userAgent.includes('ipod')) {
      platform = 'ios';
    } else if (userAgent.includes('android')) {
      platform = 'android';
    }
    return {
      platform,
      version: navigator.userAgent
    };
    // #endif

    // #ifdef MP
    return {
      platform: 'mp',
      version: '1.0.0'
    };
    // #endif
  }

  /**
   * 比较版本号
   * @param {string} version1 版本号1
   * @param {string} version2 版本号2
   * @returns {number} 1: version1 > version2, -1: version1 < version2, 0: 相等
   */
  static compareVersion(version1, version2) {
    const v1 = version1.split('.').map(Number);
    const v2 = version2.split('.').map(Number);

    const maxLength = Math.max(v1.length, v2.length);

    for (let i = 0; i < maxLength; i++) {
      const num1 = v1[i] || 0;
      const num2 = v2[i] || 0;

      if (num1 > num2) return 1;
      if (num1 < num2) return -1;
    }

    return 0;
  }

  /**
   * 检查是否需要强制更新
   * @param {string} currentVersion 当前版本
   * @param {string} serverVersion 服务器版本
   * @returns {boolean} 是否需要更新
   */
  static needForceUpdate(currentVersion, serverVersion) {
    return this.compareVersion(currentVersion, serverVersion) < 0;
  }
}

export default VersionUtils;

2. 版本更新服务 (services/versionService.js)

import VersionUtils from '@/utils/version.js';

/**
 * 版本更新服务
 */
class VersionService {
  constructor() {
    this.baseUrl = 'https://your-api-domain.com'; // 替换为你的后端API地址
  }

  /**
   * 检查版本更新
   * @returns {Promise<object>} 版本检查结果
   */
  async checkVersion() {
    try {
      const currentVersion = VersionUtils.getCurrentVersion();
      const systemInfo = VersionUtils.getSystemInfo();

      const response = await uni.request({
        url: `${this.baseUrl}/api/version/check`,
        method: 'POST',
        data: {
          currentVersion,
          platform: systemInfo.platform,
          systemVersion: systemInfo.version
        },
        header: {
          'Content-Type': 'application/json'
        }
      });

      if (response.statusCode === 200 && response.data) {
        const { code, data, message } = response.data;

        if (code === 200) {
          return {
            success: true,
            data: {
              currentVersion,
              serverVersion: data.serverVersion,
              downloadUrl: data.downloadUrl,
              forceUpdate: data.forceUpdate || false,
              updateMessage: data.updateMessage || '发现新版本,请更新到最新版本',
              needUpdate: VersionUtils.needForceUpdate(currentVersion, data.serverVersion)
            }
          };
        } else {
          return {
            success: false,
            message: message || '版本检查失败'
          };
        }
      } else {
        return {
          success: false,
          message: '网络请求失败'
        };
      }
    } catch (error) {
      console.error('版本检查错误:', error);
      return {
        success: false,
        message: '版本检查异常'
      };
    }
  }

  /**
   * 跳转到外部浏览器下载
   * @param {string} downloadUrl 下载地址
   */
  openExternalBrowser(downloadUrl) {
    const systemInfo = VersionUtils.getSystemInfo();

    // #ifdef APP-PLUS
    if (systemInfo.platform === 'ios') {
      // iOS使用Safari浏览器
      plus.runtime.openURL(downloadUrl);
    } else if (systemInfo.platform === 'android') {
      // Android使用系统默认浏览器
      plus.runtime.openURL(downloadUrl);
    }
    // #endif

    // #ifdef H5
    // H5环境直接跳转
    window.open(downloadUrl, '_blank');
    // #endif

    // #ifdef MP
    // 小程序环境提示用户手动打开
    uni.showModal({
      title: '提示',
      content: '请复制链接到浏览器打开:' + downloadUrl,
      showCancel: false
    });
    // #endif
  }
}

export default new VersionService();

3. 强制更新组件 (components/ForceUpdate.vue)

<template>
  <view v-if="showUpdateModal" class="force-update-modal">
    <view class="modal-mask"></view>
    <view class="modal-content">
      <view class="update-icon">
        <text class="iconfont icon-update"></text>
      </view>
      <view class="update-title">发现新版本</view>
      <view class="update-message">{{ updateMessage }}</view>
      <view class="version-info">
        <text>当前版本:{{ currentVersion }}</text>
        <text>最新版本:{{ serverVersion }}</text>
      </view>
      <view class="update-actions">
        <button 
          v-if="!forceUpdate" 
          class="btn-cancel" 
          @click="handleCancel"
        >
          稍后更新
        </button>
        <button 
          class="btn-update" 
          @click="handleUpdate"
        >
          立即更新
        </button>
      </view>
    </view>
  </view>
</template>

<script>
import versionService from '@/services/versionService.js';

export default {
  name: 'ForceUpdate',
  data() {
    return {
      showUpdateModal: false,
      currentVersion: '',
      serverVersion: '',
      downloadUrl: '',
      forceUpdate: false,
      updateMessage: ''
    };
  },
  methods: {
    /**
     * 显示更新弹窗
     * @param {object} versionInfo 版本信息
     */
    showUpdate(versionInfo) {
      this.currentVersion = versionInfo.currentVersion;
      this.serverVersion = versionInfo.serverVersion;
      this.downloadUrl = versionInfo.downloadUrl;
      this.forceUpdate = versionInfo.forceUpdate;
      this.updateMessage = versionInfo.updateMessage;
      this.showUpdateModal = true;
    },

    /**
     * 处理更新
     */
    handleUpdate() {
      if (this.downloadUrl) {
        versionService.openExternalBrowser(this.downloadUrl);
      }

      // 如果是强制更新,不关闭弹窗
      if (!this.forceUpdate) {
        this.hideUpdate();
      }
    },

    /**
     * 处理取消
     */
    handleCancel() {
      this.hideUpdate();
    },

    /**
     * 隐藏更新弹窗
     */
    hideUpdate() {
      this.showUpdateModal = false;
    }
  }
};
</script>

<style scoped>
.force-update-modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9999;
}

.modal-mask {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.6);
}

.modal-content {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 280px;
  background-color: #fff;
  border-radius: 12px;
  padding: 24px;
  text-align: center;
}

.update-icon {
  margin-bottom: 16px;
}

.update-icon .iconfont {
  font-size: 48px;
  color: #007aff;
}

.update-title {
  font-size: 18px;
  font-weight: bold;
  color: #333;
  margin-bottom: 12px;
}

.update-message {
  font-size: 14px;
  color: #666;
  line-height: 1.5;
  margin-bottom: 16px;
}

.version-info {
  background-color: #f5f5f5;
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 20px;
}

.version-info text {
  display: block;
  font-size: 12px;
  color: #999;
  line-height: 1.8;
}

.update-actions {
  display: flex;
  gap: 12px;
}

.btn-cancel,
.btn-update {
  flex: 1;
  height: 40px;
  border-radius: 20px;
  font-size: 14px;
  border: none;
}

.btn-cancel {
  background-color: #f5f5f5;
  color: #666;
}

.btn-update {
  background-color: #007aff;
  color: #fff;
}
</style>

4. 在App.vue中集成 (App.vue)

<script>
import versionService from '@/services/versionService.js';
import ForceUpdate from '@/components/ForceUpdate.vue';

export default {
  components: {
    ForceUpdate
  },
  data() {
    return {
      versionChecked: false
    };
  },
  onLaunch() {
    // App启动时检查版本
    this.checkAppVersion();
  },
  methods: {
    /**
     * 检查App版本
     */
    async checkAppVersion() {
      try {
        const result = await versionService.checkVersion();

        if (result.success && result.data.needUpdate) {
          // 显示更新弹窗
          this.$refs.forceUpdate.showUpdate(result.data);
        }

        this.versionChecked = true;
      } catch (error) {
        console.error('版本检查失败:', error);
        this.versionChecked = true;
      }
    }
  }
};
</script>

<style>
/* 全局样式 */
</style>

5. 使用示例 (pages/index/index.vue)

<template>
  <view class="container">
    <view class="version-info">
      <text>当前版本:{{ currentVersion }}</text>
      <text>系统:{{ systemInfo.platform }} {{ systemInfo.version }}</text>
    </view>

    <button @click="checkVersion" class="check-btn">
      检查更新
    </button>

    <!-- 强制更新组件 -->
    <ForceUpdate ref="forceUpdate" />
  </view>
</template>

<script>
import VersionUtils from '@/utils/version.js';
import versionService from '@/services/versionService.js';
import ForceUpdate from '@/components/ForceUpdate.vue';

export default {
  components: {
    ForceUpdate
  },
  data() {
    return {
      currentVersion: '',
      systemInfo: {}
    };
  },
  onLoad() {
    this.initVersionInfo();
  },
  methods: {
    /**
     * 初始化版本信息
     */
    initVersionInfo() {
      this.currentVersion = VersionUtils.getCurrentVersion();
      this.systemInfo = VersionUtils.getSystemInfo();
    },

    /**
     * 手动检查更新
     */
    async checkVersion() {
      uni.showLoading({
        title: '检查中...'
      });

      try {
        const result = await versionService.checkVersion();

        uni.hideLoading();

        if (result.success) {
          if (result.data.needUpdate) {
            // 显示更新弹窗
            this.$refs.forceUpdate.showUpdate(result.data);
          } else {
            uni.showToast({
              title: '已是最新版本',
              icon: 'success'
            });
          }
        } else {
          uni.showToast({
            title: result.message || '检查失败',
            icon: 'error'
          });
        }
      } catch (error) {
        uni.hideLoading();
        uni.showToast({
          title: '检查失败',
          icon: 'error'
        });
      }
    }
  }
};
</script>

<style scoped>
.container {
  padding: 20px;
}

.version-info {
  background-color: #f5f5f5;
  padding: 16px;
  border-radius: 8px;
  margin-bottom: 20px;
}

.version-info text {
  display: block;
  font-size: 14px;
  color: #333;
  line-height: 1.8;
}

.check-btn {
  width: 100%;
  height: 44px;
  background-color: #007aff;
  color: #fff;
  border-radius: 22px;
  font-size: 16px;
}
</style>

后端API接口格式

你的后端需要提供一个版本检查接口,返回格式如下:

{
  "code": 200,
  "message": "success",
  "data": {
    "serverVersion": "1.2.0",
    "downloadUrl": "https://your-download-url.com",
    "forceUpdate": true,
    "updateMessage": "发现新版本,请更新到最新版本以获得更好的体验"
  }
}

主要功能特点

  1. 自动版本检查 - App启动时自动检查版本

  2. 系统识别 - 自动识别iOS和Android系统

  3. 强制更新 - 支持强制更新和可选更新

  4. 外部浏览器跳转 - iOS跳转Safari,Android跳转系统浏览器

  5. 版本比较 - 智能版本号比较算法

  6. 错误处理 - 完善的错误处理和用户提示

使用说明

  1. 将上述文件复制到你的uniapp项目中

  2. 修改 services/versionService.js 中的 baseUrl 为你的后端API地址

  3. 在 App.vue 中引入 ForceUpdate 组件

  4. 确保后端提供正确的版本检查API接口

这样就完成了完整的版本检查和强制更新功能!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端组件开发

你的钟意将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值