This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
|||||||
subscribeArticleGeneration,
|
subscribeArticleGeneration,
|
||||||
type ArticleGenerationStreamEvent,
|
type ArticleGenerationStreamEvent,
|
||||||
} from '@/lib/api'
|
} from '@/lib/api'
|
||||||
|
import { copyTextToClipboard } from '@/lib/clipboard'
|
||||||
import {
|
import {
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
getGenerateStatusMeta,
|
getGenerateStatusMeta,
|
||||||
@@ -257,7 +258,7 @@ async function copyPublishLink(record: PublishRecord): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await navigator.clipboard.writeText(target)
|
await copyTextToClipboard(target)
|
||||||
message.success(t('media.records.copySuccess'))
|
message.success(t('media.records.copySuccess'))
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import PublishArticleModal from '@/components/PublishArticleModal.vue'
|
|||||||
import { articlesApi, promptRulesApi } from '@/lib/api'
|
import { articlesApi, promptRulesApi } from '@/lib/api'
|
||||||
import { buildArticleClipboardContent, resolveArticleActionState } from '@/lib/article-list-actions'
|
import { buildArticleClipboardContent, resolveArticleActionState } from '@/lib/article-list-actions'
|
||||||
import { useArticleRegenerateAction } from '@/lib/article-regenerate-action'
|
import { useArticleRegenerateAction } from '@/lib/article-regenerate-action'
|
||||||
|
import { copyTextToClipboard } from '@/lib/clipboard'
|
||||||
import { getGenerateStatusMeta, getPublishStatusMeta } from '@/lib/display'
|
import { getGenerateStatusMeta, getPublishStatusMeta } from '@/lib/display'
|
||||||
import { formatError } from '@/lib/errors'
|
import { formatError } from '@/lib/errors'
|
||||||
import { formatPublishPlatformList } from '@/lib/publish-platforms'
|
import { formatPublishPlatformList } from '@/lib/publish-platforms'
|
||||||
@@ -223,7 +224,7 @@ async function copyArticle(articleId: number): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await navigator.clipboard.writeText(content)
|
await copyTextToClipboard(content)
|
||||||
message.success(t('common.copySuccess'))
|
message.success(t('common.copySuccess'))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(formatError(error) || t('common.noData'))
|
message.error(formatError(error) || t('common.noData'))
|
||||||
|
|||||||
@@ -72,8 +72,27 @@ export function resolveArticleActionState(
|
|||||||
export function buildArticleClipboardContent(
|
export function buildArticleClipboardContent(
|
||||||
detail: Pick<ArticleDetail, 'title' | 'markdown_content'>,
|
detail: Pick<ArticleDetail, 'title' | 'markdown_content'>,
|
||||||
): string {
|
): string {
|
||||||
return [detail.title ? `# ${detail.title}` : '', detail.markdown_content ?? '']
|
const title = detail.title?.trim() ?? ''
|
||||||
.filter(Boolean)
|
const markdown = detail.markdown_content?.trim() ?? ''
|
||||||
.join('\n\n')
|
|
||||||
.trim()
|
if (!title) {
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!markdown) {
|
||||||
|
return `# ${title}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getLeadingH1Text(markdown) === title) {
|
||||||
|
return markdown
|
||||||
|
}
|
||||||
|
|
||||||
|
return [`# ${title}`, markdown].join('\n\n').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLeadingH1Text(markdown: string): string | null {
|
||||||
|
const normalizedMarkdown = markdown.replace(/^\uFEFF/, '').trimStart()
|
||||||
|
const firstLine = normalizedMarkdown.split(/\r?\n/, 1)[0]?.trim() ?? ''
|
||||||
|
const match = firstLine.match(/^#\s+(.+?)\s*#*$/)
|
||||||
|
return match?.[1]?.trim() ?? null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import PublishArticleModal from '@/components/PublishArticleModal.vue'
|
|||||||
import { articlesApi } from '@/lib/api'
|
import { articlesApi } from '@/lib/api'
|
||||||
import { buildArticleClipboardContent, resolveArticleActionState } from '@/lib/article-list-actions'
|
import { buildArticleClipboardContent, resolveArticleActionState } from '@/lib/article-list-actions'
|
||||||
import { useArticleRegenerateAction } from '@/lib/article-regenerate-action'
|
import { useArticleRegenerateAction } from '@/lib/article-regenerate-action'
|
||||||
|
import { copyTextToClipboard } from '@/lib/clipboard'
|
||||||
import { getGenerateStatusMeta, getPublishStatusMeta, hasActiveGenerationStatus } from '@/lib/display'
|
import { getGenerateStatusMeta, getPublishStatusMeta, hasActiveGenerationStatus } from '@/lib/display'
|
||||||
import { formatError } from '@/lib/errors'
|
import { formatError } from '@/lib/errors'
|
||||||
import { useCompanyStore } from '@/stores/company'
|
import { useCompanyStore } from '@/stores/company'
|
||||||
@@ -268,7 +269,7 @@ async function copyArticle(articleId: number): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await navigator.clipboard.writeText(content)
|
await copyTextToClipboard(content)
|
||||||
message.success(t('common.copySuccess'))
|
message.success(t('common.copySuccess'))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(formatError(error) || t('common.noData'))
|
message.error(formatError(error) || t('common.noData'))
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { computed, ref, watch } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import { publishTasksApi, tenantAccountsApi } from '@/lib/api'
|
import { publishTasksApi, tenantAccountsApi } from '@/lib/api'
|
||||||
|
import { copyTextToClipboard } from '@/lib/clipboard'
|
||||||
import { formatDateTime } from '@/lib/display'
|
import { formatDateTime } from '@/lib/display'
|
||||||
import { formatError } from '@/lib/errors'
|
import { formatError } from '@/lib/errors'
|
||||||
import { resolvePlatformLogoUrl } from '@/lib/publish-account-cards'
|
import { resolvePlatformLogoUrl } from '@/lib/publish-account-cards'
|
||||||
@@ -473,23 +474,6 @@ function rowClassName(record: PublishTaskItem): string {
|
|||||||
return isPendingStatus(record.status) ? 'publish-management-row--pending' : ''
|
return isPendingStatus(record.status) ? 'publish-management-row--pending' : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyTextToClipboard(text: string): Promise<void> {
|
|
||||||
if (navigator.clipboard?.writeText) {
|
|
||||||
await navigator.clipboard.writeText(text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const textarea = document.createElement('textarea')
|
|
||||||
textarea.value = text
|
|
||||||
textarea.setAttribute('readonly', 'true')
|
|
||||||
textarea.style.position = 'fixed'
|
|
||||||
textarea.style.left = '-9999px'
|
|
||||||
document.body.appendChild(textarea)
|
|
||||||
textarea.select()
|
|
||||||
document.execCommand('copy')
|
|
||||||
document.body.removeChild(textarea)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function copyErrorMessage(record: PublishTaskItem): Promise<void> {
|
async function copyErrorMessage(record: PublishTaskItem): Promise<void> {
|
||||||
if (!record.errorMessage) {
|
if (!record.errorMessage) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import PublishArticleModal from '@/components/PublishArticleModal.vue'
|
|||||||
import { articlesApi, templatesApi, workspaceApi } from '@/lib/api'
|
import { articlesApi, templatesApi, workspaceApi } from '@/lib/api'
|
||||||
import { buildArticleClipboardContent, resolveArticleActionState } from '@/lib/article-list-actions'
|
import { buildArticleClipboardContent, resolveArticleActionState } from '@/lib/article-list-actions'
|
||||||
import { useArticleRegenerateAction } from '@/lib/article-regenerate-action'
|
import { useArticleRegenerateAction } from '@/lib/article-regenerate-action'
|
||||||
|
import { copyTextToClipboard } from '@/lib/clipboard'
|
||||||
import {
|
import {
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
getGenerateStatusMeta,
|
getGenerateStatusMeta,
|
||||||
@@ -466,7 +467,7 @@ async function copyArticle(articleId: number): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await navigator.clipboard.writeText(content)
|
await copyTextToClipboard(content)
|
||||||
message.success(t('common.copySuccess'))
|
message.success(t('common.copySuccess'))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(formatError(error) || t('common.noData'))
|
message.error(formatError(error) || t('common.noData'))
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import PublishArticleModal from '@/components/PublishArticleModal.vue'
|
|||||||
import { articlesApi, tenantAccountsApi, workspaceApi } from '@/lib/api'
|
import { articlesApi, tenantAccountsApi, workspaceApi } from '@/lib/api'
|
||||||
import { buildArticleClipboardContent, resolveArticleActionState } from '@/lib/article-list-actions'
|
import { buildArticleClipboardContent, resolveArticleActionState } from '@/lib/article-list-actions'
|
||||||
import { useArticleRegenerateAction } from '@/lib/article-regenerate-action'
|
import { useArticleRegenerateAction } from '@/lib/article-regenerate-action'
|
||||||
|
import { copyTextToClipboard } from '@/lib/clipboard'
|
||||||
import { formatDateTime, getTemplateMeta, hasActiveGenerationStatus } from '@/lib/display'
|
import { formatDateTime, getTemplateMeta, hasActiveGenerationStatus } from '@/lib/display'
|
||||||
import { formatError } from '@/lib/errors'
|
import { formatError } from '@/lib/errors'
|
||||||
import { isMediaPublishAccount } from '@/lib/publish-platforms'
|
import { isMediaPublishAccount } from '@/lib/publish-platforms'
|
||||||
@@ -265,7 +266,7 @@ async function copyArticle(articleId: number): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await navigator.clipboard.writeText(content)
|
await copyTextToClipboard(content)
|
||||||
message.success(t('common.copySuccess'))
|
message.success(t('common.copySuccess'))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error(formatError(error) || t('common.noData'))
|
message.error(formatError(error) || t('common.noData'))
|
||||||
|
|||||||
Reference in New Issue
Block a user