|
|
|
|
@@ -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(后端按租户/店铺动态下发的语言配置,推荐)
|
|
|
|
|
|