feat: 专员 JSON 配置体系 + 配图提示词规则重构
- 新增配图提示词 JSON 规则(image_prompt.json):封面图 + 每 500 字按字数分布 - 新增 Go 配置加载器(weixin_public_account_image_prompt_config.go):从嵌入的 spec 目录读取 JSON 配置 - 新增 weixin_public_account 完整 spec 目录:skill.md, workflow.json, domains.json, policy.json, validators/ - 新增 pj0012 参考文件(pj0012-wechat-image-spec.md):配图规则 + 专员 JSON 定义体系 - 产物 Tab 添加下载按钮 + 整体导出区 - 修复前端 Markdown 渲染 bug + 步骤消息分段问题 所有专员定义向 JSON 配置迁移,以 weixin_public_account 为样板。
This commit is contained in:
+80
-2
@@ -158,9 +158,9 @@
|
||||
class="oa-btn oa-btn-tiny"
|
||||
type="button"
|
||||
:disabled="previewLoading"
|
||||
@click="openArtifactPreview(file)"
|
||||
@click="downloadFile(file)"
|
||||
>
|
||||
预览
|
||||
下载
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -169,6 +169,50 @@
|
||||
</template>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 整体导出区:所有格式一次导出 -->
|
||||
<section class="oa-stack-section">
|
||||
<div class="oa-stack-head">
|
||||
<span class="oa-stack-title">整体导出</span>
|
||||
</div>
|
||||
<div v-if="hasTask" class="oa-export-row">
|
||||
<button
|
||||
class="oa-btn"
|
||||
type="button"
|
||||
:disabled="loading"
|
||||
@click="download('preview_md')"
|
||||
>
|
||||
预览 MD
|
||||
</button>
|
||||
<button
|
||||
class="oa-btn"
|
||||
type="button"
|
||||
:disabled="loading"
|
||||
@click="download('preview_html')"
|
||||
>
|
||||
预览 HTML
|
||||
</button>
|
||||
<button
|
||||
class="oa-btn"
|
||||
type="button"
|
||||
:disabled="loading"
|
||||
@click="download('pagemd')"
|
||||
>
|
||||
分页 MD
|
||||
</button>
|
||||
<button
|
||||
class="oa-btn"
|
||||
type="button"
|
||||
:disabled="loading"
|
||||
@click="download('docx')"
|
||||
>
|
||||
Word (.docx)
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="oa-empty oa-empty-inline">
|
||||
没有可导出的内容,先运行创作流程。
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
@@ -1085,6 +1129,40 @@ async function download(format) {
|
||||
}
|
||||
}
|
||||
|
||||
// 从产物文件下载(HTML / MD 直接下载,图片通过 API 获取)
|
||||
async function downloadFile(file) {
|
||||
loading.value = true
|
||||
try {
|
||||
let blob = null
|
||||
let filename = file.name || 'download'
|
||||
|
||||
if (file.kind === 'html' && file.html) {
|
||||
blob = new Blob([file.html], { type: 'text/html' })
|
||||
} else if (file.kind === 'markdown' && file.text) {
|
||||
blob = new Blob([file.text], { type: 'text/markdown' })
|
||||
} else if (file.kind === 'image' && file.url) {
|
||||
blob = await fetchWeixinPublicAccountGeneratedImage(file.url)
|
||||
if (file.name) filename = file.name
|
||||
}
|
||||
|
||||
if (blob) {
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = filename
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
ElMessage.success('下载成功')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error?.message || '下载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function exportFileName(format) {
|
||||
if (format === 'preview_html') return 'weixin-public-account-preview.html'
|
||||
if (format === 'pagemd') return 'weixin-public-account-pagemd.md'
|
||||
|
||||
@@ -474,7 +474,13 @@ function renderMarkdownBlock(block) {
|
||||
const headingMatch = trimmed.match(/^(#{1,3})\s+(.+)$/)
|
||||
if (headingMatch) {
|
||||
const level = headingMatch[1].length
|
||||
return `<h${level}>${renderInlineMarkdown(headingMatch[2])}</h${level}>`
|
||||
const heading = `<h${level}>${renderInlineMarkdown(headingMatch[2])}</h${level}>`
|
||||
// If the block has more lines after the heading, render them as additional content
|
||||
const afterHeading = trimmed.split('\n', 2)[1]
|
||||
if (afterHeading && afterHeading.trim()) {
|
||||
return heading + ' ' + renderMarkdownBlock(afterHeading.trim())
|
||||
}
|
||||
return heading
|
||||
}
|
||||
|
||||
const lines = trimmed.split('\n')
|
||||
@@ -792,7 +798,7 @@ function getWeixinPublicAccountStep(payload, stepKey) {
|
||||
return payload?.workflow?.steps?.[stepKey] || {}
|
||||
}
|
||||
|
||||
function trimWeixinPublicAccountText(value, maxLength = 180) {
|
||||
function trimWeixinPublicAccountText(value, maxLength = 360) {
|
||||
const text = String(value || '').trim()
|
||||
if (!text) return ''
|
||||
return text.length > maxLength ? `${text.slice(0, maxLength).trim()}...` : text
|
||||
@@ -876,7 +882,7 @@ function buildWeixinPublicAccountOutlineStageMessage(payload) {
|
||||
'### 3. 提纲步骤已完成',
|
||||
sections.length
|
||||
? `本轮提纲主结构:\n${formatWeixinPublicAccountBulletList(sections)}`
|
||||
: trimWeixinPublicAccountText(outline, 220) || '本轮提纲已生成。',
|
||||
: trimWeixinPublicAccountText(outline, 500) || '本轮提纲已生成。',
|
||||
].filter(Boolean).join('\n\n'),
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
}
|
||||
@@ -892,7 +898,7 @@ function buildWeixinPublicAccountContentStageMessage(payload) {
|
||||
.replace(/^>\s+.*$/gm, '')
|
||||
.replace(/\n{2,}/g, '\n')
|
||||
.trim(),
|
||||
220,
|
||||
500,
|
||||
)
|
||||
return {
|
||||
role: 'assistant',
|
||||
@@ -910,15 +916,18 @@ function buildWeixinPublicAccountImagePromptStageMessage(payload) {
|
||||
const output = step.output || {}
|
||||
const count = Number(output.count || 0)
|
||||
const prompts = Array.isArray(output.image_prompts) ? output.image_prompts.slice(0, 4) : []
|
||||
const lines = [
|
||||
`已提炼 ${count} 条配图提示词。`,
|
||||
...(prompts.length
|
||||
? prompts.map((item, index) => `${index + 1}. [${item?.section || '段落配图'}] ${trimWeixinPublicAccountText(item?.prompt || '', 80)}`)
|
||||
: []),
|
||||
]
|
||||
return {
|
||||
role: 'assistant',
|
||||
content: [
|
||||
'### 5. 配图提示词步骤已完成',
|
||||
count > 0 ? `已提炼 ${count} 条配图提示词。` : '本轮未提炼出可用配图提示词。',
|
||||
...(prompts.length
|
||||
? prompts.map((item, index) => `${index + 1}. [${item?.section || '段落配图'}] ${trimWeixinPublicAccountText(item?.prompt || '', 80)}`)
|
||||
: []),
|
||||
].filter(Boolean).join('\n'),
|
||||
lines.length ? lines.join('\n') : '本轮未提炼出可用配图提示词。',
|
||||
].filter(Boolean).join('\n\n'),
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
}
|
||||
}
|
||||
@@ -929,15 +938,18 @@ function buildWeixinPublicAccountImageGenerationStageMessage(payload) {
|
||||
const output = step.output || {}
|
||||
const count = Number(output.count || 0)
|
||||
const refs = Array.isArray(output.image_refs) ? output.image_refs.slice(0, 4) : []
|
||||
const lines = [
|
||||
`已生成 ${count} 张配图。`,
|
||||
...(refs.length
|
||||
? refs.map((item, index) => `${index + 1}. [${item?.section || '段落配图'}] ${trimWeixinPublicAccountText(item?.prompt || '', 60)}`)
|
||||
: []),
|
||||
]
|
||||
return {
|
||||
role: 'assistant',
|
||||
content: [
|
||||
'### 6. 图片生成步骤已完成',
|
||||
count > 0 ? `已生成 ${count} 张配图。` : '本轮未生成图片结果。',
|
||||
...(refs.length
|
||||
? refs.map((item, index) => `${index + 1}. [${item?.section || '段落配图'}] ${trimWeixinPublicAccountText(item?.prompt || '', 60)}`)
|
||||
: []),
|
||||
].filter(Boolean).join('\n'),
|
||||
lines.length ? lines.join('\n') : '本轮未生成图片结果。',
|
||||
].filter(Boolean).join('\n\n'),
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
}
|
||||
}
|
||||
@@ -952,7 +964,7 @@ function buildWeixinPublicAccountPreviewStageMessage(payload) {
|
||||
content: [
|
||||
'### 7. 预览与导出步骤已完成',
|
||||
`已整合正文${imageCount ? `与 ${imageCount} 张配图` : ''}生成预览稿,可检查排版并导出。`,
|
||||
].filter(Boolean).join('\n'),
|
||||
].filter(Boolean).join('\n\n'),
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
}
|
||||
}
|
||||
@@ -967,7 +979,7 @@ function buildWeixinPublicAccountPageMarkdownStageMessage(payload) {
|
||||
content: [
|
||||
'### 8. 分页 MD 步骤已完成',
|
||||
count > 0 ? `已按段落结构生成 ${count} 页分页 MD。` : '分页 MD 已生成。',
|
||||
].filter(Boolean).join('\n'),
|
||||
].filter(Boolean).join('\n\n'),
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user