feat: enhance article editor and detail components with improved link handling and UI updates

This commit is contained in:
2026-04-07 19:38:29 +08:00
parent 98ebb12fa1
commit d0f0271346
7 changed files with 281 additions and 171 deletions
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { CopyOutlined } from "@ant-design/icons-vue";
import { useQuery, useQueryClient } from "@tanstack/vue-query";
import { message } from "ant-design-vue";
import type { TableColumnsType } from "ant-design-vue";
@@ -6,6 +7,7 @@ import type { ArticleVersion, PublishRecord } from "@geo/shared-types";
import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import MarkdownPreview from "@/components/MarkdownPreview.vue";
import {
articlesApi,
isGenerationStreamEnabled,
@@ -224,23 +226,18 @@ async function refreshArticleData(): Promise<void> {
]);
}
async function copyPublishLink(record: PublishRecord): Promise<void> {
const target = record.external_article_url || record.external_manage_url;
if (!target) {
message.warning(t("media.records.linkEmpty"));
return;
}
await navigator.clipboard.writeText(target);
message.success(t("media.records.copySuccess"));
function resolvePublishLink(record: PublishRecord): string {
return record.external_article_url || record.external_manage_url || "";
}
function openPublishLink(record: PublishRecord): void {
const target = record.external_article_url || record.external_manage_url;
async function copyPublishLink(record: PublishRecord): Promise<void> {
const target = resolvePublishLink(record);
if (!target) {
message.warning(t("media.records.linkEmpty"));
return;
}
window.open(target, "_blank", "noopener,noreferrer");
await navigator.clipboard.writeText(target);
message.success(t("media.records.copySuccess"));
}
</script>
@@ -292,14 +289,14 @@ function openPublishLink(record: PublishRecord): void {
<a-tabs v-model:activeKey="activeTab" class="article-drawer__tabs">
<a-tab-pane key="content" :tab="t('article.preview')">
<div v-if="displayMarkdown" class="article-drawer__content">
<MarkdownPreview :content="displayMarkdown" />
</div>
<div
v-if="detail.html_content"
v-else-if="detail.html_content"
class="article-drawer__content"
v-html="detail.html_content"
/>
<pre v-else-if="displayMarkdown" class="article-drawer__markdown">{{
displayMarkdown
}}</pre>
<a-empty v-else :description="t('article.noContent')" />
</a-tab-pane>
@@ -355,26 +352,27 @@ function openPublishLink(record: PublishRecord): void {
{{ formatDateTime(record.published_at) }}
</template>
<template v-else-if="column.key === 'external_article_url'">
<div class="article-drawer__record-actions">
<div v-if="resolvePublishLink(record)" class="article-drawer__record-link-row">
<a
v-if="record.external_article_url || record.external_manage_url"
class="article-drawer__record-link"
:href="record.external_article_url || record.external_manage_url || '#'"
:href="resolvePublishLink(record)"
target="_blank"
rel="noreferrer"
>
{{ record.external_article_url || record.external_manage_url }}
{{ resolvePublishLink(record) }}
</a>
<span v-else class="article-drawer__record-empty">--</span>
<div class="article-drawer__record-buttons">
<a-button size="small" type="link" @click="openPublishLink(record)">
{{ t("media.records.open") }}
<a-tooltip :title="t('media.records.copy')">
<a-button
type="text"
size="small"
class="article-drawer__record-copy"
@click="copyPublishLink(record)"
>
<template #icon><CopyOutlined /></template>
</a-button>
<a-button size="small" type="link" @click="copyPublishLink(record)">
{{ t("media.records.copy") }}
</a-button>
</div>
</a-tooltip>
</div>
<span v-else class="article-drawer__record-empty">--</span>
</template>
</template>
</a-table>
@@ -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) {