update at 2025-10-09 12:39:24
This commit is contained in:
@@ -15,6 +15,9 @@ import AssetsManager from '../assets';
|
||||
import { paginateArticle, renderPage, PageInfo } from './paginator';
|
||||
import { sliceCurrentPage, sliceAllPages } from './slice';
|
||||
|
||||
const XHS_PREVIEW_DEFAULT_WIDTH = 540;
|
||||
const XHS_PREVIEW_WIDTH_OPTIONS = [1080, 720, 540, 360];
|
||||
|
||||
/**
|
||||
* 小红书预览视图类
|
||||
*/
|
||||
@@ -29,6 +32,7 @@ export class XiaohongshuPreview {
|
||||
topToolbar!: HTMLDivElement;
|
||||
templateSelect!: HTMLSelectElement;
|
||||
fontSizeInput!: HTMLInputElement;
|
||||
previewWidthSelect!: HTMLSelectElement;
|
||||
|
||||
pageContainer!: HTMLDivElement;
|
||||
bottomToolbar!: HTMLDivElement;
|
||||
@@ -110,6 +114,30 @@ export class XiaohongshuPreview {
|
||||
option.text = name;
|
||||
});
|
||||
|
||||
const previewWidthLabel = this.topToolbar.createDiv({ cls: 'toolbar-label' });
|
||||
previewWidthLabel.innerText = '预览宽度';
|
||||
this.previewWidthSelect = this.topToolbar.createEl('select', { cls: 'xhs-select' });
|
||||
const currentPreviewWidth = this.settings.xhsPreviewWidth || XHS_PREVIEW_DEFAULT_WIDTH;
|
||||
XHS_PREVIEW_WIDTH_OPTIONS.forEach(value => {
|
||||
const option = this.previewWidthSelect.createEl('option');
|
||||
option.value = String(value);
|
||||
option.text = `${value}px`;
|
||||
});
|
||||
if (!XHS_PREVIEW_WIDTH_OPTIONS.includes(currentPreviewWidth)) {
|
||||
const customOption = this.previewWidthSelect.createEl('option');
|
||||
customOption.value = String(currentPreviewWidth);
|
||||
customOption.text = `${currentPreviewWidth}px`;
|
||||
}
|
||||
this.previewWidthSelect.value = String(currentPreviewWidth);
|
||||
this.previewWidthSelect.onchange = async () => {
|
||||
const value = parseInt(this.previewWidthSelect.value, 10);
|
||||
if (Number.isFinite(value) && value > 0) {
|
||||
await this.onPreviewWidthChanged(value);
|
||||
} else {
|
||||
this.previewWidthSelect.value = String(this.settings.xhsPreviewWidth || XHS_PREVIEW_DEFAULT_WIDTH);
|
||||
}
|
||||
};
|
||||
|
||||
// 字号控制(可直接编辑)
|
||||
const fontSizeLabel = this.topToolbar.createDiv({ cls: 'toolbar-label' });
|
||||
fontSizeLabel.innerText = '字号';
|
||||
@@ -205,6 +233,7 @@ export class XiaohongshuPreview {
|
||||
if (this.currentThemeClass) classes.push('note-to-mp');
|
||||
const pageElement = wrapper.createDiv({ cls: classes.join(' ') });
|
||||
renderPage(pageElement, page.content, this.settings);
|
||||
this.applyPreviewSizing(wrapper, pageElement);
|
||||
|
||||
// 应用字体设置
|
||||
this.applyFontSettings(pageElement);
|
||||
@@ -213,6 +242,49 @@ export class XiaohongshuPreview {
|
||||
this.pageNumberDisplay.innerText = `${this.currentPageIndex + 1}/${this.pages.length}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设置的宽度和横竖比应用预览尺寸与缩放
|
||||
*/
|
||||
private applyPreviewSizing(wrapper: HTMLElement, pageElement: HTMLElement): void {
|
||||
const configuredWidth = this.settings.sliceImageWidth || 1080;
|
||||
const actualWidth = Math.max(1, configuredWidth);
|
||||
const ratio = this.parseAspectRatio(this.settings.sliceImageAspectRatio);
|
||||
const actualHeight = Math.round((actualWidth * ratio.height) / ratio.width);
|
||||
const previewWidthSetting = this.settings.xhsPreviewWidth || XHS_PREVIEW_DEFAULT_WIDTH;
|
||||
const previewWidth = Math.max(1, previewWidthSetting);
|
||||
const scale = Math.max(previewWidth / actualWidth, 0.01);
|
||||
const previewHeight = Math.max(1, Math.round(actualHeight * scale));
|
||||
|
||||
wrapper.style.width = `${previewWidth}px`;
|
||||
wrapper.style.height = `${previewHeight}px`;
|
||||
|
||||
pageElement.style.width = `${actualWidth}px`;
|
||||
pageElement.style.height = `${actualHeight}px`;
|
||||
pageElement.style.transform = `scale(${scale})`;
|
||||
pageElement.style.position = 'absolute';
|
||||
pageElement.style.top = '0';
|
||||
pageElement.style.left = '0';
|
||||
}
|
||||
|
||||
private async onPreviewWidthChanged(newWidth: number): Promise<void> {
|
||||
if (newWidth <= 0) return;
|
||||
if (this.settings.xhsPreviewWidth === newWidth) return;
|
||||
this.settings.xhsPreviewWidth = newWidth;
|
||||
await this.persistSettings();
|
||||
this.renderCurrentPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析横竖比例字符串
|
||||
*/
|
||||
private parseAspectRatio(ratio: string | undefined): { width: number; height: number } {
|
||||
const parts = (ratio ?? '').split(':').map(part => parseFloat(part.trim()));
|
||||
if (parts.length === 2 && isFinite(parts[0]) && isFinite(parts[1]) && parts[0] > 0 && parts[1] > 0) {
|
||||
return { width: parts[0], height: parts[1] };
|
||||
}
|
||||
return { width: 3, height: 4 };
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用字体设置(仅字号,字体从主题读取)
|
||||
*/
|
||||
@@ -341,6 +413,17 @@ export class XiaohongshuPreview {
|
||||
}
|
||||
}
|
||||
|
||||
private async persistSettings(): Promise<void> {
|
||||
try {
|
||||
const plugin = (this.app as any)?.plugins?.getPlugin?.('note-to-mp');
|
||||
if (plugin?.saveSettings) {
|
||||
await plugin.saveSettings();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[XiaohongshuPreview] 保存设置失败', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示小红书预览视图
|
||||
*/
|
||||
@@ -365,6 +448,7 @@ export class XiaohongshuPreview {
|
||||
destroy(): void {
|
||||
this.topToolbar = null as any;
|
||||
this.templateSelect = null as any;
|
||||
this.previewWidthSelect = null as any;
|
||||
this.fontSizeInput = null as any;
|
||||
this.pageContainer = null as any;
|
||||
this.bottomToolbar = null as any;
|
||||
|
||||
Reference in New Issue
Block a user