import { get, post } from '@/core/http/http-client' import { buildRestPath } from '@/core/config/api-paths' import parseHtml from '@/core/html/html-parser' const ROUTE_DETAILS = buildRestPath('article/details') const ROUTE_AD_DETAILS = buildRestPath('article/adDetails') const ROUTE_LIST = buildRestPath('article/getList') function unwrapListPageBody(data, raw) { const candidates = [data, raw?.data, raw?.res?.data] for (const c of candidates) { if (!c || typeof c !== 'object' || Array.isArray(c)) continue if (Array.isArray(c.data) || c.current_page != null || c.last_page != null) { return c } if (c.data && typeof c.data === 'object' && !Array.isArray(c.data)) { const inner = c.data if (Array.isArray(inner.data) || inner.current_page != null) return inner } } return { data: [], current_page: 1, last_page: 1, total: 0 } } function normalizeImages(images) { if (Array.isArray(images)) return images.filter(Boolean) if (typeof images === 'string') { try { const parsed = JSON.parse(images) return Array.isArray(parsed) ? parsed.filter(Boolean) : [] } catch (_) { return [] } } return [] } export function normalizeArticleRow(item) { if (!item || item.id == null) return null const images = normalizeImages(item.images) return { ...item, images, image: item.image || images[0] || '' } } export function isExternalArticleUrl(value) { return /^https?:\/\//i.test(String(value || '').trim()) } function unwrapArticleBody(data, raw) { if (!data || typeof data !== 'object') data = {} if (data.id != null && (data.content != null || data.url != null || data.title != null)) { return data } if (data.data && typeof data.data === 'object') { const inner = data.data if (inner.id != null || inner.content != null || inner.url != null) return inner } const env = raw?.res?.data ?? raw?.data if (env && typeof env === 'object') { if (env.id != null || env.content != null || env.url != null) return env if (env.data && typeof env.data === 'object') { const nested = env.data if (nested.id != null || nested.content != null || nested.url != null) return nested } } return data } function pickArticleContent(article) { if (!article || typeof article !== 'object') return '' return ( article.content ?? article.content_html ?? article.detail ?? article.body ?? '' ) } /** * @param {string} html * @param {(src: string, w?: number, h?: number) => string} [ossFn] * @param {{ hideImages?: boolean }} [opts] * @returns {{ html: string, nodes: unknown[] }} */ export function formatArticleRichContent(html, ossFn, opts = {}) { const raw = pickArticleContent(typeof html === 'object' ? html : { content: html }) if (!raw) return { html: '', nodes: [] } let content = String(raw).replace(/\\/g, '') if (opts.hideImages) { content = content.replace(/]*src=['"]([^'"]+)['"][^>]*>/gi, (match, capture) => { const src = ossFn(capture, 500, 0) return `` }) } let nodes = [] try { nodes = parseHtml(content) } catch (_) { nodes = [{ type: 'text', text: content.replace(/<[^>]+>/g, '') }] } return { html: content, nodes } } /** @deprecated 使用 formatArticleRichContent */ export function formatArticleHtmlNodes(html, ossFn, opts = {}) { return formatArticleRichContent(html, ossFn, opts).nodes } export async function fetchArticleDetails(id) { const { data, raw } = await get(ROUTE_DETAILS, { id }) const article = unwrapArticleBody(data, raw) if (!article.content) { const merged = pickArticleContent(article) if (merged) article.content = merged } return article } export async function fetchAdvertDetails(id) { const { data, raw } = await get(ROUTE_AD_DETAILS, { id }) return unwrapArticleBody(data, raw) } export async function fetchArticleListPage(page = 1, type = 'new') { const { data, raw } = await post(ROUTE_LIST, { page, type }) const body = unwrapListPageBody(data, raw) const rows = (Array.isArray(body.data) ? body.data : []) .map(normalizeArticleRow) .filter(Boolean) return { rows, total: Number(body.total || rows.length), currentPage: Number(body.current_page || page || 1), lastPage: Number(body.last_page || body.current_page || 1) } }