官网开发中 0731
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 链接解析:区分外部 URL 与站内路由
|
||||
*
|
||||
* 站内路由自动加当前语言前缀(中文无前缀,英文 /en 前缀)。
|
||||
*/
|
||||
export function useLinkResolver() {
|
||||
const localePath = useLocalePath();
|
||||
|
||||
function isExternal(link: string): boolean {
|
||||
return /^https?:\/\//.test(link);
|
||||
}
|
||||
|
||||
function resolveLink(link: string): string {
|
||||
if (!link) return '';
|
||||
if (isExternal(link)) return link;
|
||||
return localePath(link);
|
||||
}
|
||||
|
||||
return { isExternal, resolveLink };
|
||||
}
|
||||
@@ -1,28 +1,41 @@
|
||||
/**
|
||||
* 统一请求封装(基于 Nuxt 内置 ofetch)
|
||||
* 响应格式约定:{ code, data, message },成功码 '0000'
|
||||
*
|
||||
* 与 dashboard 后端约定响应格式:{ code, msg, data }
|
||||
* - code = 1:成功
|
||||
* - code = 0:业务错误
|
||||
* - code = 2:无数据
|
||||
*/
|
||||
|
||||
interface ApiResponse<T = unknown> {
|
||||
code: string;
|
||||
data: T;
|
||||
message: string;
|
||||
export interface ApiResponse<T = unknown> {
|
||||
code: number;
|
||||
msg: string;
|
||||
data: T | null;
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
code: number;
|
||||
constructor(msg: string, code: number) {
|
||||
super(msg);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
export function useRequest() {
|
||||
const config = useRuntimeConfig();
|
||||
const baseURL = config.public.apiBaseUrl as string;
|
||||
|
||||
async function request<T = unknown>(url: string, options?: Parameters<typeof $fetch>[1]): Promise<T> {
|
||||
async function request<T = unknown>(url: string, options?: Parameters<typeof $fetch>[1]): Promise<T | null> {
|
||||
const res = await $fetch<ApiResponse<T>>(url, {
|
||||
baseURL,
|
||||
...options
|
||||
});
|
||||
|
||||
if (res.code !== '0000') {
|
||||
throw new Error(res.message || 'Request failed');
|
||||
if (res.code === 0) {
|
||||
throw new ApiError(res.msg || '请求失败', res.code);
|
||||
}
|
||||
|
||||
// code = 2(无数据)时 data 为 null,正常返回
|
||||
return res.data;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { SectionBlock, SectionConfig } from '~/types/site';
|
||||
|
||||
/**
|
||||
* 区块内容多语言解析
|
||||
*
|
||||
* Section 的各文本字段均有 _zh / _en 两个版本,
|
||||
* 该 composable 根据当前语言返回对应的值,避免各布局组件重复判断。
|
||||
*/
|
||||
export function useSectionContent(section: SectionBlock) {
|
||||
const { locale } = useI18n();
|
||||
const isEn = computed(() => locale.value === 'en');
|
||||
|
||||
const overline = computed(() => (isEn.value ? section.overline_en : section.overline_zh) || '');
|
||||
const title = computed(() => (isEn.value ? section.title_en : section.title_zh) || '');
|
||||
const subtitle = computed(() => (isEn.value ? section.subtitle_en : section.subtitle_zh) || '');
|
||||
const content = computed(() => (isEn.value ? section.content_en : section.content_zh) || '');
|
||||
const ctaPrimaryText = computed(() => (isEn.value ? section.cta_primary_en : section.cta_primary_zh) || '');
|
||||
const ctaSecondaryText = computed(() => (isEn.value ? section.cta_secondary_en : section.cta_secondary_zh) || '');
|
||||
const mediaUrl = computed(() => (isEn.value ? section.media_url_en : section.media_url_zh) || section.media_url_zh || '');
|
||||
|
||||
const config = computed<SectionConfig>(() => section.config ?? {});
|
||||
|
||||
/** 深色主题区块 */
|
||||
const isDark = computed(() => section.theme === 'dark');
|
||||
|
||||
return {
|
||||
isEn,
|
||||
overline,
|
||||
title,
|
||||
subtitle,
|
||||
content,
|
||||
ctaPrimaryText,
|
||||
ctaSecondaryText,
|
||||
mediaUrl,
|
||||
config,
|
||||
isDark
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { GlobalConfig, NavConfig, SectionBlock, I18nEntry, ProductsData } from '~/types/site';
|
||||
|
||||
/**
|
||||
* 官网前台公开接口服务层
|
||||
*
|
||||
* 消费 dashboard 后端的 /api/site/* 只读接口(免鉴权)。
|
||||
*/
|
||||
export function useSiteApi() {
|
||||
const { get } = useRequest();
|
||||
|
||||
/** 全局配置:站点信息 + 页脚 + 社交链接 + SEO 默认值 */
|
||||
function getGlobal() {
|
||||
return get<GlobalConfig>('/site/global');
|
||||
}
|
||||
|
||||
/** 导航:可见入口 + 外观 */
|
||||
function getNav() {
|
||||
return get<NavConfig>('/site/nav');
|
||||
}
|
||||
|
||||
/** Section 区块列表(按页面类型,仅可见) */
|
||||
function getSections(pageType: string, productId?: number) {
|
||||
const params: Record<string, unknown> = { page_type: pageType };
|
||||
if (productId) params.product_id = productId;
|
||||
return get<{ items: SectionBlock[]; total: number }>('/site/sections', params);
|
||||
}
|
||||
|
||||
/** i18n 词条列表 */
|
||||
function getI18n() {
|
||||
return get<{ items: I18nEntry[]; total: number }>('/site/i18n');
|
||||
}
|
||||
|
||||
/** 产品:可见系列及其下可见产品 */
|
||||
function getProducts() {
|
||||
return get<ProductsData>('/site/products');
|
||||
}
|
||||
|
||||
return { getGlobal, getNav, getSections, getI18n, getProducts };
|
||||
}
|
||||
Reference in New Issue
Block a user