update at 2025-10-10 19:13:38
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
* 4. 提供文章导出HTML功能
|
||||
*/
|
||||
|
||||
import { Notice, Platform, TFile, TFolder } from 'obsidian';
|
||||
import { Notice, Platform, TFile } from 'obsidian';
|
||||
import { NMPSettings } from '../settings';
|
||||
import AssetsManager from '../assets';
|
||||
import { ArticleRender } from '../article-render';
|
||||
@@ -32,8 +32,9 @@ export class WechatPreview {
|
||||
currentHighlight: string;
|
||||
|
||||
// UI 元素
|
||||
toolbar: HTMLDivElement | null = null;
|
||||
renderDiv: HTMLDivElement | null = null;
|
||||
board: HTMLDivElement | null = null;
|
||||
contentCell: HTMLElement | null = null;
|
||||
contentEl: HTMLElement | null = null;
|
||||
wechatSelect: HTMLSelectElement | null = null;
|
||||
themeSelect: HTMLSelectElement | null = null;
|
||||
highlightSelect: HTMLSelectElement | null = null;
|
||||
@@ -65,97 +66,95 @@ export class WechatPreview {
|
||||
*/
|
||||
build(): void {
|
||||
this.container.empty();
|
||||
|
||||
// 创建工具栏
|
||||
this.toolbar = this.container.createDiv({ cls: 'preview-toolbar' });
|
||||
this.buildToolbar(this.toolbar);
|
||||
|
||||
// 创建渲染区域
|
||||
this.renderDiv = this.container.createDiv({ cls: 'render-div' });
|
||||
this.renderDiv.id = 'render-div';
|
||||
this.container.addClass('wechat-preview-container');
|
||||
|
||||
// 将 ArticleRender 的 style 与内容节点挂载
|
||||
try {
|
||||
if (this.render && this.render.styleEl && !this.renderDiv.contains(this.render.styleEl)) {
|
||||
this.renderDiv.appendChild(this.render.styleEl);
|
||||
}
|
||||
if (this.render && this.render.articleDiv && !this.renderDiv.contains(this.render.articleDiv)) {
|
||||
// 容器样式:模拟公众号编辑器宽度,更好的排版显示
|
||||
this.render.articleDiv.addClass('wechat-article-wrapper');
|
||||
this.renderDiv.appendChild(this.render.articleDiv);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[WechatPreview] 挂载文章容器失败', e);
|
||||
}
|
||||
this.board = this.container.createDiv({ cls: 'wechat-board' });
|
||||
|
||||
this.buildAccountRow();
|
||||
|
||||
//this.buildCoverRow();
|
||||
//this.buildStyleRow();
|
||||
|
||||
this.contentCell = this.createCell('content');
|
||||
this.contentCell.addClass('wechat-cell-content');
|
||||
|
||||
this.mountArticle(this.board);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建工具栏
|
||||
*/
|
||||
private buildToolbar(parent: HTMLDivElement): void {
|
||||
// 单层 Grid:所有控件直接挂到 parent(.preview-toolbar)
|
||||
private createCell(area: string, tag: keyof HTMLElementTagNameMap = 'div', extraClasses: string[] = []): HTMLElement {
|
||||
if (!this.board) {
|
||||
throw new Error('Wechat board not initialized');
|
||||
}
|
||||
const cell = this.board.createEl(tag, { attr: { 'data-area': area } });
|
||||
cell.addClass('wechat-cell');
|
||||
for (const cls of extraClasses) {
|
||||
cell.addClass(cls);
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
// 公众号选择
|
||||
if (this.settings.wxInfo.length > 1 || Platform.isDesktop) {
|
||||
const wxLabel = parent.createDiv({ cls: 'style-label' });
|
||||
wxLabel.innerText = '公众号';
|
||||
private buildAccountRow(): void {
|
||||
const selectCell = this.createCell('account-select');
|
||||
const selectLabel = selectCell.createEl('label', {
|
||||
cls: 'style-label',
|
||||
attr: { for: 'wechat-account-select' },
|
||||
text: '公众号'
|
||||
});
|
||||
selectLabel.addClass('wechat-account-label');
|
||||
|
||||
const wxSelect = parent.createEl('select', { cls: 'wechat-select' });
|
||||
wxSelect.onchange = async () => {
|
||||
this.currentAppId = wxSelect.value;
|
||||
this.onAppIdChanged();
|
||||
const wxSelect = selectCell.createEl('select', {
|
||||
cls: 'wechat-select',
|
||||
attr: { id: 'wechat-account-select' }
|
||||
}) as HTMLSelectElement;
|
||||
wxSelect.onchange = async () => {
|
||||
this.currentAppId = wxSelect.value;
|
||||
this.onAppIdChanged();
|
||||
};
|
||||
|
||||
const defaultOp = wxSelect.createEl('option');
|
||||
defaultOp.value = '';
|
||||
defaultOp.text = '请在设置里配置公众号';
|
||||
|
||||
for (let i = 0; i < this.settings.wxInfo.length; i++) {
|
||||
const op = wxSelect.createEl('option');
|
||||
const wx = this.settings.wxInfo[i];
|
||||
op.value = wx.appid;
|
||||
op.text = wx.name;
|
||||
if (i === 0) {
|
||||
op.selected = true;
|
||||
this.currentAppId = wx.appid;
|
||||
}
|
||||
}
|
||||
this.wechatSelect = wxSelect;
|
||||
|
||||
const actionsCell = this.createCell('account-back-export');
|
||||
if (Platform.isDesktop) {
|
||||
const openBtn = actionsCell.createEl('button', {
|
||||
text: '访问后台',
|
||||
cls: 'toolbar-button purple-gradient wechat-action-button'
|
||||
});
|
||||
openBtn.onclick = async () => {
|
||||
const { shell } = require('electron');
|
||||
shell.openExternal('https://mp.weixin.qq.com');
|
||||
uevent('open-mp');
|
||||
};
|
||||
|
||||
const defaultOp = wxSelect.createEl('option');
|
||||
defaultOp.value = '';
|
||||
defaultOp.text = '请在设置里配置公众号';
|
||||
|
||||
for (let i = 0; i < this.settings.wxInfo.length; i++) {
|
||||
const op = wxSelect.createEl('option');
|
||||
const wx = this.settings.wxInfo[i];
|
||||
op.value = wx.appid;
|
||||
op.text = wx.name;
|
||||
if (i === 0) {
|
||||
op.selected = true;
|
||||
this.currentAppId = wx.appid;
|
||||
}
|
||||
}
|
||||
this.wechatSelect = wxSelect;
|
||||
|
||||
if (Platform.isDesktop) {
|
||||
//parent.createDiv({ cls: 'toolbar-separator' });
|
||||
const openBtn = parent.createEl('button', { text: '🌐 去公众号后台', cls: 'toolbar-button purple-gradient' });
|
||||
openBtn.onclick = async () => {
|
||||
const { shell } = require('electron');
|
||||
shell.openExternal('https://mp.weixin.qq.com');
|
||||
uevent('open-mp');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 操作按钮(直接平铺在 Grid 中)
|
||||
if (Platform.isDesktop && this.settings.isAuthKeyVaild()) {
|
||||
const htmlBtn = parent.createEl('button', { text: '💾 导出HTML', cls: 'toolbar-button' });
|
||||
htmlBtn.onclick = async () => await this.exportHTML();
|
||||
const exportBtn = actionsCell.createEl('button', { text: '导出页面', cls: 'toolbar-button wechat-action-button' });
|
||||
exportBtn.onclick = async () => await this.exportHTML();
|
||||
}
|
||||
|
||||
// 封面选择
|
||||
//this.buildCoverSelector(parent);
|
||||
|
||||
// 样式选择(如果启用)
|
||||
if (this.settings.showStyleUI) {
|
||||
this.buildStyleSelector(parent);
|
||||
if (actionsCell.childElementCount === 0) {
|
||||
actionsCell.addClass('wechat-cell-placeholder');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建封面选择器
|
||||
*/
|
||||
private buildCoverSelector(parent: HTMLDivElement): void {
|
||||
const coverTitle = parent.createDiv({ cls: 'style-label' });
|
||||
coverTitle.innerText = '封面';
|
||||
private buildCoverRow(): void {
|
||||
const selectCell = this.createCell('cover-select');
|
||||
selectCell.createDiv({ cls: 'style-label', text: '封面' });
|
||||
|
||||
this.useDefaultCover = parent.createEl('input', { cls: 'input-style' });
|
||||
this.useDefaultCover = selectCell.createEl('input', { cls: 'input-style' }) as HTMLInputElement;
|
||||
this.useDefaultCover.setAttr('type', 'radio');
|
||||
this.useDefaultCover.setAttr('name', 'cover');
|
||||
this.useDefaultCover.setAttr('value', 'default');
|
||||
@@ -166,43 +165,42 @@ export class WechatPreview {
|
||||
this.coverEl.setAttr('style', 'visibility:hidden;width:0px;');
|
||||
}
|
||||
};
|
||||
selectCell.createEl('label', { text: '默认', attr: { for: 'default-cover' } });
|
||||
|
||||
const defaultLabel = parent.createEl('label');
|
||||
defaultLabel.innerText = '默认';
|
||||
defaultLabel.setAttr('for', 'default-cover');
|
||||
|
||||
this.useLocalCover = parent.createEl('input', { cls: 'input-style' });
|
||||
this.useLocalCover = selectCell.createEl('input', { cls: 'input-style' }) as HTMLInputElement;
|
||||
this.useLocalCover.setAttr('type', 'radio');
|
||||
this.useLocalCover.setAttr('name', 'cover');
|
||||
this.useLocalCover.setAttr('value', 'local');
|
||||
this.useLocalCover.id = 'local-cover';
|
||||
this.useLocalCover.setAttr('style', 'margin-left:20px;');
|
||||
this.useLocalCover.onchange = async () => {
|
||||
if (this.useLocalCover?.checked && this.coverEl) {
|
||||
this.coverEl.setAttr('style', 'visibility:visible;width:180px;');
|
||||
}
|
||||
};
|
||||
selectCell.createEl('label', { text: '上传', attr: { for: 'local-cover' } });
|
||||
|
||||
const localLabel = parent.createEl('label');
|
||||
localLabel.setAttr('for', 'local-cover');
|
||||
localLabel.innerText = '上传';
|
||||
|
||||
this.coverEl = parent.createEl('input', { cls: 'upload-input' });
|
||||
this.coverEl.setAttr('type', 'file');
|
||||
this.coverEl.setAttr('placeholder', '封面图片');
|
||||
this.coverEl.setAttr('accept', '.png, .jpg, .jpeg');
|
||||
this.coverEl.setAttr('name', 'cover');
|
||||
this.coverEl.id = 'cover-input';
|
||||
const inputCell = this.createCell('cover-input');
|
||||
this.coverEl = inputCell.createEl('input', {
|
||||
cls: 'upload-input',
|
||||
attr: {
|
||||
type: 'file',
|
||||
placeholder: '封面图片',
|
||||
accept: '.png, .jpg, .jpeg',
|
||||
name: 'cover',
|
||||
id: 'cover-input'
|
||||
}
|
||||
}) as HTMLInputElement;
|
||||
if (this.useDefaultCover?.checked) {
|
||||
this.coverEl.setAttr('style', 'visibility:hidden;width:0px;');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建样式选择器
|
||||
*/
|
||||
private buildStyleSelector(parent: HTMLDivElement): void {
|
||||
const cssStyle = parent.createDiv({ cls: 'style-label' });
|
||||
cssStyle.innerText = '样式';
|
||||
private buildStyleRow(): void {
|
||||
const styleLabelCell = this.createCell('style-label', 'div', ['style-label']);
|
||||
styleLabelCell.setText('样式');
|
||||
|
||||
const selectBtn = parent.createEl('select', { cls: 'style-select' });
|
||||
const styleSelectCell = this.createCell('style-select');
|
||||
const selectBtn = styleSelectCell.createEl('select', { cls: 'style-select wechat-style-select' }) as HTMLSelectElement;
|
||||
selectBtn.onchange = async () => {
|
||||
this.currentTheme = selectBtn.value;
|
||||
this.render.updateStyle(selectBtn.value);
|
||||
@@ -216,12 +214,11 @@ export class WechatPreview {
|
||||
}
|
||||
this.themeSelect = selectBtn;
|
||||
|
||||
parent.createDiv({ cls: 'toolbar-separator' });
|
||||
const highlightLabelCell = this.createCell('highlight-label', 'div', ['style-label']);
|
||||
highlightLabelCell.setText('代码高亮');
|
||||
|
||||
const highlightStyle = parent.createDiv({ cls: 'style-label' });
|
||||
highlightStyle.innerText = '代码高亮';
|
||||
|
||||
const highlightStyleBtn = parent.createEl('select', { cls: 'style-select' });
|
||||
const highlightSelectCell = this.createCell('highlight-select');
|
||||
const highlightStyleBtn = highlightSelectCell.createEl('select', { cls: 'style-select wechat-style-select' }) as HTMLSelectElement;
|
||||
highlightStyleBtn.onchange = async () => {
|
||||
this.currentHighlight = highlightStyleBtn.value;
|
||||
this.render.updateHighLight(highlightStyleBtn.value);
|
||||
@@ -237,6 +234,29 @@ export class WechatPreview {
|
||||
this.highlightSelect = highlightStyleBtn;
|
||||
}
|
||||
|
||||
private mountArticle(_parent: HTMLElement): void {
|
||||
if (!this.contentCell) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (this.render?.styleEl && !this.contentCell.contains(this.render.styleEl)) {
|
||||
this.contentCell.appendChild(this.render.styleEl);
|
||||
}
|
||||
if (this.render?.articleDiv) {
|
||||
this.render.articleDiv.addClass('wechat-article-wrapper');
|
||||
if (this.render.articleDiv.parentElement !== this.contentCell) {
|
||||
this.contentCell.appendChild(this.render.articleDiv);
|
||||
}
|
||||
this.contentEl = this.render.articleDiv;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[WechatPreview] 挂载文章容器失败', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建封面选择器
|
||||
*/
|
||||
/**
|
||||
* 显示微信预览视图
|
||||
*/
|
||||
@@ -360,8 +380,9 @@ export class WechatPreview {
|
||||
* 清理资源
|
||||
*/
|
||||
destroy(): void {
|
||||
this.toolbar = null;
|
||||
this.renderDiv = null;
|
||||
this.board = null;
|
||||
this.contentCell = null;
|
||||
this.contentEl = null;
|
||||
this.wechatSelect = null;
|
||||
this.themeSelect = null;
|
||||
this.highlightSelect = null;
|
||||
|
||||
Reference in New Issue
Block a user