Files
lucky_shop/store/index.js
ZF sun c01a026149 perf(store, globalConfig): 优化多语言刷新和主题颜色缓存
1.  修改全局配置的多语言刷新逻辑,新增参数控制全量刷新时机
2.  为主题颜色生成添加缓存,避免重复遍历主题对象计算CSS变量
3.  从本地存储初始化主题配置,持久化主题状态
2026-06-16 17:34:09 +08:00

513 lines
15 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.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import Http from '../common/js/http.js'
import { themeConfig } from '../common/js/config-external.js'
import configExternal from '../common/js/config-external.js'
import util from '../common/js/util.js'
import ThemeOverride from '../common/js/theme-override.js'
import { sanitizeFlatMap, mergeThemeBuckets } from '../common/js/merge-theme.js'
// themeColor 结果缓存(避免每次都全量遍历 theme 对象)
// key: JSON.stringify(theme) + tabBarHeight, value: CSS变量字符串
let _themeColorCache = null;
let _themeColorCacheKey = '';
const store = new Vuex.Store({
state: {
token: null,
siteInfo: null,
memberInfo: null,
tabBarList: null,
siteState: 1,
themeStyle: (function() { try { return uni.getStorageSync('themeStyle') || ''; } catch(e) { return ''; } })(),
themeColor: (function() { try { return uni.getStorageSync('themeColor') || ''; } catch(e) { return ''; } })(),
addonIsExist: {
bundling: 0,
coupon: 0,
discount: 0,
fenxiao: 0,
gift: 0,
groupbuy: 0,
manjian: 0,
memberconsume: 0,
memberrecharge: 0,
memberregister: 0,
membersignin: 0,
memberwithdraw: 0,
memberrecommend: 0,
pintuan: 0,
pointexchange: 0,
seckill: 0,
store: 0,
topic: 0,
bargain: 0,
membercancel: 0,
servicer: 0,
supermember: 0,
giftcard: 0,
divideticket: 0,
scenefestival: 0,
birthdaygift: 0,
pinfan: 0,
form: 0
},
authInfo: {}, // 授权信息
flRefresh: 0,
location: null, // 定位信息
defaultImg: {
goods: '',
head: '',
store: '',
article: '',
aiAgent: ''
},
cartList: {},
cartIds: [],
cartNumber: 0,
cartMoney: 0,
cartChange: 0,
wechatConfigStatus: 0,
bottomNavHidden: false, // 底部导航是否隐藏,true:隐藏,false:显示
aiUnreadCount: 10, // AI未读消息数量
globalAIKefuConfig: null, // AI客服配置
customerServiceType: 'ai',
globalStoreConfig: null, // 门店配置
globalStoreInfo: null, // 门店信息
defaultStoreInfo: null, // 默认门店
cartPosition: null, // 购物车所在位置
componentRefresh: 0, // 组件刷新
servicerConfig: null, // 客服配置
wxworkConfig: null, // 企业微信配置
diySeckillInterval: 0,
diyGroupPositionObj: {},
diyGroupShowModule: '',
tabBarHeight: '56px',
packageEntryTab: {}, // 追踪每个分包的入口Tab { 'pages_goods': '/pages_goods/category' }
mapConfig: {
tencent_map_key: '',
wap_is_open: 1,
wap_valid_time: 0,
},
copyright: null,
initStatus: false,
offlineWhiteList: ['pages_order/payment', 'pages_order/list', 'pages_order/detail'],//线下支付白名单
canReceiveRegistergiftInfo: {
status: false,
path: ''
},
},
mutations: {
// 设置是否可以领取新人礼
setCanReceiveRegistergiftInfo(state, data) {
state.canReceiveRegistergiftInfo = data;
},
// 设置那些组件展示
setDiyGroupShowModule(state, data) {
state.diyGroupShowModule = data;
},
// 设置diyGroup中组件原有高度,通过他们来实现在首页的定位
setDiyGroupPositionObj(state, data) {
state.diyGroupPositionObj = Object.assign({}, state.diyGroupPositionObj, data);
},
setSiteState(state, siteStateVal) {
state.siteState = siteStateVal;
},
setThemeStyle(state, value) {
state.themeStyle = value
uni.setStorageSync('themeStyle', value); // 初始化数据调用
},
setTabBarList(state, value) {
state.tabBarList = value;
},
setAddonIsExist(state, value) {
state.addonIsExist = value;
uni.setStorageSync('addonIsExist', value); // 初始化数据调用
},
setToken(state, value) {
state.token = value;
if (value) {
uni.setStorageSync('token', value); // 初始化数据调用
} else {
uni.removeStorageSync('token');
}
},
setAuthinfo(state, value) {
state.authInfo = value;
},
setflRefresh(state, flRefreshVal) {
state.flRefresh = flRefreshVal;
},
setLocation(state, value) {
var date = new Date();
date.setSeconds(60 * state.mapConfig.wap_valid_time);
value.valid_time = date.getTime() / 1000; // 定位信息 5分钟内有效,过期后将重新获取定位信息
state.location = value;
uni.setStorageSync('location', value); // 初始化数据调用
},
setDefaultImg(state, value) {
state.defaultImg = value;
uni.setStorageSync('defaultImg', value); // 初始化数据调用
},
setSiteInfo(state, value) {
state.siteInfo = value;
uni.setStorageSync('siteInfo', value); // 初始化数据调用
},
setShopInfo(state, value) {
state.shopInfo = value;
uni.setStorageSync('shopInfo', value); // 初始化数据调用
},
setCartChange(state) {
state.cartChange += 1;
},
setBottomNavHidden(state, value) {
state.bottomNavHidden = value;
},
setglobalAIKefuConfig(state, value) {
state.globalAIKefuConfig = value;
uni.setStorageSync('globalAIKefuConfig', value); // 初始化数据调用
},
setCustomerServiceType(state, type) {
state.customerServiceType = type;
},
setGlobalStoreConfig(state, value) {
state.globalStoreConfig = value;
uni.setStorageSync('globalStoreConfig', value); // 初始化数据调用
},
setGlobalStoreInfo(state, value) {
state.globalStoreInfo = value;
uni.setStorageSync('globalStoreInfo', value); // 初始化数据调用
},
setDefaultStoreInfo(state, value) {
state.defaultStoreInfo = value;
uni.setStorageSync('defaultStoreInfo', value); // 初始化数据调用
},
setCartPosition(state, value) {
state.cartPosition = value;
},
setComponentRefresh(state) {
state.componentRefresh += 1;
},
// 客服配置
setServicerConfig(state, value) {
state.servicerConfig = value;
uni.setStorageSync('servicerConfig', value);
},
// 企业微信配置
setWxworkConfig(state, value) {
state.wxworkConfig = value;
uni.setStorageSync('wxworkConfig', value);
},
setDiySeckillInterval(state, value) {
state.diySeckillInterval = value;
},
setTabBarHeight(state, value) {
state.tabBarHeight = value;
},
setPackageEntryTab(state, value) {
state.packageEntryTab = value;
},
setMapConfig(state, value) {
state.mapConfig = value;
uni.setStorageSync('mapConfig', value);
},
setCopyright(state, value) {
state.copyright = value;
uni.setStorageSync('copyright', value);
},
setMemberInfo(state, value) {
// 会员被锁定后,清除会员登录信息
if (value && value.status == 0) {
value = null;
}
state.memberInfo = value;
if (value) {
uni.setStorageSync('memberInfo', value);
} else {
// 会员为空时,清除会员登录信息
uni.removeStorageSync('memberInfo');
this.commit('setToken', '');
this.dispatch('emptyCart');
//uni.removeStorageSync('authInfo');
}
},
setCartNumber(state, cartNumber) {
state.cartNumber = cartNumber
},
setCartList(state, value) {
state.cartList = value;
},
setCartIds(state, value) {
state.cartIds = value;
},
setCartMoney(state, value) {
state.cartMoney = value;
},
setInitStatus(state, value) {
state.initStatus = value
},
setWechatConfigStatus(state, value) {
state.wechatConfigStatus = value
},
// 设置AI未读消息数量
setAiUnreadCount(state, value) {
state.aiUnreadCount = value;
},
// 设置主题颜色
setThemeColor(state, value) {
state.themeColor = value;
}
},
getters: {
// AI智能助手配置
globalAIKefuConfig: state => state.globalAIKefuConfig,
// AI未读消息数量
aiUnreadCount: state => state.aiUnreadCount,
},
actions: {
init() {
return new Promise(async (resolve, reject) => {
Http.sendRequest({
url: '/api/config/init',
success: async res => {
var data = res.data;
if (data) {
// 走新 action: 加载预设 + 远端覆写合并 + 应用
// (不直接 commit setThemeStyle + themeColorSet,避免漏远端)
await this.dispatch('applyThemeWithRemote', data.style_theme?.name || themeConfig.defaultTheme);
// 底部导航
this.commit('setTabBarList', data.diy_bottom_nav);
this.commit('setAddonIsExist', data.addon_is_exist);
this.commit('setDefaultImg', data.default_img);
this.commit('setSiteInfo', data.site_info);
this.commit('setServicerConfig', data.servicer);
// 企业微信配置
if (data?.wxwork_config) {
this.commit('setWxworkConfig', data.wxwork_config);
}
this.commit('setCopyright', data.copyright);
this.commit('setMapConfig', data.map_config);
this.commit('setglobalAIKefuConfig', data.ai_agent_config);
this.commit('setGlobalStoreConfig', data.store_config);
//联系我们
this.commit('setShopInfo', data.shop_info);
this.commit('setWechatConfigStatus', data?.wechat_config_status);
// 默认总店
if (data.store_info) {
this.commit('setDefaultStoreInfo', data.store_info);
} else {
// 清空不存在的门店信息
this.commit('setDefaultStoreInfo', null);
this.commit('setGlobalStoreInfo', null);
}
this.commit('setInitStatus', true)
resolve(data);
}
}
});
})
},
// 查询购物车列表、总数量、总价格
getCartNumber() {
Http.sendRequest({
url: '/api/cart/lists',
data: {},
success: res => {
if (res.code == 0) {
let list = {};
let ids = [];
let totalMoney = 0;
let totalNum = 0;
if (res.data.length) {
res.data.forEach((item) => {
let cart = {
cart_id: item.cart_id,
goods_id: item.goods_id,
sku_id: item.sku_id,
num: item.num,
discount_price: item.discount_price,
min_buy: item.min_buy,
stock: item.stock,
};
if (!list['goods_' + cart.goods_id]) {
list['goods_' + cart.goods_id] = {};
}
list['goods_' + cart.goods_id]['max_buy'] = item.max_buy;
list['goods_' + cart.goods_id]['goods_name'] = item.goods_name;
list['goods_' + cart.goods_id]['sku_' + cart.sku_id] = cart;
ids.push(cart.cart_id);
});
for (let goods in list) {
let num = 0;
let money = 0;
for (let sku in list[goods]) {
let item = list[goods][sku];
if (typeof item == 'object') {
num += item.num;
money += parseFloat(item.discount_price) * parseInt(item.num);
}
}
list[goods].num = num;
list[goods].total_money = money;
totalNum += num;
totalMoney += money;
}
}
this.commit('setCartList', list);
this.commit('setCartIds', ids);
this.commit('setCartNumber', totalNum);
this.commit('setCartMoney', totalMoney);
}
}
});
},
// 清空购物车 ns-goods-sku-index组件中引用
emptyCart() {
this.commit('setCartList', {});
this.commit('setCartIds', []);
this.commit('setCartNumber', 0);
this.commit('setCartMoney', 0);
},
// 计算购物车数量、价格
cartCalculate() {
let ids = [];
let totalMoney = 0;
let totalNum = 0;
for (let k in this.state.cartList) {
let item = this.state.cartList[k];
let num = 0;
let money = 0;
for (let sku in item) {
if (typeof item[sku] == 'object') {
num += item[sku].num;
money += parseFloat(item[sku].discount_price) * parseInt(item[sku].num);
ids.push(item[sku].cart_id);
}
}
item.num = num;
item.total_money = money;
totalNum += num;
totalMoney += money;
}
this.commit('setCartNumber', totalNum);
this.commit('setCartMoney', totalMoney);
this.commit('setCartIds', ids);
},
// 生成主题颜色CSS变量(带缓存)
themeColorSet() {
let theme = this.state.themeStyle;
if (!theme?.main_color || !theme?.aux_color) return;
try {
const cacheKey = (typeof theme === 'object' ? JSON.stringify(theme) : String(theme)) + '|' + (this.state.tabBarHeight || '');
if (_themeColorCache && _themeColorCacheKey === cacheKey) {
if (this.state.themeColor !== _themeColorCache) {
this.commit('setThemeColor', _themeColorCache);
}
return;
}
let themeColor = `--base-color:${theme.main_color};--base-help-color:${theme.aux_color};`;
if (this.state.tabBarHeight != '56px') themeColor += `--tab-bar-height:${this.state.tabBarHeight};`
Object.keys(theme).forEach(key => {
let data = theme[key];
if (typeof (data) == "object") {
Object.keys(data).forEach(k => {
themeColor += '--' + k.replace(/_/g, "-") + ':' + data[k] + ';';
});
} else if (typeof (key) == "string" && key) {
themeColor += '--' + key.replace(/_/g, "-") + ':' + data + ';';
}
});
for (let i = 9; i >= 5; i--) {
let color = util.colourBlend(theme.main_color, '#ffffff', (i / 10));
themeColor += `--base-color-light-${i}:${color};`;
}
_themeColorCache = themeColor;
_themeColorCacheKey = cacheKey;
this.commit('setThemeColor', themeColor);
} catch (e) {
console.error('设置主题颜色失败', e);
}
},
/**
* 加载预设 + 拉远端覆写 + 深合并 + 走原 setThemeStyle/themeColorSet 链路
* 关键: 应用层一行不动 — Vuex.themeStyle / themeColor / 页面 wrapper / CSS var
* 全部保持兼容,所有改动只发生在"加载"环节
*
* @param {string} presetName style_color.js 中的 preset 名(default/red/green/...)
* @returns {Promise<{ ok: boolean, source: string, applied: object }>}
*/
async applyThemeWithRemote({ state, commit, dispatch }, presetName) {
const finalPresetName = presetName || themeConfig.defaultTheme;
try {
// 1) 加载本地预设(style_color.js)
const base = await configExternal.loadTheme(finalPresetName);
if (!base) {
console.warn('[theme] loadTheme 返回空:', finalPresetName);
return { ok: false, source: 'empty', applied: null };
}
// 2) 拉远端 brand 桶(失败静默,使用本地缓存或空)
const brandOverrides = ThemeOverride.getByTheme('brand') || {};
// 3) 浅合并(主题 token 是扁平叶子,不需要深合并)
// preset 先, brand 后 — 远端覆盖本地
const merged = mergeThemeBuckets(
sanitizeFlatMap(base),
sanitizeFlatMap(brandOverrides)
);
// 4) 走原链路(完全不动下游) — Vuex.themeStyle = 合并后对象
commit('setThemeStyle', merged);
dispatch('themeColorSet');
return { ok: true, source: 'merged', applied: merged };
} catch (e) {
console.error('[theme] applyThemeWithRemote 失败,回退到纯预设:', e);
// 兜底: 至少把预设应用了,避免页面无主题
try {
const base = await configExternal.loadTheme(finalPresetName);
if (base) {
commit('setThemeStyle', base);
dispatch('themeColorSet');
return { ok: false, source: 'preset-only-fallback', applied: base };
}
} catch (e2) {
console.error('[theme] 纯预设回退也失败:', e2);
}
return { ok: false, source: 'fail', applied: null };
}
}
}
})
export default store