chore:悬浮按钮可以后台自定义调节

This commit is contained in:
2026-10-09 10:46:40 +08:00
parent 356a6d2bbf
commit c36d8dbce6
2 changed files with 89 additions and 12 deletions

View File

@@ -13,7 +13,7 @@
<view
v-if="isLanguageSwitchEnabled && hasLangButton"
class="btn-item common-bg"
:style="[btnItemStyle, { order: orderIndex.lang }]"
:style="[btnItemStyle, { order: orderIndex.lang }, perBtnStyle.lang]"
@click="handleLangButtonClick"
>
<view class="lang-btn-text">{{ langButtonText }}</view>
@@ -22,7 +22,7 @@
<button
v-if="hasKefuButton && hasValidKefuConfig && isWeappOfficialKefu"
class="btn-item common-bg"
:style="[btnItemStyle, { order: orderIndex.kefu }]"
:style="[btnItemStyle, { order: orderIndex.kefu }, perBtnStyle.kefu]"
open-type="contact"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
@@ -32,7 +32,7 @@
<view
v-if="hasKefuButton && hasValidKefuConfig && !isWeappOfficialKefu && !showWeappSystemKefu"
class="btn-item common-bg"
:style="[btnItemStyle, { order: orderIndex.kefu }]"
:style="[btnItemStyle, { order: orderIndex.kefu }, perBtnStyle.kefu]"
@click="handleUnifiedKefuClick"
>
<image
@@ -50,7 +50,7 @@
<view
v-if="hasKefuButton && hasValidKefuConfig && !isWeappOfficialKefu && showWeappSystemKefu"
class="btn-item common-bg"
:style="[btnItemStyle, { order: orderIndex.kefu }]"
:style="[btnItemStyle, { order: orderIndex.kefu }, perBtnStyle.kefu]"
@click="handleUnifiedKefuClick"
>
<image
@@ -68,7 +68,7 @@
<button
v-if="hasKefuButton && hasValidKefuConfig && showWeappSystemKefu"
class="btn-item common-bg"
:style="[btnItemStyle, { order: orderIndex.kefu }]"
:style="[btnItemStyle, { order: orderIndex.kefu }, perBtnStyle.kefu]"
open-type="contact"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
@@ -78,7 +78,7 @@
<view
v-if="hasPhoneButton"
class="btn-item common-bg"
:style="[btnItemStyle, { order: orderIndex.phone }, { backgroundImage: phoneimg ? `url(${phoneimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
:style="[btnItemStyle, { order: orderIndex.phone }, { backgroundImage: phoneimg ? `url(${phoneimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle, perBtnStyle.phone]"
@click="call"
>
<text class="iconfont icon-dianhua" v-if="!phoneimg"></text>
@@ -175,6 +175,10 @@ export default {
return {
pageCount: 0,
currentRoute: '', // 当前页面路由(onShow / created 时刷新, 供页面范围判定)
// 每个按钮的专属样式(来源: 主题 token hover_nav_{key}_bg_color/_text_color/_size,
// 由 _parsePerButtonStyle 从 themeColor 的 CSS 变量串解析, 全平台可用)
// key: lang/kefu/phone, value: { backgroundColor?, color?, width?... } , 空 = 走全局 token
perBtnStyle: { lang: {}, kefu: {}, phone: {} },
shopInfo: null,
currentLangIndex: 0,
langIndexMap: {},
@@ -365,11 +369,24 @@ export default {
const b = this.visibleButtons;
if (b && b.length === 0) return '0rpx';
const extraKefuSlot = (this.hasKefuButton && this.showWeappSystemKefu) ? 1 : 0;
const buttonCount = b
? b.length + extraKefuSlot
: 1 + (this.isLanguageSwitchEnabled ? 1 : 0) + extraKefuSlot;
const totalRpx = buttonCount * 100 + 40;
// 汇总实际渲染的按钮列表 (b 存在 = 新配置; null = 老行为兜底, 与旧计数等价)
const rendered = [];
if (b) {
for (let i = 0; i < b.length; i++) rendered.push(b[i]);
} else {
if (this.isLanguageSwitchEnabled) rendered.push('lang');
rendered.push('kefu');
if (this.showWeappSystemKefu) rendered.push('kefu');
rendered.push('phone');
}
// 槽位高度按每个按钮的实际尺寸算(72rpx 默认 + 28rpx 上下 margin),
// token 单独调大某个按钮时容器高度自动跟随, 不出现裁切/空白
let slotsRpx = 0;
for (let i = 0; i < rendered.length; i++) {
slotsRpx += this.btnSizeRpx(rendered[i]) + 28;
}
const totalRpx = slotsRpx + 40;
return `${totalRpx}rpx`;
},
@@ -624,6 +641,9 @@ export default {
* 内部会去重,不会频繁触发)。
*/
_applyThemeColor(tc) {
// 0) 解析每个按钮的专属 token (颜色/尺寸) — 必须放在 $el 早退之前,
// 小程序端 $el 可能不可用, 但 inline-style 解析不依赖 DOM, 全平台都要执行
this._parsePerButtonStyle(tc);
if (!this.$el || !this.$el.style) return;
// 1) 剥离旧注入(避免重复 + 处理主题切换场景)
if (this._appliedThemeColor) {
@@ -640,6 +660,63 @@ export default {
this.$el.style.cssText = (this.$el.style.cssText || '') + ';' + tc;
}
},
/**
* 从 themeColor 的 CSS 变量串(--xxx:value;...)中解析每个悬浮按钮的专属 token:
* --hover-nav-{key}-bg-color → backgroundColor
* --hover-nav-{key}-text-color → color
* --hover-nav-{key}-size → width/height/min-max 等尺寸属性
* key ∈ lang / kefu / phone; token 未配置(或空值被 sanitize 剔除)时对应样式为空,
* CSS/inline 回退到全局 hover_nav_bg_color / hover_nav_text_color / 72rpx。
*/
_parsePerButtonStyle(tc) {
const result = { lang: {}, kefu: {}, phone: {} };
if (tc && typeof tc === 'string') {
const pick = (name) => {
const m = tc.match(new RegExp('--hover-nav-' + name + '\\s*:\\s*([^;]+);'));
return m ? m[1].trim() : '';
};
for (const key of ['lang', 'kefu', 'phone']) {
const s = {};
const bg = pick(key + '-bg-color');
const text = pick(key + '-text-color');
const size = pick(key + '-size');
if (bg) s.backgroundColor = bg;
if (text) s.color = text;
if (size) {
s.width = size;
s.height = size;
s['min-width'] = size;
s['min-height'] = size;
s['max-width'] = size;
s['max-height'] = size;
}
result[key] = s;
}
}
this.perBtnStyle = result;
},
/**
* 取某个按钮当前的尺寸数值(rpx 基准, 供容器高度计算)。
* token 值可能是 rpx(小程序/H5 主题串原样)或 px(H5 端 themeColorSet 已把 rpx 换算成 px),
* px 时按 windowWidth 折算回 rpx; 无 token 时回退默认 72rpx。
*/
btnSizeRpx(key) {
const s = this.perBtnStyle && this.perBtnStyle[key];
const v = s && s.width;
if (!v) return 72;
const m = String(v).match(/^([\d.]+)\s*(rpx|px)?$/i);
if (!m) return 72;
let n = parseFloat(m[1]);
if ((m[2] || 'rpx').toLowerCase() === 'px') {
try {
const w = (uni.getSystemInfoSync() || {}).windowWidth;
if (w) n = n * 750 / w;
} catch (e) { /* 保持原值 */ }
}
return n;
},
initLanguage() {
// 数据源优先级:
// 1) shopInfo.langList(后端按租户/店铺动态下发的语言配置,推荐)