提交 21a46a3d 编写于 作者: Q qq_41923622

2025-10-29 14:00:00 inscode

上级 b8232db6
const s: string = "欢迎来到 InsCode"; /**
console.log(s); * 内容块类型定义(最小不可拆分单元,如段落、图片、表格等)
tsx 函数式 返回类型 JSX.Element */
interface ContentBlock {
id: string; // 内容块唯一标识
type: 'paragraph' | 'image' | 'table'; // 内容块类型
height: number; // 内容块渲染高度(px)
content: string; // 内容块实际内容(简化示例)
}
/**
* 区域(Region)类型定义(管理连续的1-2页内容)
*/
interface Region {
id: string; // 区域唯一标识
startPage: number; // 起始页号
endPage: number; // 结束页号(startPage <= endPage <= startPage + 1)
contentBlocks: ContentBlock[]; // 包含的内容块
totalHeight: number; // 区域内所有内容块总高度(px)
prevRegionId?: string; // 上一个区域ID(形成链表)
nextRegionId?: string; // 下一个区域ID
}
/**
* 区域管理器(核心控制器,处理区域拆分、合并、页号更新等逻辑)
*/
class RegionManager {
private regions: Map<string, Region>; // 存储所有区域(ID -> 区域)
private pageHeight: number; // 单页高度(px,如A4约842px)
private maxPagesPerRegion: number = 2; // 每个区域最多包含的页数
constructor(pageHeight: number = 842) {
this.regions = new Map();
this.pageHeight = pageHeight;
}
/**
* 创建初始区域(文档首次加载时调用)
* @param initialBlocks 初始内容块列表
*/
createInitialRegions(initialBlocks: ContentBlock[]): void {
let currentRegion: Region = this.createEmptyRegion(1); // 从第1页开始
let currentTotalHeight = 0;
for (const block of initialBlocks) {
currentTotalHeight += block.height;
currentRegion.contentBlocks.push(block);
currentRegion.totalHeight = currentTotalHeight;
// 若当前区域总高度超过最大容量(maxPagesPerRegion页),拆分区域
if (currentTotalHeight > this.pageHeight * this.maxPagesPerRegion) {
const newRegion = this.splitRegion(currentRegion);
this.regions.set(currentRegion.id, currentRegion);
currentRegion = newRegion;
currentTotalHeight = currentRegion.totalHeight; // 重置为新区域的初始高度
}
}
// 添加最后一个区域
this.regions.set(currentRegion.id, currentRegion);
}
/**
* 向指定区域添加内容块(模拟编辑操作)
* @param regionId 目标区域ID
* @param block 要添加的内容块
*/
addBlockToRegion(regionId: string, block: ContentBlock): void {
const region = this.regions.get(regionId);
if (!region) throw new Error(`Region ${regionId} not found`);
// 添加内容块并更新区域总高度
region.contentBlocks.push(block);
region.totalHeight += block.height;
// 检查是否溢出,若溢出则拆分
if (region.totalHeight > this.pageHeight * this.maxPagesPerRegion) {
const newRegion = this.splitRegion(region);
this.regions.set(region.id, region); // 更新原区域
this.regions.set(newRegion.id, newRegion); // 添加新区域
this.updateSubsequentRegions(newRegion); // 更新后续区域页号
} else {
this.regions.set(regionId, region); // 未溢出,直接更新
}
}
/**
* 拆分溢出的区域
* @param overflowRegion 溢出的区域
* @returns 拆分后生成的新区域
*/
private splitRegion(overflowRegion: Region): Region {
// 1. 计算拆分点:找到第一个使累计高度 >= 单页高度的内容块
let accumulatedHeight = 0;
let splitIndex = -1;
for (let i = 0; i < overflowRegion.contentBlocks.length; i++) {
const block = overflowRegion.contentBlocks[i];
accumulatedHeight += block.height;
if (accumulatedHeight >= this.pageHeight) {
splitIndex = i;
break;
}
}
if (splitIndex === -1) {
// 特殊情况:所有内容块总高度 < 单页高度(理论上不会触发,因已判断溢出)
splitIndex = overflowRegion.contentBlocks.length - 1;
}
// 2. 拆分内容块(前半部分保留在原区域,后半部分移到新区域)
const [originalBlocks, newBlocks] = [
overflowRegion.contentBlocks.slice(0, splitIndex + 1),
overflowRegion.contentBlocks.slice(splitIndex + 1),
];
// 3. 更新原区域
const originalHeight = originalBlocks.reduce((sum, b) => sum + b.height, 0);
overflowRegion.contentBlocks = originalBlocks;
overflowRegion.totalHeight = originalHeight;
overflowRegion.endPage = overflowRegion.startPage; // 原区域缩减为1页
// 4. 创建新区域(承接原区域的后续内容)
const newRegionId = `region-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const newRegion: Region = {
id: newRegionId,
startPage: overflowRegion.startPage + 1, // 新区域起始页为原区域起始页+1
endPage: overflowRegion.startPage + 1, // 初始为1页,后续可能扩展
contentBlocks: newBlocks,
totalHeight: newBlocks.reduce((sum, b) => sum + b.height, 0),
prevRegionId: overflowRegion.id,
nextRegionId: overflowRegion.nextRegionId, // 继承原区域的下一个区域
};
// 5. 更新原区域的下一个区域关联
if (overflowRegion.nextRegionId) {
const nextRegion = this.regions.get(overflowRegion.nextRegionId);
if (nextRegion) {
nextRegion.prevRegionId = newRegionId;
this.regions.set(nextRegion.id, nextRegion);
}
}
overflowRegion.nextRegionId = newRegionId;
return newRegion;
}
/**
* 更新后续区域的页号(因拆分导致页码偏移时调用)
* @param newRegion 新生成的区域
*/
private updateSubsequentRegions(newRegion: Region): void {
let currentRegionId = newRegion.nextRegionId;
const pageOffset = newRegion.endPage - newRegion.startPage + 1; // 新区域占用的页数
while (currentRegionId) {
const region = this.regions.get(currentRegionId);
if (!region) break;
// 后续区域的页号整体后移
region.startPage += pageOffset;
region.endPage += pageOffset;
currentRegionId = region.nextRegionId; // 继续处理下一个区域
this.regions.set(region.id, region);
}
}
/**
* 尝试合并相邻区域(当内容减少时)
* @param regionId 目标区域ID
*/
tryMergeRegions(regionId: string): void {
const currentRegion = this.regions.get(regionId);
if (!currentRegion) return;
// 检查下一个区域是否可合并
if (currentRegion.nextRegionId) {
const nextRegion = this.regions.get(currentRegion.nextRegionId);
if (nextRegion) {
const totalHeight = currentRegion.totalHeight + nextRegion.totalHeight;
// 若合并后总高度 <= 最大容量(2页),则合并
if (totalHeight <= this.pageHeight * this.maxPagesPerRegion) {
this.mergeRegions(currentRegion.id, nextRegion.id);
}
}
}
}
/**
* 合并两个相邻区域
* @param regionAId 前一个区域ID
* @param regionBId 后一个区域ID(必须是regionA的nextRegion)
*/
private mergeRegions(regionAId: string, regionBId: string): void {
const regionA = this.regions.get(regionAId);
const regionB = this.regions.get(regionBId);
if (!regionA || !regionB || regionA.nextRegionId !== regionBId) return;
// 1. 合并内容块和高度
regionA.contentBlocks = [...regionA.contentBlocks, ...regionB.contentBlocks];
regionA.totalHeight = regionA.totalHeight + regionB.totalHeight;
regionA.endPage = regionB.endPage; // 结束页更新为regionB的结束页
regionA.nextRegionId = regionB.nextRegionId; // 继承regionB的下一个区域
// 2. 更新原regionB的下一个区域关联
if (regionB.nextRegionId) {
const nextRegion = this.regions.get(regionB.nextRegionId);
if (nextRegion) {
nextRegion.prevRegionId = regionAId;
this.regions.set(nextRegion.id, nextRegion);
}
}
// 3. 删除regionB
this.regions.delete(regionBId);
this.regions.set(regionAId, regionA);
}
/**
* 创建空区域
* @param startPage 起始页号
* @returns 空区域实例
*/
private createEmptyRegion(startPage: number): Region {
return {
id: `region-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
startPage,
endPage: startPage, // 初始为1页
contentBlocks: [],
totalHeight: 0,
};
}
/**
* 获取所有区域信息(用于调试/展示)
*/
getRegions(): Region[] {
return Array.from(this.regions.values());
}
}
// ------------------------------
// 示例使用
// ------------------------------
function demo() {
// 1. 初始化区域管理器(单页高度设为800px)
const manager = new RegionManager(800);
// 2. 创建初始内容块(模拟一篇长文档,包含多个段落)
const initialBlocks: ContentBlock[] = [
{ id: 'b1', type: 'paragraph', height: 300, content: '第一段内容...' },
{ id: 'b2', type: 'paragraph', height: 400, content: '第二段内容...' },
{ id: 'b3', type: 'paragraph', height: 500, content: '第三段内容...' },
{ id: 'b4', type: 'paragraph', height: 300, content: '第四段内容...' },
];
// 3. 创建初始区域
manager.createInitialRegions(initialBlocks);
console.log('初始区域状态:', manager.getRegions());
// 4. 向第一个区域添加内容块(触发拆分)
const firstRegionId = manager.getRegions()[0].id;
manager.addBlockToRegion(firstRegionId, {
id: 'b5', type: 'image', height: 700, content: '一张大图...'
});
console.log('添加内容块后的区域状态:', manager.getRegions());
// 5. 尝试合并区域(模拟删除内容后触发)
manager.tryMergeRegions(firstRegionId);
console.log('尝试合并后的区域状态:', manager.getRegions());
}
// 执行示例
demo();
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册