109 lines
3.6 KiB
JavaScript
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, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
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,
|
|
}
|