congming_huose-apk/common/utils/appVersion.js

204 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 应用版本:与 manifest 发版时尽量保持 versionName/versionCode 一致(非 App 端会回退到默认值)。
*
* 本地待更新信息:写入 uni.setStorageSync(APP_PENDING_UPDATE_STORAGE_KEY, 对象或 JSON 字符串)
* 形状:{ versionName, versionCode?, changelog?, downloadUrl?, packageSize? }
* packageSize 示例「128 MB」「7.13 GB」展示在标题下灰色副标题
* 与 getCurrentAppVersionInfo() 比较,若本地版本更新则展示「可更新」界面。
*/
export const APP_PENDING_UPDATE_STORAGE_KEY = 'APP_PENDING_UPDATE'
export function readPendingUpdateFromStorage() {
try {
const raw = uni.getStorageSync(APP_PENDING_UPDATE_STORAGE_KEY)
if (raw == null || raw === '') return null
if (typeof raw === 'object' && raw !== null && !Array.isArray(raw)) return raw
if (typeof raw === 'string') {
const o = JSON.parse(raw)
return o && typeof o === 'object' && !Array.isArray(o) ? o : null
}
} catch (e) {}
return null
}
function parseVersionNameToComparable(name) {
if (!name || typeof name !== 'string') return 0
const m = name.trim().match(/^(\d+)\.(\d+)\.(\d+)/)
if (!m) return 0
const a = parseInt(m[1], 10) || 0
const b = parseInt(m[2], 10) || 0
const c = parseInt(m[3], 10) || 0
return a * 10000 + b * 100 + c
}
export function effectiveVersionComparable(info) {
if (!info) return 0
const n = Number(info.versionCode)
if (!Number.isNaN(n) && n > 0) return n
const fromName = parseVersionNameToComparable(info.versionName)
return fromName > 0 ? fromName : 0
}
/** 版本号字符串规范化后比较(与接口 `version`、本地存储一致) */
export function normalizeVersionString(s) {
if (s == null) return ''
return String(s).trim()
}
function stripHtmlToPlain(html) {
if (!html || typeof html !== 'string') return ''
return html
.replace(/<\/p>/gi, '\n')
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<[^>]+>/g, '')
.replace(/\r\n/g, '\n')
.replace(/\n{3,}/g, '\n\n')
.trim()
}
/** 当前运行包版本App 读 runtime小程序读账号信息其余与 manifest 默认一致) */
export function getCurrentAppVersionInfo() {
let versionName = '1.0.2'
let versionCode = 102
// #ifdef APP-PLUS
try {
if (typeof plus !== 'undefined' && plus.runtime) {
versionName = plus.runtime.version || versionName
const vc = plus.runtime.versionCode
if (vc != null && vc !== '') {
const parsed = parseInt(String(vc), 10)
if (!Number.isNaN(parsed)) versionCode = parsed
}
}
} catch (e) {}
// #endif
// #ifdef MP-WEIXIN
try {
const acc = uni.getAccountInfoSync && uni.getAccountInfoSync()
if (acc && acc.miniProgram && acc.miniProgram.version) {
versionName = acc.miniProgram.version
const parsed = parseVersionNameToComparable(versionName)
if (parsed > 0) versionCode = parsed
}
} catch (e) {}
// #endif
return {
versionName: String(versionName),
versionCode: Number(versionCode) || 0
}
}
/** 与当前包比较pending 是否为更高版本(本地存储或任意来源的待更新描述对象) */
export function remoteHasNewerVersion(currentInfo, pending) {
if (!pending || typeof pending !== 'object') return false
const c = effectiveVersionComparable(currentInfo)
const r = effectiveVersionComparable({
versionName: pending.versionName,
versionCode: pending.versionCode
})
if (r <= 0) return false
return r > c
}
function isTrivialEmptyHtml(html) {
if (!html || typeof html !== 'string') return true
const t = html.replace(/\s/g, '').toLowerCase()
return (
t === '' ||
t === '<p></p>' ||
t === '<p><br></p>' ||
t === '<p><br/></p>' ||
t === '<br>' ||
t === '<br/>'
)
}
/** 富文本更新说明(接口 `updateContent`,原样供 rich-text 解析) */
export function pickUpdateContentHtml(remote) {
if (!remote || typeof remote !== 'object') return ''
const raw = remote.updateContent
if (typeof raw !== 'string') return ''
const trimmed = raw.trim()
if (!trimmed || isTrivialEmptyHtml(trimmed)) return ''
return trimmed
}
/** 纯文本更新说明(不含 updateContent避免与富文本重复 */
export function pickRemoteChangelog(remote) {
if (!remote || typeof remote !== 'object') return ''
const raw =
remote.changelog ||
remote.description ||
remote.releaseNotes ||
remote.intro ||
''
if (typeof raw !== 'string') return ''
return stripHtmlToPlain(raw)
}
export function pickDownloadUrl(remote) {
if (!remote || typeof remote !== 'object') return ''
const u =
remote.downloadUrl ||
remote.apkUrl ||
remote.apkDownloadUrl ||
remote.fileUrl ||
remote.url ||
remote.androidUrl ||
remote.iosUrl ||
''
return typeof u === 'string' ? u.trim() : ''
}
/** 从接口返回的单条记录里取版本号展示名 */
export function pickVersionNameFromRecord(row) {
if (!row || typeof row !== 'object') return ''
const v =
row.versionName ??
row.version ??
row.apkVersion ??
row.appVersion ??
row.newVersion ??
''
if (typeof v === 'string') return v.trim()
if (v != null && v !== '') return String(v).trim()
return ''
}
function pickVersionCodeFromRecord(row) {
if (!row || typeof row !== 'object') return 0
const raw = row.versionCode ?? row.build ?? row.versionNum
const n = Number(raw)
return Number.isNaN(n) ? 0 : n
}
/**
* 解析 GET /app/apkVersion/latest 等接口的响应体,得到与本地 pending 相同形状的待更新对象。
* 兼容data 为对象 / 数组首项 / rows 首项。
*/
export function extractLatestApkRecord(httpResponse) {
if (!httpResponse || typeof httpResponse !== 'object') return null
let row = httpResponse.data
if (Array.isArray(row)) row = row[0]
else if (row && typeof row === 'object' && Array.isArray(row.rows)) row = row.rows[0]
if (!row || typeof row !== 'object' || Array.isArray(row)) return null
const versionName = pickVersionNameFromRecord(row)
const versionCode = pickVersionCodeFromRecord(row)
if (!versionName && versionCode <= 0) return null
return {
versionName,
versionCode,
changelog: pickRemoteChangelog(row),
updateContentHtml: pickUpdateContentHtml(row),
downloadUrl: pickDownloadUrl(row),
packageSize: pickPackageSizeLabel(row)
}
}
/** 可选展示用安装包大小如「128 MB」「7.13 GB」 */
export function pickPackageSizeLabel(remote) {
if (!remote || typeof remote !== 'object') return ''
const s = remote.packageSize || remote.size || remote.fileSize || ''
return typeof s === 'string' ? s.trim() : ''
}