diff --git a/common/js/config-external.js b/common/js/config-external.js index b380f61..eae8989 100644 --- a/common/js/config-external.js +++ b/common/js/config-external.js @@ -158,6 +158,21 @@ export const privacyConfig = { // }, }; +// ============================================================ +// 启动屏配置 (与 privacyConfig 同构, 由 splash-state.js 消费) +// showProgress 是否显示进度条本身 + 数字 (label 单独可独立关闭) +// showLabel 是否显示阶段文案 (Loading language... 等) +// showPercent 是否显示百分比数字 +// showProgress=true + showLabel=false + showPercent=true → 只显示进度条+百分比 +// showProgress=false → 整个进度条区域隐藏 +// 业务侧可改 splashConfig.* 或在 splash 组件 覆盖 +// ============================================================ +export const splashConfig = { + showProgress: true, + showLabel: false, // 默认 false: 用户想要"只显示百分比" + showPercent: true, +} + // 配置外置管理类 class ConfigExternal { constructor() { diff --git a/common/js/splash-state.js b/common/js/splash-state.js index 5f66b6f..5e2e363 100644 --- a/common/js/splash-state.js +++ b/common/js/splash-state.js @@ -10,8 +10,12 @@ // 4. 兜底超时: 默认 8s 未关闭则强制 hide,防止卡死 // 5. 平台隔离: APP-PLUS 有原生 splash,这里直接返回 false // 6. 跨平台兼容: 同步状态可被 Vue 组件直接 watch +// 7. 可配置: splashConfig 支持 showProgress/showLabel/showPercent 单独控制 +// 业务侧可在 config-external.js 改 splashConfig, 或 覆盖 // ============================================================ +import { splashConfig as _defaultSplashConfig } from './config-external.js' + // APP-PLUS 直接返回 false (有原生启动屏) function needSplash() { // #ifdef APP-PLUS @@ -67,6 +71,8 @@ class SplashState { // 避免多通道并发时, splash 组件 re-render 风暴 this._pendingProgress = null; // 待 emit 的最新进度 this._flushTimer = null; // 节流定时器 + // 启动屏显示配置: 初始读 config-external.js 的 splashConfig, 可运行时改 + this._splashConfig = Object.assign({}, _defaultSplashConfig) } show(opts = {}) { @@ -343,6 +349,33 @@ getStageDef(stageId) { return STAGES[stageId] } +/** + * 读取启动屏显示配置 + * splash 组件读这个决定 label/percent 是否显示 + * 缺字段时回退到默认 (splashConfig=true) + * @returns {{showProgress: boolean, showLabel: boolean, showPercent: boolean}} + */ +getSplashConfig() { + const c = this._splashConfig || {} + return { + showProgress: c.showProgress !== false, + showLabel: !!c.showLabel, + showPercent: c.showPercent !== false, + } +} + + +/** + * 运行时合并更新 splash 配置 + * 全局 splashConfig 在 boot 前注入; 业务运行时改某些字段可用本方法 + * 不会触发组件 re-render (splash 组件用 props 优先) + * @param {Object} partial + */ +setSplashConfig(partial) { + if (!partial || typeof partial !== 'object') return + this._splashConfig = Object.assign({}, this._splashConfig, partial) +} + /** * 立即刷新挂起的进度更新 * - reportProgress 默认 16ms throttle (1 帧), 避免连续触发 splash 组件多次 re-render diff --git a/components/splash-screen/splash-screen.vue b/components/splash-screen/splash-screen.vue index 654795e..6b4bf72 100644 --- a/components/splash-screen/splash-screen.vue +++ b/components/splash-screen/splash-screen.vue @@ -23,14 +23,28 @@ - - - - + + + + + + + + {{ percent }}% - - {{ stageLabel }} - {{ percent }}% + + + + + + + {{ stageLabel }} + {{ percent }}% + @@ -42,6 +56,23 @@ export default { name: 'splash-screen', + // props 优先级: 页面级 > splashState.getSplashConfig() + // 任何 prop 为 undefined 时回退全局, 给业务侧最大灵活性 + // 注意: prop 命名前缀 'display' 避免与未来 splash-state 内部字段同名引起 Vue prop-mutation 误判 + props: { + hideProgress: { + type: Boolean, + default: undefined + }, + hideLabel: { + type: Boolean, + default: undefined + }, + hidePercent: { + type: Boolean, + default: undefined + }, + }, data() { return { // 默认 visible=true: 让 splash 组件一渲染就可见, 减少"白屏闪烁" @@ -51,16 +82,32 @@ leaving: false, percent: 0, stage: 'boot', - showProgress: false, + // 内部 showProgress 标志 (保留兼容, 由 created() 维护) + // 实际模板用 displayConfig.showProgress 决定, 这个内部标志走原逻辑 + _showProgressLegacy: false, }; }, computed: { // 直接从 splashState.STAGES 读取文案, 避免重复维护 STAGE_LABEL - // (splash-state.js 暴露 getStageDef() 避免外部直接读 STAGES) stageLabel() { const def = splashState.getStageDef(this.stage) return def ? def.label : this.stage }, + // 显示配置: props 优先 (hide 语义, false=show), 否则回退全局 splashConfig + // 注意: splashConfig 的 show* 是肯定语义 (true=show), 我们这里 hide* 取反 + displayConfig() { + const cfg = splashState.getSplashConfig() + return { + showProgress: this.hideProgress !== undefined ? !this.hideProgress : cfg.showProgress, + showLabel: this.hideLabel !== undefined ? !this.hideLabel : cfg.showLabel, + showPercent: this.hidePercent !== undefined ? !this.hidePercent : cfg.showPercent, + } + }, + // inline 布局: 只显示百分比 + 进度条 (label 隐藏) + // 此时进度条和百分比在同一水平线 (flex) + inlineMode() { + return this.displayConfig.showPercent && !this.displayConfig.showLabel + }, }, // created (而非 mounted): 提前同步 splashState 状态, 减少"白屏一帧" created() { @@ -68,7 +115,10 @@ const init = splashState.getProgress() this.percent = init.percent this.stage = init.stage - this.showProgress = init.percent > 0 + // _showProgressLegacy 控制 progress-fade-in CSS 动画触发 + // (true → 触发 CSS 关键帧; false → 隐藏整个进度区) + // 模板用 displayConfig.showProgress 综合 props + splashConfig 决定 + this._showProgressLegacy = init.percent > 0 } catch (e) {} // 同步 splashState 状态: 防止组件挂载在 splash 已 hide 之后误显示 if (!splashState.isShow()) { @@ -76,34 +126,34 @@ } }, mounted() { - splashState.notifyMounted(); - splashState.on('splash:show', () => { - this.visible = true; - this.leaving = false; - // 注意: 不要在这里再读 getProgress(). percent 不允许后退 - // 已通过 created() 同步; emit 进来多是无变化 - const init = splashState.getProgress() - if (init.percent > this.percent) this.percent = init.percent - if (init.stage !== this.stage) this.stage = init.stage - this.showProgress = true - }); - splashState.on('splash:hide', (info) => { - // 进度条补完到 100% (视觉上"完成"), 立即 leave - if (this.percent < 100) { - this.percent = 100; - this.stage = 'done'; - } - // mode='fast' (隐私弹窗即将出现) → 80ms 短动画让 modal 更快接管视觉 - const fast = info && info.mode === 'fast' - this._leave(fast) - }); - splashState.onProgress((info) => { - // 去重: percent/stage 没变化就不 setState, 避免空 re-render - if (info.percent !== this.percent) this.percent = info.percent - if (info.stage !== this.stage) this.stage = info.stage - this.showProgress = true; - }); - }, + splashState.notifyMounted(); + splashState.on('splash:show', () => { + this.visible = true; + this.leaving = false; + // 注意: 不要在这里再读 getProgress(). percent 不允许后退 + // 已通过 created() 同步; emit 进来多是无变化 + const init = splashState.getProgress() + if (init.percent > this.percent) this.percent = init.percent + if (init.stage !== this.stage) this.stage = init.stage + this._showProgressLegacy = true + }); + splashState.on('splash:hide', (info) => { + // 进度条补完到 100% (视觉上"完成"), 立即 leave + if (this.percent < 100) { + this.percent = 100; + this.stage = 'done'; + } + // mode='fast' (隐私弹窗即将出现) → 80ms 短动画让 modal 更快接管视觉 + const fast = info && info.mode === 'fast' + this._leave(fast) + }); + splashState.onProgress((info) => { + // 去重: percent/stage 没变化就不 setState, 避免空 re-render + if (info.percent !== this.percent) this.percent = info.percent + if (info.stage !== this.stage) this.stage = info.stage + this._showProgressLegacy = true; + }); + }, beforeDestroy() { splashState.off('splash:show'); splashState.off('splash:hide'); @@ -187,6 +237,18 @@ background: rgba($accent, 0.08); border-radius: 3rpx; overflow: hidden; + + // inline 模式 (showLabel=false, showPercent=true): 进度条占主宽, 比例调整 + &--inline { + flex: 1; + min-width: 0; + } + + // full 模式: 原始整条进度条 + &--full { + // 默认宽度 100% 由父容器 .splash-screen__progress 提供 + width: 100%; + } } .splash-screen__progress-fill { @@ -200,6 +262,15 @@ box-shadow: 0 0 12rpx rgba($accent, 0.4); } + // 新增: inline row 布局 (showLabel=false + showPercent=true) + // 进度条和百分比在同一水平线: 进度条 + 右侧百分比 + .splash-screen__progress-row { + display: flex; + align-items: center; + gap: 12rpx; + width: 100%; + } + .splash-screen__progress-text { display: flex; justify-content: space-between; @@ -220,6 +291,14 @@ font-variant-numeric: tabular-nums; font-weight: 500; color: $accent; + + // inline 模式: 字号略大, 与进度条同行 + &--inline { + flex-shrink: 0; + font-size: 24rpx; + min-width: 72rpx; + text-align: right; + } } @keyframes progress-fade-in {