fix: [lock:] 已解锁块渲染为锁块容器(组件层切段,同款背景边框+🔓标识)
This commit is contained in:
@@ -21,15 +21,67 @@ function fileTag(filename) {
|
||||
|
||||
/**
|
||||
* 剥离内联 [lock:] 成对标签,保留块内内容(只去标签不去内容)。
|
||||
* - 已解锁块:后端在剥离后的 content 里原样保留 [lock:type]...[/lock] 标签,
|
||||
* 读者视角不该看到标签本身 → 去标签留内容
|
||||
* - 未解锁块:后端已替换为 @@LOCK<n>@@ 占位符(不匹配此正则),不受影响
|
||||
* - 支持 [lock](无参数)与 [lock:type]、[lock:type:参数] 形态;幂等,可重复调用
|
||||
* 保留导出供外部/预览使用;renderContent 不再调用——组件层已用切段方案接管(保留锁块容器)。
|
||||
*/
|
||||
export function stripLockTags(content) {
|
||||
return String(content).replace(/\[lock(?::[^\]]*)?\]([\s\S]*?)\[\/lock\]/g, '$1');
|
||||
}
|
||||
|
||||
/** 从整块标签提取 lock 类型参数:'login' / 'reply' / 'password'(无参数返回 '') */
|
||||
function extractLockType(block) {
|
||||
const m = String(block).match(/^\[lock(?::([^\]]*))?\]/);
|
||||
const param = (m && m[1]) || '';
|
||||
return param.split(':')[0] || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 按内联 [lock:] 成对标签切段(split 带捕获组):
|
||||
* 交替产出 [文本, 整块(含标签), 块内内容, 文本, …](i%3:0=文本 / 1=整块 / 2=块内内容)
|
||||
*/
|
||||
const INLINE_LOCK_RE = /(\[lock(?::[^\]]*)?\]([\s\S]*?)\[\/lock\])/g;
|
||||
|
||||
/**
|
||||
* 渲染一段文本:文本段走 renderContent;内联 [lock:] 段渲染「已解锁内容」容器。
|
||||
* 已解锁块(admin/作者/解锁后)后端在 content 里原样保留 [lock:...]...[/lock] 标签,
|
||||
* 这里按标签切成锁块容器(同款背景+边框 + 🔓 已解锁头部),内容递归 MarkdownRenderer 渲染。
|
||||
*/
|
||||
function renderSegments(text, useMarkdown, rest) {
|
||||
const parts = String(text).split(INLINE_LOCK_RE);
|
||||
const nodes = [];
|
||||
let textIdx = 0;
|
||||
let lockIdx = 0;
|
||||
for (let i = 0; i < parts.length; i += 1) {
|
||||
const p = parts[i];
|
||||
if (i % 3 === 1) {
|
||||
// 整块(含标签),紧随其后 i+1 是对应块内内容
|
||||
const inner = parts[i + 1] || '';
|
||||
const type = extractLockType(p);
|
||||
nodes.push(
|
||||
<div key={'reveal' + lockIdx++} className="lock-reveal">
|
||||
<div className="lock-placeholder lock-revealed">
|
||||
<div className="lock-revealed-head">
|
||||
<span className="material-icons" style={{ fontSize: 16 }} aria-hidden="true">lock_open</span>
|
||||
已解锁内容
|
||||
{type ? <span className="lock-revealed-type">{type}</span> : null}
|
||||
</div>
|
||||
<div className="lock-revealed-body">
|
||||
<MarkdownRenderer
|
||||
content={inner}
|
||||
useMarkdown={useMarkdown}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
i += 1; // 跳过内容段(已作为 inner 渲染)
|
||||
} else if (p) {
|
||||
nodes.push(<RawBlock key={'text' + textIdx++} html={renderContent(p, useMarkdown)} />);
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容渲染:useMarkdown=false 走纯文本(转义 + 标签替换 + <br>);
|
||||
* useMarkdown=true 先抽取 [image:]/[file:] 标签,marked 渲染后再还原(照搬 render.js)。
|
||||
@@ -37,9 +89,6 @@ export function stripLockTags(content) {
|
||||
*/
|
||||
export function renderContent(content, useMarkdown) {
|
||||
if (content == null) return '';
|
||||
// 最前段剥离内联 [lock:] 成对标签:已解锁块的标签透出问题(保留块内内容)。
|
||||
// 未解锁块是 @@LOCK<n>@@ 占位(不匹配),先后顺序与占位分段无冲突
|
||||
content = stripLockTags(content);
|
||||
|
||||
if (!useMarkdown) {
|
||||
let html = escapeHtml(content);
|
||||
@@ -74,11 +123,10 @@ function RawBlock({ html }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建渲染节点:
|
||||
* 1. 详情页(locks 非空):按服务端剥离的 @@LOCK<n>@@ 占位符分段——
|
||||
* 已解锁且有内容(lockContent)的块递归渲染(.lock-reveal 淡入),否则渲染 LockBlock 占位
|
||||
* 2. 编辑器作者预览(previewMode):剥离 [lock:type]...[/lock] 配对标签,直接展开内容
|
||||
* 3. 普通:整段原样渲染
|
||||
* 构建渲染节点(两层分段):
|
||||
* 1. @@LOCK<n>@@ 占位分段(详情页 locks 非空)——lock 段:已解锁+有内容 → 递归渲染(.lock-reveal 淡入),
|
||||
* 否则 LockBlock 锁定卡片;文本段 → 再按内联 [lock:] 切段(已解锁容器 / 文本)
|
||||
* 2. locks 为空(含 previewMode 编辑预览):整段按内联 [lock:] 切段,作者视角同样显示已解锁容器
|
||||
*/
|
||||
function buildNodes({ content, useMarkdown, locks, unlocked, lockContent, onUnlock, onGoComment, previewMode }) {
|
||||
if (content == null) return null;
|
||||
@@ -89,7 +137,6 @@ function buildNodes({ content, useMarkdown, locks, unlocked, lockContent, onUnlo
|
||||
// 服务端已把未解锁块替换为 @@LOCK<n>@@;split 交替产出 [文本, 索引, 文本, 索引…]
|
||||
const parts = str.split(/@@LOCK(\d+)@@/);
|
||||
const nodes = [];
|
||||
let textIdx = 0;
|
||||
for (let i = 0; i < parts.length; i += 1) {
|
||||
const p = parts[i];
|
||||
if (i % 2 === 1) {
|
||||
@@ -115,19 +162,15 @@ function buildNodes({ content, useMarkdown, locks, unlocked, lockContent, onUnlo
|
||||
nodes.push(<LockBlock key={'lock' + n} index={n} type={type} onUnlock={onUnlock} onGoComment={onGoComment} />);
|
||||
}
|
||||
} else if (p) {
|
||||
nodes.push(<RawBlock key={'text' + textIdx++} html={renderContent(p, useMarkdown)} />);
|
||||
// 文本段:可能仍含内联 [lock:](已解锁块)→ 切段渲染已解锁容器
|
||||
nodes.push(...renderSegments(p, useMarkdown, { locks, unlocked, lockContent, onUnlock, onGoComment }));
|
||||
}
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
// 编辑器作者预览:锁定块直接展开内容(作者视角;stripLockTags 幂等,renderContent 内还会再处理一次)
|
||||
if (previewMode) {
|
||||
const stripped = stripLockTags(str);
|
||||
return [<RawBlock key="text0" html={renderContent(stripped, useMarkdown)} />];
|
||||
}
|
||||
|
||||
return [<RawBlock key="text0" html={renderContent(content, useMarkdown)} />];
|
||||
// locks 为空:整段按内联 [lock:] 切段(previewMode 作者预览同样显示「已解锁内容」容器)
|
||||
return renderSegments(str, useMarkdown, { locks, unlocked, lockContent, onUnlock, onGoComment });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user