update at 2025-10-10 19:13:38

This commit is contained in:
douboer
2025-10-10 19:13:38 +08:00
parent 86c3beea49
commit 90933673f3
4 changed files with 492 additions and 331 deletions

View File

@@ -61,4 +61,6 @@
现在分页依据真实渲染高度,预览窗口内不会再丢失底部内容。建议在小红书预览里多翻几页、调整字号后重新分页验证结果。 现在分页依据真实渲染高度,预览窗口内不会再丢失底部内容。建议在小红书预览里多翻几页、调整字号后重新分页验证结果。
## v1.3.9
重构xhs和wechat布局统一使用grid便于维护。

View File

@@ -9,7 +9,7 @@
* 4. 提供文章导出HTML功能 * 4. 提供文章导出HTML功能
*/ */
import { Notice, Platform, TFile, TFolder } from 'obsidian'; import { Notice, Platform, TFile } from 'obsidian';
import { NMPSettings } from '../settings'; import { NMPSettings } from '../settings';
import AssetsManager from '../assets'; import AssetsManager from '../assets';
import { ArticleRender } from '../article-render'; import { ArticleRender } from '../article-render';
@@ -32,8 +32,9 @@ export class WechatPreview {
currentHighlight: string; currentHighlight: string;
// UI 元素 // UI 元素
toolbar: HTMLDivElement | null = null; board: HTMLDivElement | null = null;
renderDiv: HTMLDivElement | null = null; contentCell: HTMLElement | null = null;
contentEl: HTMLElement | null = null;
wechatSelect: HTMLSelectElement | null = null; wechatSelect: HTMLSelectElement | null = null;
themeSelect: HTMLSelectElement | null = null; themeSelect: HTMLSelectElement | null = null;
highlightSelect: HTMLSelectElement | null = null; highlightSelect: HTMLSelectElement | null = null;
@@ -65,97 +66,95 @@ export class WechatPreview {
*/ */
build(): void { build(): void {
this.container.empty(); this.container.empty();
this.container.addClass('wechat-preview-container');
// 创建工具栏
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';
// 将 ArticleRender 的 style 与内容节点挂载 this.board = this.container.createDiv({ cls: 'wechat-board' });
try {
if (this.render && this.render.styleEl && !this.renderDiv.contains(this.render.styleEl)) { this.buildAccountRow();
this.renderDiv.appendChild(this.render.styleEl);
} //this.buildCoverRow();
if (this.render && this.render.articleDiv && !this.renderDiv.contains(this.render.articleDiv)) { //this.buildStyleRow();
// 容器样式:模拟公众号编辑器宽度,更好的排版显示
this.render.articleDiv.addClass('wechat-article-wrapper'); this.contentCell = this.createCell('content');
this.renderDiv.appendChild(this.render.articleDiv); this.contentCell.addClass('wechat-cell-content');
}
} catch (e) { this.mountArticle(this.board);
console.warn('[WechatPreview] 挂载文章容器失败', e);
}
} }
/** private createCell(area: string, tag: keyof HTMLElementTagNameMap = 'div', extraClasses: string[] = []): HTMLElement {
* 构建工具栏 if (!this.board) {
*/ throw new Error('Wechat board not initialized');
private buildToolbar(parent: HTMLDivElement): void { }
// 单层 Grid所有控件直接挂到 parent.preview-toolbar const cell = this.board.createEl(tag, { attr: { 'data-area': area } });
cell.addClass('wechat-cell');
for (const cls of extraClasses) {
cell.addClass(cls);
}
return cell;
}
// 公众号选择 private buildAccountRow(): void {
if (this.settings.wxInfo.length > 1 || Platform.isDesktop) { const selectCell = this.createCell('account-select');
const wxLabel = parent.createDiv({ cls: 'style-label' }); const selectLabel = selectCell.createEl('label', {
wxLabel.innerText = '公众号'; cls: 'style-label',
attr: { for: 'wechat-account-select' },
text: '公众号'
});
selectLabel.addClass('wechat-account-label');
const wxSelect = parent.createEl('select', { cls: 'wechat-select' }); const wxSelect = selectCell.createEl('select', {
wxSelect.onchange = async () => { cls: 'wechat-select',
this.currentAppId = wxSelect.value; attr: { id: 'wechat-account-select' }
this.onAppIdChanged(); }) 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()) { if (Platform.isDesktop && this.settings.isAuthKeyVaild()) {
const htmlBtn = parent.createEl('button', { text: '💾 导出HTML', cls: 'toolbar-button' }); const exportBtn = actionsCell.createEl('button', { text: '导出页面', cls: 'toolbar-button wechat-action-button' });
htmlBtn.onclick = async () => await this.exportHTML(); exportBtn.onclick = async () => await this.exportHTML();
} }
// 封面选择 if (actionsCell.childElementCount === 0) {
//this.buildCoverSelector(parent); actionsCell.addClass('wechat-cell-placeholder');
// 样式选择(如果启用)
if (this.settings.showStyleUI) {
this.buildStyleSelector(parent);
} }
} }
/** private buildCoverRow(): void {
* 构建封面选择器 const selectCell = this.createCell('cover-select');
*/ selectCell.createDiv({ cls: 'style-label', text: '封面' });
private buildCoverSelector(parent: HTMLDivElement): void {
const coverTitle = parent.createDiv({ cls: 'style-label' });
coverTitle.innerText = '封面';
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('type', 'radio');
this.useDefaultCover.setAttr('name', 'cover'); this.useDefaultCover.setAttr('name', 'cover');
this.useDefaultCover.setAttr('value', 'default'); this.useDefaultCover.setAttr('value', 'default');
@@ -166,43 +165,42 @@ export class WechatPreview {
this.coverEl.setAttr('style', 'visibility:hidden;width:0px;'); this.coverEl.setAttr('style', 'visibility:hidden;width:0px;');
} }
}; };
selectCell.createEl('label', { text: '默认', attr: { for: 'default-cover' } });
const defaultLabel = parent.createEl('label'); this.useLocalCover = selectCell.createEl('input', { cls: 'input-style' }) as HTMLInputElement;
defaultLabel.innerText = '默认';
defaultLabel.setAttr('for', 'default-cover');
this.useLocalCover = parent.createEl('input', { cls: 'input-style' });
this.useLocalCover.setAttr('type', 'radio'); this.useLocalCover.setAttr('type', 'radio');
this.useLocalCover.setAttr('name', 'cover'); this.useLocalCover.setAttr('name', 'cover');
this.useLocalCover.setAttr('value', 'local'); this.useLocalCover.setAttr('value', 'local');
this.useLocalCover.id = 'local-cover'; this.useLocalCover.id = 'local-cover';
this.useLocalCover.setAttr('style', 'margin-left:20px;');
this.useLocalCover.onchange = async () => { this.useLocalCover.onchange = async () => {
if (this.useLocalCover?.checked && this.coverEl) { if (this.useLocalCover?.checked && this.coverEl) {
this.coverEl.setAttr('style', 'visibility:visible;width:180px;'); this.coverEl.setAttr('style', 'visibility:visible;width:180px;');
} }
}; };
selectCell.createEl('label', { text: '上传', attr: { for: 'local-cover' } });
const localLabel = parent.createEl('label'); const inputCell = this.createCell('cover-input');
localLabel.setAttr('for', 'local-cover'); this.coverEl = inputCell.createEl('input', {
localLabel.innerText = '上传'; cls: 'upload-input',
attr: {
this.coverEl = parent.createEl('input', { cls: 'upload-input' }); type: 'file',
this.coverEl.setAttr('type', 'file'); placeholder: '封面图片',
this.coverEl.setAttr('placeholder', '封面图片'); accept: '.png, .jpg, .jpeg',
this.coverEl.setAttr('accept', '.png, .jpg, .jpeg'); name: 'cover',
this.coverEl.setAttr('name', 'cover'); id: 'cover-input'
this.coverEl.id = 'cover-input'; }
}) as HTMLInputElement;
if (this.useDefaultCover?.checked) {
this.coverEl.setAttr('style', 'visibility:hidden;width:0px;');
}
} }
/** private buildStyleRow(): void {
* 构建样式选择器 const styleLabelCell = this.createCell('style-label', 'div', ['style-label']);
*/ styleLabelCell.setText('样式');
private buildStyleSelector(parent: HTMLDivElement): void {
const cssStyle = parent.createDiv({ cls: 'style-label' });
cssStyle.innerText = '样式';
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 () => { selectBtn.onchange = async () => {
this.currentTheme = selectBtn.value; this.currentTheme = selectBtn.value;
this.render.updateStyle(selectBtn.value); this.render.updateStyle(selectBtn.value);
@@ -216,12 +214,11 @@ export class WechatPreview {
} }
this.themeSelect = selectBtn; 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' }); const highlightSelectCell = this.createCell('highlight-select');
highlightStyle.innerText = '代码高亮'; const highlightStyleBtn = highlightSelectCell.createEl('select', { cls: 'style-select wechat-style-select' }) as HTMLSelectElement;
const highlightStyleBtn = parent.createEl('select', { cls: 'style-select' });
highlightStyleBtn.onchange = async () => { highlightStyleBtn.onchange = async () => {
this.currentHighlight = highlightStyleBtn.value; this.currentHighlight = highlightStyleBtn.value;
this.render.updateHighLight(highlightStyleBtn.value); this.render.updateHighLight(highlightStyleBtn.value);
@@ -237,6 +234,29 @@ export class WechatPreview {
this.highlightSelect = highlightStyleBtn; 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 { destroy(): void {
this.toolbar = null; this.board = null;
this.renderDiv = null; this.contentCell = null;
this.contentEl = null;
this.wechatSelect = null; this.wechatSelect = null;
this.themeSelect = null; this.themeSelect = null;
this.highlightSelect = null; this.highlightSelect = null;

View File

@@ -84,22 +84,14 @@
.wechat-preview-container:not([style*="display: none"]), .wechat-preview-container:not([style*="display: none"]),
.xiaohongshu-preview-container:not([style*="display: none"]) { .xiaohongshu-preview-container:not([style*="display: none"]) {
flex: 1; flex: 1;
display: grid !important; display: flex !important;
grid-template-rows: auto 1fr;
min-height: 0; /* 允许内部滚动区域正确计算高度 */ min-height: 0; /* 允许内部滚动区域正确计算高度 */
} }
.render-div {
overflow-y: auto;
padding: 10px;
-webkit-user-select: text;
user-select: text;
min-height: 0;
}
/* 文章包裹:模拟公众号编辑器阅读宽度 */ /* 文章包裹:模拟公众号编辑器阅读宽度 */
.wechat-article-wrapper { .wechat-article-wrapper {
max-width: 720px; width: 100%;
max-width: clamp(360px, 80vw, 760px);
margin: 0 auto; margin: 0 auto;
padding: 12px 18px 80px 18px; /* 底部留白方便滚动到底部操作 */ padding: 12px 18px 80px 18px; /* 底部留白方便滚动到底部操作 */
box-sizing: border-box; box-sizing: border-box;
@@ -110,19 +102,6 @@
background: transparent; background: transparent;
} }
.preview-toolbar {
position: relative;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, max-content));
gap: 12px;
align-items: center;
min-height: auto;
padding: 8px 12px;
border-bottom: 1px solid #e8eaed;
background: var(--grad-toolbar);
box-shadow: var(--shadow-overlay);
}
.copy-button { .copy-button {
margin-right: 10px; margin-right: 10px;
padding: 6px 14px; padding: 6px 14px;
@@ -312,23 +291,6 @@ label:hover { color: var(--c-primary); }
/* Toolbar 行样式 */ /* Toolbar 行样式 */
/* =========================================================== */ /* =========================================================== */
.toolbar-line {
display: grid;
grid-auto-flow: column;
align-items: center;
gap: 12px;
padding: 8px 12px;
background: white;
border-radius: 6px;
margin: 8px 10px;
box-shadow: var(--shadow-sm);
}
.toolbar-line.flex-wrap {
grid-auto-flow: row;
grid-template-columns: repeat(auto-fit, minmax(160px, max-content));
}
.platform-selector-line { .platform-selector-line {
background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%) !important; background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%) !important;
border-left: 4px solid var(--c-primary); border-left: 4px solid var(--c-primary);
@@ -367,13 +329,125 @@ label:hover { color: var(--c-primary); }
font-weight: 500; font-weight: 500;
} }
/* =========================================================== */
/* 微信公众号预览布局 */
/* =========================================================== */
.wechat-preview-container {
width: 100%;
height: 100%;
display: flex;
padding: 12px;
box-sizing: border-box;
}
.wechat-board {
flex: 1;
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
grid-template-rows: auto auto auto minmax(0, 1fr);
grid-template-areas:
"account-select account-select account-select account-back-export account-back-export account-back-export"
"cover-select cover-select cover-select cover-select cover-input cover-input"
"style-label style-select highlight-label highlight-select highlight-select highlight-select"
"content content content content content content";
gap: 5px;
background: var(--grad-toolbar);
border-radius: 12px;
padding: 5px;
box-shadow: var(--shadow-sm);
min-height: 0;
}
.wechat-cell {
background: white;
border-radius: 10px;
padding: 12px 16px;
box-shadow: var(--shadow-sm);
display: flex;
align-items: center;
gap: 12px;
}
.wechat-cell[data-area="account-select"] {
grid-area: account-select;
justify-content: flex-start;
}
.wechat-cell[data-area="account-back-export"] {
grid-area: account-back-export;
justify-content: flex-center;
}
.wechat-cell[data-area="account-select"] .style-label {
white-space: nowrap;
}
.wechat-cell[data-area="account-select"] .wechat-select {
width: 100%;
}
.wechat-cell[data-area="account-back-export"] .wechat-action-button { white-space: nowrap; }
.wechat-cell[data-area="cover-select"] {
grid-area: cover-select;
justify-content: flex-start;
gap: 18px;
}
.wechat-cell[data-area="cover-input"] { grid-area: cover-input; }
.wechat-cell[data-area="cover-select"] label {
white-space: nowrap;
}
.wechat-cell[data-area="cover-input"] .upload-input {
margin-left: 0;
width: 100%;
}
.wechat-cell[data-area="style-label"] { grid-area: style-label; white-space: nowrap; justify-content: flex-start; }
.wechat-cell[data-area="style-select"] { grid-area: style-select; width: 100%; }
.wechat-cell[data-area="highlight-label"] { grid-area: highlight-label; white-space: nowrap; justify-content: flex-start; }
.wechat-cell[data-area="highlight-select"] { grid-area: highlight-select; width: 100%; }
.wechat-cell[data-area="style-select"] .style-select,
.wechat-cell[data-area="style-select"] .wechat-style-select,
.wechat-cell[data-area="highlight-select"] .style-select,
.wechat-cell[data-area="highlight-select"] .wechat-style-select {
width: 100%;
min-width: 0;
}
.wechat-cell[data-area="content"] {
grid-area: content;
overflow-y: auto;
padding: 10px;
-webkit-user-select: text;
user-select: text;
min-height: 0;
background: white;
border-radius: 12px;
box-shadow: var(--shadow-sm);
display: block;
}
.wechat-cell-placeholder {
background: transparent;
box-shadow: none;
padding: 0;
}
.wechat-cell-placeholder .toolbar-button {
display: none;
}
/* =========================================================== */ /* =========================================================== */
/* 微信公众号选择器样式 */ /* 微信公众号选择器样式 */
/* =========================================================== */ /* =========================================================== */
.wechat-select { .wechat-select {
padding: 6px 12px; padding: 6px 12px;
min-width: 200px; min-width: 100px;
width: 100%;
} }
/* =========================================================== */ /* =========================================================== */
@@ -397,17 +471,6 @@ label:hover { color: var(--c-primary); }
.toolbar-button.purple-gradient:hover { box-shadow: var(--shadow-purple-4); } .toolbar-button.purple-gradient:hover { box-shadow: var(--shadow-purple-4); }
/* =========================================================== */
/* 分隔线样式 */
/* =========================================================== */
.toolbar-separator {
width: 1px;
height: 24px;
background: var(--c-border);
margin: 0 4px;
}
/* =========================================================== */ /* =========================================================== */
/* Doc Modal 样式 */ /* Doc Modal 样式 */
/* =========================================================== */ /* =========================================================== */
@@ -485,11 +548,11 @@ label:hover { color: var(--c-primary); }
"content content content content content content" "content content content content content content"
"content content content content content content" "content content content content content content"
"pagination pagination pagination slice slice slice"; "pagination pagination pagination slice slice slice";
gap: 12px; gap: 5px;
width: 100%; width: 100%;
background: var(--grad-xhs-bg); background: var(--grad-xhs-bg);
border-radius: 12px; border-radius: 12px;
padding: 16px; padding: 5px;
box-shadow: var(--shadow-sm); box-shadow: var(--shadow-sm);
min-height: 0; min-height: 0;
} }

View File

@@ -70,11 +70,10 @@
.wechat-select:hover { border-color: var(--c-primary); } .wechat-select:hover { border-color: var(--c-primary); }
.platform-select:focus, .platform-select:focus,
.wechat-select:focus { outline: none; border-color: var(--c-primary); } .wechat-select:focus { outline: none; border-color: var(--c-primary); }
.note-preview { .note-preview {
display: grid;
grid-template-rows: auto 1fr; grid-template-rows: auto 1fr;
grid-template-columns: 1fr; grid-template-columns: 1fr;
display: grid;
min-height: 100%; min-height: 100%;
width: 100%; width: 100%;
height: 100%; height: 100%;
@@ -85,22 +84,14 @@
.wechat-preview-container:not([style*="display: none"]), .wechat-preview-container:not([style*="display: none"]),
.xiaohongshu-preview-container:not([style*="display: none"]) { .xiaohongshu-preview-container:not([style*="display: none"]) {
flex: 1; flex: 1;
display: grid !important; display: flex !important;
grid-template-rows: auto 1fr;
min-height: 0; /* 允许内部滚动区域正确计算高度 */ min-height: 0; /* 允许内部滚动区域正确计算高度 */
} }
.render-div {
overflow-y: auto;
padding: 10px;
-webkit-user-select: text;
user-select: text;
min-height: 0;
}
/* 文章包裹:模拟公众号编辑器阅读宽度 */ /* 文章包裹:模拟公众号编辑器阅读宽度 */
.wechat-article-wrapper { .wechat-article-wrapper {
max-width: 720px; width: 100%;
max-width: clamp(360px, 80vw, 760px);
margin: 0 auto; margin: 0 auto;
padding: 12px 18px 80px 18px; /* 底部留白方便滚动到底部操作 */ padding: 12px 18px 80px 18px; /* 底部留白方便滚动到底部操作 */
box-sizing: border-box; box-sizing: border-box;
@@ -111,19 +102,6 @@
background: transparent; background: transparent;
} }
.preview-toolbar {
position: relative;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, max-content));
gap: 12px;
align-items: center;
min-height: auto;
padding: 8px 12px;
border-bottom: 1px solid #e8eaed;
background: var(--grad-toolbar);
box-shadow: var(--shadow-overlay);
}
.copy-button { .copy-button {
margin-right: 10px; margin-right: 10px;
padding: 6px 14px; padding: 6px 14px;
@@ -313,23 +291,6 @@ label:hover { color: var(--c-primary); }
/* Toolbar 行样式 */ /* Toolbar 行样式 */
/* =========================================================== */ /* =========================================================== */
.toolbar-line {
display: grid;
grid-auto-flow: column;
align-items: center;
gap: 12px;
padding: 8px 12px;
background: white;
border-radius: 6px;
margin: 8px 10px;
box-shadow: var(--shadow-sm);
}
.toolbar-line.flex-wrap {
grid-auto-flow: row;
grid-template-columns: repeat(auto-fit, minmax(160px, max-content));
}
.platform-selector-line { .platform-selector-line {
background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%) !important; background: linear-gradient(135deg, #fff3e0 0%, #ffffff 100%) !important;
border-left: 4px solid var(--c-primary); border-left: 4px solid var(--c-primary);
@@ -338,7 +299,7 @@ label:hover { color: var(--c-primary); }
/* 平台选择容器:单层 Grid 排列 */ /* 平台选择容器:单层 Grid 排列 */
.platform-chooser-container.platform-chooser-grid { .platform-chooser-container.platform-chooser-grid {
display: grid; display: grid;
grid-auto-flow: column; grid-template-columns: auto minmax(160px, 1fr) auto auto;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
padding: 8px 12px; padding: 8px 12px;
@@ -348,6 +309,16 @@ label:hover { color: var(--c-primary); }
box-shadow: var(--shadow-sm); box-shadow: var(--shadow-sm);
} }
.platform-chooser-container .toolbar-button {
justify-self: start;
}
@media (max-width: 720px) {
.platform-chooser-container.platform-chooser-grid {
grid-template-columns: repeat(auto-fit, minmax(140px, max-content));
}
}
/* =========================================================== */ /* =========================================================== */
/* 平台选择器样式 */ /* 平台选择器样式 */
/* =========================================================== */ /* =========================================================== */
@@ -358,13 +329,119 @@ label:hover { color: var(--c-primary); }
font-weight: 500; font-weight: 500;
} }
/* =========================================================== */
/* 微信公众号预览布局 */
/* =========================================================== */
.wechat-preview-container {
width: 100%;
height: 100%;
display: flex;
padding: 12px;
box-sizing: border-box;
}
.wechat-board {
flex: 1;
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
grid-template-rows: auto auto auto minmax(0, 1fr);
grid-template-areas:
"account-label account-select account-select account-select account-backend account-export"
"cover-label cover-default cover-upload cover-input cover-input cover-input"
"style-label style-select highlight-label highlight-select highlight-select highlight-select"
"content content content content content content";
gap: 12px;
background: var(--grad-toolbar);
border-radius: 12px;
padding: 16px;
box-shadow: var(--shadow-sm);
min-height: 0;
}
.wechat-cell {
background: white;
border-radius: 10px;
padding: 12px 16px;
box-shadow: var(--shadow-sm);
display: flex;
align-items: center;
gap: 12px;
}
.wechat-cell[data-area="account-label"] { grid-area: account-label; white-space: nowrap; justify-content: flex-start; }
.wechat-cell[data-area="account-select"] { grid-area: account-select; }
.wechat-cell[data-area="account-backend"] { grid-area: account-backend; justify-content: flex-end; }
.wechat-cell[data-area="account-export"] { grid-area: account-export; justify-content: flex-end; }
.wechat-cell[data-area="account-select"] .wechat-select {
width: 100%;
}
.wechat-cell[data-area="account-backend"] .wechat-action-button,
.wechat-cell[data-area="account-export"] .wechat-action-button {
white-space: nowrap;
}
.wechat-cell[data-area="cover-label"] { grid-area: cover-label; white-space: nowrap; justify-content: flex-start; }
.wechat-cell[data-area="cover-default"] { grid-area: cover-default; gap: 8px; }
.wechat-cell[data-area="cover-upload"] { grid-area: cover-upload; gap: 8px; }
.wechat-cell[data-area="cover-input"] { grid-area: cover-input; }
.wechat-cell[data-area="cover-default"] label,
.wechat-cell[data-area="cover-upload"] label {
white-space: nowrap;
}
.wechat-cell[data-area="cover-input"] .upload-input {
margin-left: 0;
width: 100%;
}
.wechat-cell[data-area="style-label"] { grid-area: style-label; white-space: nowrap; justify-content: flex-start; }
.wechat-cell[data-area="style-select"] { grid-area: style-select; width: 100%; }
.wechat-cell[data-area="highlight-label"] { grid-area: highlight-label; white-space: nowrap; justify-content: flex-start; }
.wechat-cell[data-area="highlight-select"] { grid-area: highlight-select; width: 100%; }
.wechat-cell[data-area="style-select"] .style-select,
.wechat-cell[data-area="style-select"] .wechat-style-select,
.wechat-cell[data-area="highlight-select"] .style-select,
.wechat-cell[data-area="highlight-select"] .wechat-style-select {
width: 100%;
min-width: 0;
}
.wechat-cell[data-area="content"] {
grid-area: content;
overflow-y: auto;
padding: 10px;
-webkit-user-select: text;
user-select: text;
min-height: 0;
background: white;
border-radius: 12px;
box-shadow: var(--shadow-sm);
display: block;
}
.wechat-cell-placeholder {
background: transparent;
box-shadow: none;
padding: 0;
}
.wechat-cell-placeholder .toolbar-button {
display: none;
}
/* =========================================================== */ /* =========================================================== */
/* 微信公众号选择器样式 */ /* 微信公众号选择器样式 */
/* =========================================================== */ /* =========================================================== */
.wechat-select { .wechat-select {
padding: 6px 12px; padding: 6px 12px;
min-width: 200px; min-width: 100px;
width: 100%;
} }
/* =========================================================== */ /* =========================================================== */
@@ -388,17 +465,6 @@ label:hover { color: var(--c-primary); }
.toolbar-button.purple-gradient:hover { box-shadow: var(--shadow-purple-4); } .toolbar-button.purple-gradient:hover { box-shadow: var(--shadow-purple-4); }
/* =========================================================== */
/* 分隔线样式 */
/* =========================================================== */
.toolbar-separator {
width: 1px;
height: 24px;
background: var(--c-border);
margin: 0 4px;
}
/* =========================================================== */ /* =========================================================== */
/* Doc Modal 样式 */ /* Doc Modal 样式 */
/* =========================================================== */ /* =========================================================== */
@@ -461,38 +527,138 @@ label:hover { color: var(--c-primary); }
.xiaohongshu-preview-container { .xiaohongshu-preview-container {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex;
padding: 12px;
box-sizing: border-box;
} }
.xhs-preview-container:not([style*="display: none"]) { .xhs-board {
display: grid !important; display: grid;
grid-template-rows: auto 1fr auto; grid-template-columns: repeat(6, minmax(0, 1fr));
height: 100%; grid-template-rows: auto auto auto 1fr auto;
grid-template-areas:
"template template preview preview font font"
"content content content content content content"
"content content content content content content"
"content content content content content content"
"pagination pagination pagination slice slice slice";
gap: 5px;
width: 100%;
background: var(--grad-xhs-bg); background: var(--grad-xhs-bg);
border-radius: 12px;
padding: 5px;
box-shadow: var(--shadow-sm);
min-height: 0; min-height: 0;
} }
.xhs-page-container { .xhs-card {
overflow-y: auto; display: flex;
overflow-x: hidden; align-items: center;
display: grid; gap: 10px;
align-content: start; background: white;
justify-content: center; border-radius: 10px;
padding: 0px; padding: 10px 14px;
background: radial-gradient(ellipse at top, rgba(255,255,255,0.1) 0%, transparent 70%); box-shadow: var(--shadow-sm);
min-height: 0; /* 允许子项正确收缩和滚动 */ }
.xhs-label {
font-size: 13px;
font-weight: 600;
color: var(--c-text-muted);
white-space: nowrap;
}
.xhs-select {
flex: 1 1 auto;
padding: 6px 10px;
border: 1px solid var(--c-border);
border-radius: 6px;
background: white;
font-size: 13px;
cursor: pointer;
max-width: 100px;
transition: border-color 0.2s ease;
}
.xhs-select:hover { border-color: var(--c-primary); }
.xhs-select:focus {
outline: none;
border-color: var(--c-primary);
box-shadow: 0 0 0 3px rgba(30, 136, 229, 0.1);
}
.font-size-group {
display: grid;
grid-auto-flow: column;
align-items: center;
gap: 6px;
padding: 2px;
border: 1px solid var(--c-border);
border-radius: 6px;
background: #f7f9fb;
}
.font-size-btn {
width: 26px;
height: 26px;
border: none;
background: transparent;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
color: #5f6368;
transition: background 0.2s ease;
}
.font-size-btn:hover { background: #eaf1fe; }
.font-size-input {
width: 35px;
border: none;
background: transparent;
text-align: center;
font: inherit;
color: inherit;
}
.font-size-input:focus { outline: none; }
.xhs-area-template { grid-area: template; }
.xhs-area-preview { grid-area: preview; }
.xhs-area-font {
grid-area: font;
flex-wrap: nowrap;
}
.xhs-area-pagination { grid-area: pagination; justify-content: center; gap: 16px; }
.xhs-area-slice { grid-area: slice; justify-content: center; gap: 16px; }
.xhs-area-content {
grid-area: content;
background: white;
border-radius: 12px;
padding: 0;
box-shadow: var(--shadow-sm);
display: flex;
}
.xhs-page-container {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
background: radial-gradient(ellipse at top, rgba(255,255,255,0.1) 0%, transparent 70%);
border-radius: 12px;
min-height: 0;
position: relative;
} }
/* 小红书单页包裹器:为缩放后的页面预留正确的布局空间 */
.xhs-page-wrapper { .xhs-page-wrapper {
margin: 0px auto; margin: 0 auto;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
} }
/* 小红书单页样式:实际尺寸 1080×1440通过 scale 缩放到 540×720 */
.xhs-page { .xhs-page {
/* 实际尺寸与缩放由代码在运行时设置 */
transform-origin: top left;
background: white; background: white;
box-shadow: 0 4px 12px rgba(0,0,0,0.1); box-shadow: 0 4px 12px rgba(0,0,0,0.1);
border-radius: 8px; border-radius: 8px;
@@ -503,87 +669,10 @@ label:hover { color: var(--c-primary); }
height: auto; height: auto;
} }
.xhs-top-toolbar { .xhs-pagination {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, max-content));
align-items: center;
gap: 12px;
padding: 8px 12px;
background: var(--grad-toolbar);
border-bottom: 1px solid #e8eaed;
box-shadow: var(--shadow-overlay);
}
.toolbar-label {
font-size: 11px;
color: var(--c-text-muted);
font-weight: 500;
white-space: nowrap;
}
.xhs-select {
padding: 4px 8px;
border: 1px solid var(--c-border);
border-radius: 4px;
background: white;
font-size: 11px;
cursor: pointer;
transition: border-color 0.2s ease;
}
.xhs-select:hover {
border-color: var(--c-primary);
}
.xhs-select:focus {
outline: none;
border-color: var(--c-primary);
}
.font-size-group {
display: grid; display: grid;
grid-auto-flow: column; grid-auto-flow: column;
align-items: center; align-items: center;
gap: 6px;
background: white;
border: 1px solid var(--c-border);
border-radius: 4px;
padding: 2px;
}
.font-size-btn {
width: 24px;
height: 24px;
border: none;
background: transparent;
border-radius: 3px;
cursor: pointer;
font-size: 16px;
color: #5f6368;
transition: background 0.2s ease;
}
.font-size-btn:hover {
background: #f1f3f4;
}
.font-size-display {
min-width: 24px;
text-align: center;
font-size: 12px;
color: #202124;
font-weight: 500;
}
.xhs-page-navigation {
display: grid;
grid-auto-flow: column;
justify-content: center;
align-items: center;
gap: 16px;
padding: 12px;
background: white;
border-bottom: 1px solid #e8eaed;
} }
.xhs-nav-btn { .xhs-nav-btn {
@@ -605,7 +694,6 @@ label:hover { color: var(--c-primary); }
border-color: var(--c-primary); border-color: var(--c-primary);
} }
/* 页码指示器:输入框 + 总页数标记 */
.xhs-page-indicator { .xhs-page-indicator {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -615,9 +703,8 @@ label:hover { color: var(--c-primary); }
color: #202124; color: #202124;
} }
/* 可编辑页码输入框 */
.xhs-page-number-input { .xhs-page-number-input {
width: 56px; width: 35px;
padding: 4px 6px; padding: 4px 6px;
text-align: center; text-align: center;
border: 1px solid var(--c-border); border: 1px solid var(--c-border);
@@ -635,24 +722,12 @@ label:hover { color: var(--c-primary); }
box-shadow: 0 0 0 3px rgba(30, 136, 229, 0.15); box-shadow: 0 0 0 3px rgba(30, 136, 229, 0.15);
} }
/* 总页数显示(只读) */
.xhs-page-number-total { .xhs-page-number-total {
font-size: 14px; font-size: 14px;
color: #5f6368; color: #5f6368;
user-select: none; user-select: none;
} }
.xhs-bottom-toolbar {
display: grid;
grid-auto-flow: column;
justify-content: center;
gap: 12px;
padding: 12px 16px;
background: var(--grad-toolbar-bottom);
border-top: 1px solid #e8eaed;
box-shadow: 0 -2px 4px rgba(0,0,0,0.04);
}
.xhs-slice-btn { .xhs-slice-btn {
padding: 8px 20px; padding: 8px 20px;
background: var(--grad-primary); background: var(--grad-primary);