+
+
--
@@ -433,8 +431,7 @@ function openPublishLink(record: PublishRecord): void {
margin-top: 24px;
}
-.article-drawer__content,
-.article-drawer__markdown {
+.article-drawer__content {
padding: 20px;
background: #f8fafc;
border: 1px solid #e5ecf5;
@@ -447,13 +444,6 @@ function openPublishLink(record: PublishRecord): void {
margin-top: 0;
}
-.article-drawer__markdown {
- margin: 0;
- font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
- line-height: 1.6;
- white-space: pre-wrap;
-}
-
.article-drawer__version-title {
display: flex;
flex-direction: column;
@@ -477,23 +467,30 @@ function openPublishLink(record: PublishRecord): void {
font-size: 12px;
}
-.article-drawer__record-actions {
- display: flex;
- flex-direction: column;
- gap: 8px;
- min-width: 0;
-}
-
.article-drawer__record-link {
+ flex: 1;
+ min-width: 0;
overflow: hidden;
color: #1677ff;
text-overflow: ellipsis;
white-space: nowrap;
}
-.article-drawer__record-buttons {
+.article-drawer__record-link-row {
display: flex;
- gap: 8px;
+ align-items: center;
+ gap: 6px;
+ min-width: 0;
+}
+
+.article-drawer__record-copy.ant-btn {
+ flex: none;
+ color: #8c8c8c;
+}
+
+.article-drawer__record-copy.ant-btn:hover:not(:disabled) {
+ color: #1677ff;
+ background: #e6f4ff;
}
@media (max-width: 960px) {
diff --git a/apps/admin-web/src/components/ArticleEditorCanvas.vue b/apps/admin-web/src/components/ArticleEditorCanvas.vue
index 49cd6ca..07e8a03 100644
--- a/apps/admin-web/src/components/ArticleEditorCanvas.vue
+++ b/apps/admin-web/src/components/ArticleEditorCanvas.vue
@@ -318,6 +318,8 @@ let selectedImageLoadHandler: (() => void) | null = null;
let aiOptimizeAbortController: AbortController | null = null;
let aiOptimizeIgnoreNextWindowPointerDown = false;
+const EDITOR_ACTION_MENU_DROPDOWN_SELECTOR = ".editor-action-menu__dropdown-overlay";
+
const tableContextHeaderRow = computed(() => tableContextMenu.value.rowIndex === 0);
const aiOptimizeStreaming = computed(() => aiOptimizeStatus.value === "generating");
const aiOptimizeHasPreview = computed(() => aiOptimizePreview.value.trim().length > 0);
@@ -334,106 +336,135 @@ const aiOptimizeUiText = computed(() => ({
const tableContextMenuGroups = computed(() => [
{
- key: "rows",
+ key: "operations",
columns: 4,
items: [
{
- key: "add-row-before",
- label: t("article.editor.tableMenu.addRowBefore"),
+ key: "rows",
+ label: "行操作",
icon: InsertRowAboveOutlined,
- disabled: tableContextHeaderRow.value,
+ children: [
+ {
+ key: "add-row-before",
+ label: t("article.editor.tableMenu.addRowBefore"),
+ icon: InsertRowAboveOutlined,
+ disabled: tableContextHeaderRow.value,
+ },
+ {
+ key: "add-row-after",
+ label: t("article.editor.tableMenu.addRowAfter"),
+ icon: InsertRowBelowOutlined,
+ },
+ {
+ key: "delete-row",
+ label: t("article.editor.tableMenu.deleteRow"),
+ icon: DeleteOutlined,
+ danger: true,
+ disabled: tableContextHeaderRow.value,
+ },
+ ],
},
{
- key: "add-row-after",
- label: t("article.editor.tableMenu.addRowAfter"),
- icon: InsertRowBelowOutlined,
+ key: "columns",
+ label: "列操作",
+ icon: InsertRowLeftOutlined,
+ children: [
+ {
+ key: "add-col-before",
+ label: t("article.editor.tableMenu.addColBefore"),
+ icon: InsertRowLeftOutlined,
+ },
+ {
+ key: "add-col-after",
+ label: t("article.editor.tableMenu.addColAfter"),
+ icon: InsertRowRightOutlined,
+ },
+ {
+ key: "delete-col",
+ label: t("article.editor.tableMenu.deleteCol"),
+ icon: DeleteOutlined,
+ danger: true,
+ },
+ ],
},
{
- key: "delete-row",
- label: t("article.editor.tableMenu.deleteRow"),
- icon: DeleteOutlined,
- danger: true,
- disabled: tableContextHeaderRow.value,
+ key: "align",
+ label: "对齐方式",
+ icon: AlignCenterOutlined,
+ children: [
+ {
+ key: "align-left",
+ label: t("article.editor.tableMenu.alignLeft"),
+ icon: AlignLeftOutlined,
+ },
+ {
+ key: "align-center",
+ label: t("article.editor.tableMenu.alignCenter"),
+ icon: AlignCenterOutlined,
+ },
+ {
+ key: "align-right",
+ label: t("article.editor.tableMenu.alignRight"),
+ icon: AlignRightOutlined,
+ },
+ ],
},
{
key: "delete-table",
label: t("article.editor.tableMenu.deleteTable"),
- icon: TableOutlined,
- danger: true,
- },
- ],
- },
- {
- key: "columns",
- columns: 6,
- items: [
- {
- key: "add-col-before",
- label: t("article.editor.tableMenu.addColBefore"),
- icon: InsertRowLeftOutlined,
- },
- {
- key: "add-col-after",
- label: t("article.editor.tableMenu.addColAfter"),
- icon: InsertRowRightOutlined,
- },
- {
- key: "delete-col",
- label: t("article.editor.tableMenu.deleteCol"),
icon: DeleteOutlined,
danger: true,
},
- {
- key: "align-left",
- label: t("article.editor.tableMenu.alignLeft"),
- icon: AlignLeftOutlined,
- },
- {
- key: "align-center",
- label: t("article.editor.tableMenu.alignCenter"),
- icon: AlignCenterOutlined,
- },
- {
- key: "align-right",
- label: t("article.editor.tableMenu.alignRight"),
- icon: AlignRightOutlined,
- },
],
},
]);
const imageContextMenuGroups = computed(() => [
{
- key: "image",
- columns: 3,
+ key: "operations",
+ columns: 4,
items: [
{
- key: "replace-image",
- label: t("article.editor.imageMenu.replace"),
+ key: "image",
+ label: "图片操作",
icon: PictureOutlined,
+ children: [
+ {
+ key: "replace-image",
+ label: t("article.editor.imageMenu.replace"),
+ icon: PictureOutlined,
+ },
+ {
+ key: "toggle-caption",
+ label: t("article.editor.imageMenu.caption"),
+ icon: CodeOutlined,
+ },
+ ],
},
{
- key: "toggle-caption",
- label: t("article.editor.imageMenu.caption"),
- icon: CodeOutlined,
- },
- {
- key: "align-left",
- label: t("article.editor.imageMenu.alignLeft"),
- icon: AlignLeftOutlined,
- active: selectedImage.value.align === "left",
- },
- {
- key: "align-center",
- label: t("article.editor.imageMenu.alignCenter"),
+ key: "align",
+ label: "对齐方式",
icon: AlignCenterOutlined,
- active: selectedImage.value.align === "center",
- },
- {
- key: "align-right",
- label: t("article.editor.imageMenu.alignRight"),
- icon: AlignRightOutlined,
- active: selectedImage.value.align === "right",
+ children: [
+ {
+ key: "align-left",
+ label: t("article.editor.imageMenu.alignLeft"),
+ icon: AlignLeftOutlined,
+ active: selectedImage.value.align === "left",
+ },
+ {
+ key: "align-center",
+ label: t("article.editor.imageMenu.alignCenter"),
+ icon: AlignCenterOutlined,
+ active: selectedImage.value.align === "center",
+ },
+ {
+ key: "align-right",
+ label: t("article.editor.imageMenu.alignRight"),
+ icon: AlignRightOutlined,
+ active: selectedImage.value.align === "right",
+ },
+ ],
},
{
key: "reset-size",
@@ -1224,6 +1255,10 @@ function handleWindowPointerDown(event: PointerEvent): void {
}
}
+ if (isEditorActionMenuDropdownTarget(target)) {
+ return;
+ }
+
if (tablePickerOpen.value) {
closeTablePicker();
}
@@ -1241,6 +1276,18 @@ function handleWindowPointerDown(event: PointerEvent): void {
}
}
+function isEditorActionMenuDropdownTarget(target: EventTarget | null): boolean {
+ if (target instanceof Element) {
+ return target.closest(EDITOR_ACTION_MENU_DROPDOWN_SELECTOR) !== null;
+ }
+
+ if (target instanceof Node) {
+ return target.parentElement?.closest(EDITOR_ACTION_MENU_DROPDOWN_SELECTOR) !== null;
+ }
+
+ return false;
+}
+
function resolveTableContextFromPointer(clientX: number, clientY: number): Omit
| null {
const editor = getEditor();
if (!editor) {
@@ -1870,9 +1917,7 @@ function runTableContextAction(action: TableContextMenuAction): void {
:x="tableContextMenu.x"
:y="tableContextMenu.y"
:groups="tableContextMenuGroups"
- variant="tile"
- min-width="620px"
- max-width="720px"
+ variant="inline"
@select="handleTableContextMenuSelect"
/>
@@ -1882,8 +1927,6 @@ function runTableContextAction(action: TableContextMenuAction): void {
:y="imageContextMenu.y"
:groups="imageContextMenuGroups"
variant="inline"
- min-width="440px"
- max-width="min(520px, calc(100vw - 32px))"
@select="handleImageContextMenuSelect"
/>
diff --git a/apps/admin-web/src/components/MarkdownPreview.vue b/apps/admin-web/src/components/MarkdownPreview.vue
index 1d7406e..a67108f 100644
--- a/apps/admin-web/src/components/MarkdownPreview.vue
+++ b/apps/admin-web/src/components/MarkdownPreview.vue
@@ -108,24 +108,36 @@ const html = computed(() => renderer.render(props.content || ""));
.markdown-preview :deep(table) {
width: 100%;
- margin: 0 0 1em;
- border-collapse: collapse;
- overflow: hidden;
+ margin: 1em 0;
+ border-collapse: separate;
+ border-spacing: 0;
border: 1px solid #e5e7eb;
- border-radius: 12px;
+ border-radius: 8px;
+ overflow: hidden;
}
.markdown-preview :deep(th),
.markdown-preview :deep(td) {
- padding: 10px 12px;
- border: 1px solid #e5e7eb;
+ padding: 12px 16px;
+ border-bottom: 1px solid #e5e7eb;
+ border-right: 1px solid #e5e7eb;
text-align: left;
vertical-align: top;
}
+.markdown-preview :deep(th:last-child),
+.markdown-preview :deep(td:last-child) {
+ border-right: none;
+}
+
+.markdown-preview :deep(tr:last-child td) {
+ border-bottom: none;
+}
+
.markdown-preview :deep(th) {
background: #f9fafb;
font-weight: 600;
+ color: #374151;
}
.markdown-preview :deep(hr) {
diff --git a/apps/admin-web/src/components/PublishArticleModal.vue b/apps/admin-web/src/components/PublishArticleModal.vue
index b77074c..04127ef 100644
--- a/apps/admin-web/src/components/PublishArticleModal.vue
+++ b/apps/admin-web/src/components/PublishArticleModal.vue
@@ -475,7 +475,11 @@ function showPublishFailures(result: PublisherPublishResponse): void {
{{ coverRequired ? t("media.publish.messages.coverRequired") : t("media.publish.coverHint") }}
-
+
-
+
-
- {{ coverAssetUrl ? t("article.editor.coverReplace") : t("media.publish.coverUpload") }}
-
-
+
{{ t("article.editor.coverRemove") }}
-
+
{{ coverFileName || t("article.editor.coverSaved") }}
@@ -733,6 +734,10 @@ function showPublishFailures(result: PublisherPublishResponse): void {
margin-top: 14px;
}
+.publish-modal__cover-body--single {
+ grid-template-columns: 176px;
+}
+
.publish-modal__cover-preview {
display: flex;
align-items: center;
diff --git a/apps/admin-web/src/components/editor-common/EditorActionMenu.vue b/apps/admin-web/src/components/editor-common/EditorActionMenu.vue
index c731aed..4fbd653 100644
--- a/apps/admin-web/src/components/editor-common/EditorActionMenu.vue
+++ b/apps/admin-web/src/components/editor-common/EditorActionMenu.vue
@@ -1,5 +1,6 @@