Files
Mycontent/miniprogram/utils/superArticleDetail.js
2026-05-09 18:46:59 +08:00

109 lines
3.6 KiB
JavaScript

/**
* Soul创业派对 - 超级个体动态在阅读页(文章详情模式)中的解析与渲染
*/
function formatSuperArticleTime(value) {
if (!value) return ''
const d = new Date(value)
if (isNaN(d.getTime())) return ''
const m = `${d.getMonth() + 1}`.padStart(2, '0')
const day = `${d.getDate()}`.padStart(2, '0')
const hh = `${d.getHours()}`.padStart(2, '0')
const mm = `${d.getMinutes()}`.padStart(2, '0')
return `${m}-${day} ${hh}:${mm}`
}
function parseImagesField(images) {
if (images == null) return []
if (typeof images === 'string') {
const s = images.trim()
if (!s) return []
try {
const p = JSON.parse(s)
return Array.isArray(p) ? p : []
} catch (_) {
return []
}
}
return Array.isArray(images) ? images : []
}
function normalizeArticleImageSrc(raw, baseUrl) {
const u = String(raw || '').trim()
if (!u || u === 'undefined' || u === 'null') return ''
if (/^https?:\/\//i.test(u)) return u
if (u.startsWith('//')) return `https:${u}`
if (u.startsWith('wxfile://') || u.startsWith('cloud://')) return u
const base = String(baseUrl || '').replace(/\/$/, '')
if (u.startsWith('/') && base) return `${base}${u}`
return u
}
function buildGalleryItems(imagesField, baseUrl) {
const rawList = parseImagesField(imagesField)
return rawList.map((url, idx) => {
const src = normalizeArticleImageSrc(String(url || '').trim(), baseUrl)
const ok =
!!src &&
(/^https?:\/\//i.test(src) || src.startsWith('wxfile://') || src.startsWith('cloud://'))
return { idx, src: ok ? src : '', loadFailed: false }
})
}
function renderSuperArticleHtml(text) {
const src = String(text || '')
const isHtml = /<[a-z][\s\S]*>/i.test(src)
if (isHtml) {
return src.replace(/<a\b([^>]*)>/gi, (full, attrs) => {
if (!/href\s*=/.test(attrs)) return full
const hasStyle = /\sstyle\s*=/.test(attrs)
const styleText = 'color:#22d3ee;text-decoration:underline;word-break:break-all;'
if (hasStyle) {
return `<a${attrs.replace(/\sstyle\s*=\s*(['"])(.*?)\1/i, (_m, q, s) => ` style=${q}${s};${styleText}${q}`)}>`
}
return `<a${attrs} style="${styleText}">`
})
}
const escaped = src
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
const withMentions = escaped.replace(/(^|[\s])(@[\u4e00-\u9fa5A-Za-z0-9_]+)/g, '$1<span style="color:#67e8f9;">$2</span>')
const withTags = withMentions.replace(/(^|[\s])(#[^\s#]+)/g, '$1<span style="color:#5eead4;">$2</span>')
const withLinks = withTags.replace(
/(https?:\/\/[^\s<]+)/g,
'<a href="$1" style="color:#22d3ee;text-decoration:underline;word-break:break-all;">$1</a>'
)
return withLinks.replace(/\n/g, '<br/>')
}
/** 根据详情接口 data + 当前登录用户,生成 setData 用的文案与按钮态 */
function buildSuperArticleAuditMeta(article, viewerUserId) {
const uid = viewerUserId
const isOwner = !!(uid != null && article && String(article.userId) === String(uid))
const st = String((article && article.auditStatus) || 'approved').toLowerCase()
let auditTip = ''
let auditRejectReason = ''
let showAuditEdit = false
if (isOwner && st === 'pending') {
auditTip = '审核中:通过后将在动态广场展示'
} else if (isOwner && st === 'rejected') {
auditTip = '未通过审核'
auditRejectReason = String((article && article.rejectReason) || '').trim()
showAuditEdit = true
}
return { auditTip, auditRejectReason, showAuditEdit }
}
module.exports = {
formatSuperArticleTime,
parseImagesField,
normalizeArticleImageSrc,
buildGalleryItems,
renderSuperArticleHtml,
buildSuperArticleAuditMeta,
}