feat: P0-2抽纯函数+补a-g断言13组+P1-2编辑器空覆盖守卫
P0-2: - content-adapter.js: 解耦Node crypto(客户端直引), 新增mergeCmsItem/normalizeThumbnail/buildPublishPayload - buildPublishPayload补normalizeCmsList归一(抓到vote_id漏生成真bug) - check-article-flow.js: 4组->13组断言,覆盖a-g P1-2: - editText.vue/smallTitle.vue getContents成功回调加空覆盖守卫 - editSetting封面已统一字符串数组,无需改动 验证: verify.js全绿,h5构建90 JS通过
This commit is contained in:
@@ -1,4 +1,13 @@
|
||||
const crypto = require('crypto')
|
||||
function randomHex(bytes) {
|
||||
try {
|
||||
if (typeof require === 'function') {
|
||||
return require('crypto').randomBytes(bytes).toString('hex')
|
||||
}
|
||||
} catch (e) { /* fall through to Math.random */ }
|
||||
let out = ''
|
||||
while (out.length < bytes * 2) out += Math.random().toString(16).slice(2)
|
||||
return out.slice(0, bytes * 2)
|
||||
}
|
||||
|
||||
function clone(value) {
|
||||
if (value === undefined || value === null) return value
|
||||
@@ -59,8 +68,9 @@ function normalizeCmsList(list) {
|
||||
}, [])
|
||||
}
|
||||
|
||||
function createVoteId() {
|
||||
return `vote_${Date.now().toString(36)}_${crypto.randomBytes(6).toString('hex')}`
|
||||
function createVoteId(randomFn) {
|
||||
const rand = typeof randomFn === 'function' ? randomFn(6) : randomHex(6)
|
||||
return `vote_${Date.now().toString(36)}_${rand}`
|
||||
}
|
||||
|
||||
function textOps(item) {
|
||||
@@ -159,12 +169,89 @@ function hasRenderableContent(content, cmsLst) {
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* 编辑器写回守卫(P1-2 同源逻辑)。
|
||||
* 有旧内容但新内容为空时返回 null(调用方跳过覆盖并返回上一页),否则返回合并后的条目。
|
||||
*/
|
||||
function mergeCmsItem(oldItem, incoming) {
|
||||
const next = incoming || {}
|
||||
const nextText = next.text || ''
|
||||
const nextHtml = next.html || ''
|
||||
const nextDelta = next.delta
|
||||
const hadContent = Boolean(oldItem && (oldItem.text || oldItem.html))
|
||||
const gotContent = Boolean(
|
||||
nextText || nextHtml || (nextDelta && Array.isArray(nextDelta.ops) && nextDelta.ops.length)
|
||||
)
|
||||
if (hadContent && !gotContent) return null
|
||||
return {
|
||||
...(oldItem || {}),
|
||||
text: nextText,
|
||||
html: nextHtml,
|
||||
delta: nextDelta && typeof nextDelta === 'object' ? nextDelta : { ops: [] }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 封面归一:始终返回字符串数组(d. editSetting 同源逻辑)。
|
||||
* mode: 0 无封面 / 1 单图 / 3 三图。
|
||||
*/
|
||||
function normalizeThumbnail(raw, mode) {
|
||||
const srcOf = (v) => (typeof v === 'string' ? v : (v && typeof v.src === 'string' ? v.src : ''))
|
||||
const list = Array.isArray(raw) ? raw.map(srcOf).filter(Boolean) : []
|
||||
if (mode === 0) return []
|
||||
if (mode === 1) return list[0] ? [list[0]] : []
|
||||
if (mode === 3) return list.slice(0, 3)
|
||||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布 payload 构造(e. preview 同源逻辑)。
|
||||
* 返回 { query } 或 { error },调用方按 error 弹 toast。
|
||||
*/
|
||||
function buildPublishPayload(stored, extra) {
|
||||
const s = stored || {}
|
||||
const x = extra || {}
|
||||
if (!s.title || !String(s.title).trim()) return { error: '请先填写标题' }
|
||||
const cmsLst = normalizeCmsList(Array.isArray(s.cmsLst) ? s.cmsLst : [])
|
||||
const articleStatus = x.articleStatus === 1 ? 1 : 0
|
||||
if (articleStatus === 1 && !hasRenderableContent(s.content, cmsLst)) {
|
||||
return { error: '正文不能为空' }
|
||||
}
|
||||
const content = (s.content && Array.isArray(s.content.ops))
|
||||
? s.content
|
||||
: cmsLstToDelta(cmsLst)
|
||||
return {
|
||||
query: {
|
||||
title: s.title,
|
||||
title_html: s.title_html,
|
||||
title_delta: s.title_delta,
|
||||
thumbnail: s.thumbnail,
|
||||
p_type: s.p_type,
|
||||
category_id: s.category_id,
|
||||
temp_id: x.tempId || s.temp_id || '',
|
||||
music_url_id: s.music_url_id || {},
|
||||
edit_type: 'mobile',
|
||||
cmsLst,
|
||||
content,
|
||||
excerpt: s.excerpt || '',
|
||||
article_status: articleStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const adapterModule = {
|
||||
clone,
|
||||
normalizeCmsList,
|
||||
cmsLstToDelta,
|
||||
createVoteId,
|
||||
getArticleText,
|
||||
getArticleImages,
|
||||
hasRenderableContent
|
||||
hasRenderableContent,
|
||||
mergeCmsItem,
|
||||
normalizeThumbnail,
|
||||
buildPublishPayload
|
||||
}
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = adapterModule
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user