114 lines
2.9 KiB
TypeScript
114 lines
2.9 KiB
TypeScript
|
|
import { defineStore } from 'pinia';
|
|||
|
|
import type {
|
|||
|
|
SiteSettings,
|
|||
|
|
FooterSettings,
|
|||
|
|
SocialLink,
|
|||
|
|
SeoDefaults,
|
|||
|
|
NavItem,
|
|||
|
|
NavAppearance,
|
|||
|
|
ProductSeries,
|
|||
|
|
I18nEntry
|
|||
|
|
} from '~/types/site';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 官网全局站点状态
|
|||
|
|
*
|
|||
|
|
* 在应用启动时一次性拉取全局配置、导航、产品(Mega Menu)与 i18n 词条,
|
|||
|
|
* 供 AppHeader / AppFooter 及各页面共享使用。
|
|||
|
|
*/
|
|||
|
|
export const useSiteStore = defineStore('site', () => {
|
|||
|
|
const { getGlobal, getNav, getI18n, getProducts } = useSiteApi();
|
|||
|
|
const { locale } = useI18n();
|
|||
|
|
|
|||
|
|
// ===== 全局配置 =====
|
|||
|
|
const site = ref<SiteSettings | null>(null);
|
|||
|
|
const footer = ref<FooterSettings | null>(null);
|
|||
|
|
const social = ref<SocialLink[]>([]);
|
|||
|
|
const seo = ref<SeoDefaults | null>(null);
|
|||
|
|
|
|||
|
|
// ===== 导航 =====
|
|||
|
|
const navItems = ref<NavItem[]>([]);
|
|||
|
|
const navAppearance = ref<NavAppearance | null>(null);
|
|||
|
|
|
|||
|
|
// ===== 产品(导航 Mega Menu) =====
|
|||
|
|
const productSeries = ref<ProductSeries[]>([]);
|
|||
|
|
|
|||
|
|
// ===== i18n 词条(dict_key -> entry) =====
|
|||
|
|
const i18nMap = ref<Record<string, I18nEntry>>({});
|
|||
|
|
|
|||
|
|
const loaded = ref(false);
|
|||
|
|
const loading = ref(false);
|
|||
|
|
|
|||
|
|
/** 应用启动时调用,并行拉取全局数据 */
|
|||
|
|
async function init() {
|
|||
|
|
if (loaded.value || loading.value) return;
|
|||
|
|
loading.value = true;
|
|||
|
|
try {
|
|||
|
|
const [globalRes, navRes, i18nRes, productsRes] = await Promise.all([
|
|||
|
|
getGlobal(),
|
|||
|
|
getNav(),
|
|||
|
|
getI18n(),
|
|||
|
|
getProducts()
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
if (globalRes) {
|
|||
|
|
site.value = globalRes.site;
|
|||
|
|
footer.value = globalRes.footer;
|
|||
|
|
social.value = globalRes.social ?? [];
|
|||
|
|
seo.value = globalRes.seo;
|
|||
|
|
}
|
|||
|
|
if (navRes) {
|
|||
|
|
navItems.value = navRes.items ?? [];
|
|||
|
|
navAppearance.value = navRes.appearance;
|
|||
|
|
}
|
|||
|
|
if (i18nRes) {
|
|||
|
|
const map: Record<string, I18nEntry> = {};
|
|||
|
|
for (const entry of i18nRes.items ?? []) {
|
|||
|
|
map[entry.dict_key] = entry;
|
|||
|
|
}
|
|||
|
|
i18nMap.value = map;
|
|||
|
|
}
|
|||
|
|
if (productsRes) {
|
|||
|
|
productSeries.value = productsRes.series ?? [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
loaded.value = true;
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 翻译 CMS 动态词条(www_i18n_entries)
|
|||
|
|
* @param key 词条 key,如 'btn.learn_more'
|
|||
|
|
* @param fallback 词条缺失时的回退文案
|
|||
|
|
*/
|
|||
|
|
function td(key: string, fallback = ''): string {
|
|||
|
|
const entry = i18nMap.value[key];
|
|||
|
|
if (!entry) return fallback || key;
|
|||
|
|
const value = locale.value === 'en' ? entry.value_en : entry.value_zh;
|
|||
|
|
return value || fallback || key;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 根据当前语言取导航入口名称 */
|
|||
|
|
function navLabel(item: NavItem): string {
|
|||
|
|
return locale.value === 'en' ? item.name_en : item.name_zh;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
site,
|
|||
|
|
footer,
|
|||
|
|
social,
|
|||
|
|
seo,
|
|||
|
|
navItems,
|
|||
|
|
navAppearance,
|
|||
|
|
productSeries,
|
|||
|
|
i18nMap,
|
|||
|
|
loaded,
|
|||
|
|
loading,
|
|||
|
|
init,
|
|||
|
|
td,
|
|||
|
|
navLabel
|
|||
|
|
};
|
|||
|
|
});
|