Files
test-repo/docs/.vitepress/theme/components/appendix/image-gen-intro/PromptEngineeringDemo.vue
T

521 lines
13 KiB
Vue
Raw Normal View History

<!--
PromptEngineeringDemo.vue
提示词工程演示组件
用途
展示提示词如何影响生成结果帮助用户理解提示词工程的重要性
交互功能
- 提示词实时编辑
- 关键词提取和高亮
- 权重调节
- 对比不同提示词的效果
-->
<template>
<div class="prompt-engineering-demo">
<el-card shadow="never">
<template #header>
<div class="header-title">
<el-icon><EditPen /></el-icon>
<span> 提示词工程实验室</span>
</div>
</template>
<div class="demo-layout">
<!-- 左侧提示词编辑 -->
<div class="prompt-panel">
<div class="prompt-input-section">
<label>提示词 (Prompt)</label>
<el-input
v-model="prompt"
type="textarea"
:rows="4"
placeholder="输入你的提示词..."
/>
</div>
<div class="prompt-analysis">
<div class="analysis-title">
关键词分析
</div>
<div class="keywords-list">
<div
v-for="(keyword, index) in analyzedKeywords"
:key="index"
class="keyword-item"
:class="keyword.type"
>
<span class="keyword-text">{{ keyword.text }}</span>
<el-slider
v-model="keyword.weight"
:min="0"
:max="2"
:step="0.1"
size="small"
class="weight-slider"
/>
<span class="weight-value">{{ keyword.weight.toFixed(1) }}</span>
</div>
</div>
</div>
<div class="prompt-tips">
<el-collapse>
<el-collapse-item title="💡 提示词技巧">
<ul class="tips-list">
<li><strong>主体描述</strong>明确你要画什么 "一只橘猫"</li>
<li><strong>风格词</strong>指定艺术风格 "水彩画""赛博朋克"</li>
<li><strong>质量词</strong>提升画质 "8k"" masterpiece""highly detailed"</li>
<li><strong>光照</strong>控制光线效果 "golden hour""volumetric lighting"</li>
<li><strong>权重语法</strong>使用 (word:1.5) 增加权重(word:0.5) 降低权重</li>
</ul>
</el-collapse-item>
</el-collapse>
</div>
</div>
<!-- 右侧效果预览 -->
<div class="preview-panel">
<div class="preview-tabs">
<el-tabs v-model="activeTab">
<el-tab-pane
label="结构解析"
name="structure"
>
<div class="structure-viz">
<div class="structure-section">
<div class="section-header">
<el-tag type="primary">
主体 (Subject)
</el-tag>
</div>
<div class="section-content">
{{ extractSubject() || '未检测到主体' }}
</div>
</div>
<div class="structure-section">
<div class="section-header">
<el-tag type="success">
风格 (Style)
</el-tag>
</div>
<div class="section-content">
{{ extractStyle() || '未检测到风格词' }}
</div>
</div>
<div class="structure-section">
<div class="section-header">
<el-tag type="warning">
质量 (Quality)
</el-tag>
</div>
<div class="section-content">
{{ extractQuality() || '未检测到质量词' }}
</div>
</div>
<div class="structure-section">
<div class="section-header">
<el-tag type="info">
环境 (Environment)
</el-tag>
</div>
<div class="section-content">
{{ extractEnvironment() || '未检测到环境描述' }}
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane
label="对比示例"
name="comparison"
>
<div class="comparison-list">
<div
v-for="(example, index) in promptExamples"
:key="index"
class="comparison-item"
:class="{ active: selectedExample === index }"
@click="selectExample(index)"
>
<div class="example-prompt">
{{ example.prompt }}
</div>
<div class="example-desc">
{{ example.description }}
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane
label="负面提示词"
name="negative"
>
<div class="negative-prompt-section">
<label>负面提示词 (Negative Prompt)</label>
<el-input
v-model="negativePrompt"
type="textarea"
:rows="3"
placeholder="输入你不希望出现的内容..."
/>
<div class="negative-presets">
<el-tag
v-for="preset in negativePresets"
:key="preset"
size="small"
class="negative-preset-tag"
@click="addNegativePreset(preset)"
>
+ {{ preset }}
</el-tag>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
<div class="info-box">
<p>
<span class="icon">💡</span>
<strong>提示词工程的核心</strong>
好的提示词 = 清晰的描述 + 适当的风格词 + 质量增强词通过调整不同部分的权重可以精确控制生成结果
</p>
</div>
</el-card>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { EditPen } from '@element-plus/icons-vue'
const prompt = ref('一只橘猫,坐在窗台上,阳光照射,水彩画风格,8k高清')
const negativePrompt = ref('模糊, 低质量, 变形, 多余的手指')
const activeTab = ref('structure')
const selectedExample = ref(0)
// 关键词类型
const keywordTypes = {
subject: ['猫', '狗', '人', '风景', '建筑', '汽车', '花', '树'],
style: ['水彩', '油画', '素描', '赛博朋克', '像素', '写实', '卡通', '动漫'],
quality: ['8k', '高清', ' masterpiece', 'detailed', 'high quality', '4k', 'sharp'],
environment: ['阳光', '雨天', '夜晚', '森林', '城市', '海边', '室内', '户外']
}
// 分析关键词
const analyzedKeywords = computed(() => {
const keywords = []
const words = prompt.value.split(/[,\s]+/).filter(w => w.length > 0)
words.forEach(word => {
let type = 'other'
if (keywordTypes.subject.some(k => word.includes(k))) type = 'subject'
else if (keywordTypes.style.some(k => word.includes(k))) type = 'style'
else if (keywordTypes.quality.some(k => word.toLowerCase().includes(k.toLowerCase()))) type = 'quality'
else if (keywordTypes.environment.some(k => word.includes(k))) type = 'environment'
keywords.push({
text: word,
type,
weight: 1.0
})
})
return keywords
})
// 提取不同类型的词
const extractSubject = () => {
return analyzedKeywords.value
.filter(k => k.type === 'subject')
.map(k => k.text)
.join(', ')
}
const extractStyle = () => {
return analyzedKeywords.value
.filter(k => k.type === 'style')
.map(k => k.text)
.join(', ')
}
const extractQuality = () => {
return analyzedKeywords.value
.filter(k => k.type === 'quality')
.map(k => k.text)
.join(', ')
}
const extractEnvironment = () => {
return analyzedKeywords.value
.filter(k => k.type === 'environment')
.map(k => k.text)
.join(', ')
}
// 提示词示例
const promptExamples = [
{
prompt: '一只猫',
description: '基础描述,结果可能不够理想'
},
{
prompt: '一只橘猫,坐在窗台上',
description: '添加主体细节和场景'
},
{
prompt: '一只橘猫,坐在窗台上,阳光照射,水彩画风格',
description: '添加光照和风格'
},
{
prompt: '一只橘猫,坐在窗台上,阳光照射,水彩画风格,8k高清, masterpiece',
description: '完整提示词,包含质量词'
}
]
// 负面提示词预设
const negativePresets = [
'模糊',
'低质量',
'变形',
'多余的手指',
'扭曲的脸',
'噪点',
'水印',
'文字'
]
const selectExample = (index) => {
selectedExample.value = index
prompt.value = promptExamples[index].prompt
}
const addNegativePreset = (preset) => {
if (!negativePrompt.value.includes(preset)) {
negativePrompt.value = negativePrompt.value
? `${negativePrompt.value}, ${preset}`
: preset
}
}
</script>
<style scoped>
.prompt-engineering-demo {
margin: 0.5rem 0;
}
.header-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
}
.demo-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
@media (max-width: 768px) {
.demo-layout {
grid-template-columns: 1fr;
}
}
.prompt-panel {
display: flex;
flex-direction: column;
gap: 16px;
}
.prompt-input-section label {
display: block;
font-size: 0.875rem;
font-weight: 500;
margin-bottom: 8px;
color: var(--vp-c-text-2);
}
.prompt-analysis {
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 16px;
}
.analysis-title {
font-weight: 500;
margin-bottom: 12px;
}
.keywords-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.keyword-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
background: var(--vp-c-bg);
border-radius: 6px;
border-left: 3px solid var(--vp-c-divider);
}
.keyword-item.subject {
border-left-color: #409eff;
}
.keyword-item.style {
border-left-color: #67c23a;
}
.keyword-item.quality {
border-left-color: #e6a23c;
}
.keyword-item.environment {
border-left-color: #909399;
}
.keyword-text {
min-width: 80px;
font-size: 0.875rem;
}
.weight-slider {
flex: 1;
}
.weight-value {
min-width: 40px;
text-align: right;
font-size: 0.875rem;
color: var(--vp-c-text-3);
}
.prompt-tips {
margin-top: 8px;
}
.tips-list {
margin: 0;
padding-left: 20px;
}
.tips-list li {
margin-bottom: 8px;
line-height: 1.6;
}
.preview-panel {
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 16px;
}
.structure-viz {
display: flex;
flex-direction: column;
gap: 16px;
}
.structure-section {
background: var(--vp-c-bg);
border-radius: 6px;
padding: 12px;
}
.section-header {
margin-bottom: 8px;
}
.section-content {
font-size: 0.9rem;
color: var(--vp-c-text-2);
min-height: 24px;
}
.comparison-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.comparison-item {
padding: 12px;
background: var(--vp-c-bg);
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
border: 2px solid transparent;
}
.comparison-item:hover {
border-color: var(--vp-c-brand);
}
.comparison-item.active {
border-color: var(--vp-c-brand);
background: var(--vp-c-bg-mute);
}
.example-prompt {
font-weight: 500;
margin-bottom: 4px;
}
.example-desc {
font-size: 0.8rem;
color: var(--vp-c-text-3);
}
.negative-prompt-section {
display: flex;
flex-direction: column;
gap: 12px;
}
.negative-prompt-section label {
font-size: 0.875rem;
font-weight: 500;
}
.negative-presets {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.negative-preset-tag {
cursor: pointer;
transition: all 0.2s;
}
.negative-preset-tag:hover {
transform: translateY(-2px);
}
.info-box {
margin-top: 16px;
padding: 12px;
background: var(--vp-c-bg-mute);
border-radius: 6px;
font-size: 0.9rem;
line-height: 1.6;
}
.icon {
font-size: 1.2em;
}
</style>