diff --git a/apps/admin-web/src/components/ArticleEditorCanvas.vue b/apps/admin-web/src/components/ArticleEditorCanvas.vue index 951c7fe..de68d29 100644 --- a/apps/admin-web/src/components/ArticleEditorCanvas.vue +++ b/apps/admin-web/src/components/ArticleEditorCanvas.vue @@ -1,7 +1,15 @@ @@ -605,6 +1580,211 @@ function handleWindowPointerUp(): void { line-height: 1.4; } +.article-editor-canvas__table-context-menu { + position: fixed; + z-index: 1200; + display: flex; + flex-direction: column; + gap: 16px; + min-width: 620px; + max-width: 720px; + padding: 16px; + border: 1px solid #edf2fa; + border-radius: 16px; + background: #ffffff; + box-shadow: + 0 20px 48px rgba(15, 23, 42, 0.08), + 0 6px 16px rgba(15, 23, 42, 0.04); +} + +.article-editor-canvas__table-context-row--top { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 12px; +} + +.article-editor-canvas__table-context-row--bottom { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 12px; +} + +.article-editor-canvas__table-context-divider { + height: 1px; + background: #f0f3f8; +} + +.article-editor-canvas__table-context-action { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + min-height: 86px; + padding: 12px 10px; + border: 1px solid transparent; + border-radius: 12px; + background: #f7f9fa; + color: #3f4a5c; + cursor: pointer; + transition: all 0.2s ease; +} + +.article-editor-canvas__table-context-action span { + font-size: 13px; + font-weight: 600; + line-height: 1.4; + text-align: center; +} + +.article-editor-canvas__table-context-action :deep(svg) { + font-size: 22px; + color: #5c6b81; + transition: color 0.2s ease; +} + +.article-editor-canvas__table-context-action:hover:not(:disabled) { + background: #eff4ff; + border-color: #c2d6ff; + color: #245bdb; +} + +.article-editor-canvas__table-context-action:hover:not(:disabled) :deep(svg) { + color: #245bdb; +} + +.article-editor-canvas__table-context-action--danger:hover:not(:disabled) { + background: #fff1f1; + border-color: #ffccc7; + color: #cf1322; +} + +.article-editor-canvas__table-context-action--danger:hover:not(:disabled) :deep(svg) { + color: #cf1322; +} + +.article-editor-canvas__table-context-action:disabled { + opacity: 0.46; + cursor: not-allowed; +} + +.article-editor-canvas__image-selection { + position: fixed; + z-index: 1100; + box-sizing: border-box; + border: 2px solid #2f6bff; + border-radius: 18px; + box-shadow: + 0 0 0 1px rgba(255, 255, 255, 0.9), + 0 14px 28px rgba(47, 107, 255, 0.12); + pointer-events: none; +} + +.article-editor-canvas__image-handle { + position: absolute; + width: 14px; + height: 14px; + border: 2px solid #ffffff; + border-radius: 999px; + background: #2f6bff; + box-shadow: 0 4px 10px rgba(47, 107, 255, 0.28); + pointer-events: auto; +} + +.article-editor-canvas__image-handle--nw { + top: 0; + left: 0; + cursor: nwse-resize; + transform: translate(-50%, -50%); +} + +.article-editor-canvas__image-handle--ne { + top: 0; + right: 0; + cursor: nesw-resize; + transform: translate(50%, -50%); +} + +.article-editor-canvas__image-handle--sw { + bottom: 0; + left: 0; + cursor: nesw-resize; + transform: translate(-50%, 50%); +} + +.article-editor-canvas__image-handle--se { + right: 0; + bottom: 0; + cursor: nwse-resize; + transform: translate(50%, 50%); +} + +.article-editor-canvas__image-context-menu { + position: fixed; + z-index: 1200; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + min-width: 300px; + max-width: min(360px, calc(100vw - 32px)); + padding: 12px; + border: 1px solid #e8eef8; + border-radius: 16px; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 251, 255, 0.98) 100%); + box-shadow: + 0 20px 48px rgba(15, 23, 42, 0.1), + 0 6px 18px rgba(15, 23, 42, 0.06); + backdrop-filter: blur(14px); +} + +.article-editor-canvas__image-context-action { + display: flex; + align-items: center; + gap: 10px; + min-height: 46px; + padding: 0 14px; + border: 1px solid transparent; + border-radius: 12px; + background: #f7f9fc; + color: #344054; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: + border-color 0.18s ease, + background-color 0.18s ease, + color 0.18s ease, + transform 0.18s ease; +} + +.article-editor-canvas__image-context-action :deep(svg) { + font-size: 18px; + color: #52607a; + transition: color 0.18s ease; +} + +.article-editor-canvas__image-context-action:hover { + border-color: #c8d8ff; + background: #edf4ff; + color: #245bdb; + transform: translateY(-1px); +} + +.article-editor-canvas__image-context-action:hover :deep(svg) { + color: #245bdb; +} + +.article-editor-canvas__image-context-action--danger:hover { + border-color: #ffd2cf; + background: #fff1f0; + color: #cf1322; +} + +.article-editor-canvas__image-context-action--danger:hover :deep(svg) { + color: #cf1322; +} + .article-editor-canvas__surface { position: relative; z-index: 1; @@ -655,6 +1835,30 @@ function handleWindowPointerUp(): void { outline: none; } +.article-editor-canvas__surface :deep(.milkdown-image-block > .image-wrapper) { + margin: 0 auto; +} + +.article-editor-canvas__surface :deep(.milkdown-image-block.selected > .image-wrapper::before) { + background: transparent; + border: 0; +} + +.article-editor-canvas__surface :deep(.milkdown-image-block img) { + max-width: min(100%, 960px); + margin: 0 auto; +} + +.article-editor-canvas__surface :deep(.milkdown-image-block > .image-wrapper .operation) { + opacity: 0 !important; + pointer-events: none; +} + +.article-editor-canvas__surface :deep(.milkdown-image-block > .image-wrapper .image-resize-handle) { + opacity: 0 !important; + pointer-events: none; +} + .article-editor-canvas__mask { position: absolute; inset: 0; diff --git a/apps/admin-web/src/i18n/messages/en-US.ts b/apps/admin-web/src/i18n/messages/en-US.ts index 01e2119..53bdd6f 100644 --- a/apps/admin-web/src/i18n/messages/en-US.ts +++ b/apps/admin-web/src/i18n/messages/en-US.ts @@ -410,6 +410,24 @@ const enUS = { empty: "Drag to choose a table size", hint: "Keep dragging near the edge to expand the grid", }, + tableMenu: { + addRowBefore: "Insert row above", + addRowAfter: "Insert row below", + deleteRow: "Delete row", + addColBefore: "Insert left column", + addColAfter: "Insert right column", + deleteCol: "Delete column", + alignLeft: "Align left", + alignCenter: "Align center", + alignRight: "Align right", + deleteTable: "Delete table", + }, + imageMenu: { + replace: "Replace image", + caption: "Toggle caption", + resetSize: "Reset size", + delete: "Delete image", + }, messages: { saved: "Article saved.", generating: "This article is still generating. Try editing it later.", diff --git a/apps/admin-web/src/i18n/messages/zh-CN.ts b/apps/admin-web/src/i18n/messages/zh-CN.ts index 183b314..0acda0a 100644 --- a/apps/admin-web/src/i18n/messages/zh-CN.ts +++ b/apps/admin-web/src/i18n/messages/zh-CN.ts @@ -417,6 +417,24 @@ const zhCN = { empty: "拖拽选择表格尺寸", hint: "按住拖动到边缘可继续扩展", }, + tableMenu: { + addRowBefore: "上方插行", + addRowAfter: "下方插行", + deleteRow: "删除行", + addColBefore: "左侧插列", + addColAfter: "右侧插列", + deleteCol: "删除列", + alignLeft: "左对齐", + alignCenter: "居中", + alignRight: "右对齐", + deleteTable: "删除表格", + }, + imageMenu: { + replace: "替换图片", + caption: "切换图注", + resetSize: "重置大小", + delete: "删除图片", + }, messages: { saved: "文章已保存", generating: "文章还在生成中,请稍后再编辑。",