feat: 重新设计“帮我写清楚”弹框
- 标题从“调教提示词”改成“帮我把要求写清楚”,开头说明怎么用 - 学生每次发的标成“你的要求 · 第 N 版”,AI 的回复标成“AI 的建议” - 输入框明确是“你的要求”;按钮改成“让 AI 再看看”和“用这段生成网页”, 没改过的要求不会重复发;AI 回复时不能直接生成 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
51ee246a6f
commit
1ba31844a9
1 file changed
+109
-81
@@ -2,83 +2,75 @@
|
||||
<n-modal
|
||||
:show="isOpen"
|
||||
preset="card"
|
||||
title="调教提示词"
|
||||
style="width: min(560px, 92vw)"
|
||||
title="帮我把要求写清楚"
|
||||
style="width: min(640px, 92vw)"
|
||||
:mask-closable="!streaming"
|
||||
@update:show="onModalClose"
|
||||
>
|
||||
<div class="guidance-body">
|
||||
<div class="guidance-messages" ref="messagesRef">
|
||||
<div
|
||||
v-for="(msg, i) in messages"
|
||||
:key="msg.id ?? i"
|
||||
class="guidance-msg"
|
||||
:class="msg.role"
|
||||
>
|
||||
<div class="msg-role">{{ msg.role === "user" ? "你" : "AI" }}</div>
|
||||
<div class="msg-content" v-html="renderMarkdown(msg.content)"></div>
|
||||
<p class="intro">
|
||||
AI 会看看你的要求哪里还不够清楚,给你提建议。照着建议改好下面的要求,再让它看看;
|
||||
觉得可以了就点“用这段生成网页”。
|
||||
</p>
|
||||
|
||||
<div ref="messagesRef" class="messages">
|
||||
<div v-for="(msg, i) in messages" :key="msg.id ?? i" :class="['msg', msg.role]">
|
||||
<div class="who">
|
||||
{{ msg.role === "user" ? `你的要求 · 第 ${versionOf(i)} 版` : "AI 的建议" }}
|
||||
</div>
|
||||
<div class="content markdown-body" v-html="renderMarkdown(msg.content)"></div>
|
||||
</div>
|
||||
|
||||
<div v-if="streaming" class="guidance-msg assistant">
|
||||
<div class="msg-role">AI</div>
|
||||
<div v-if="!displayStreamingContent" class="typing-indicator">
|
||||
<div v-if="streaming" class="msg assistant">
|
||||
<div class="who">AI 的建议</div>
|
||||
<div v-if="!displayStreamingContent" class="typing">
|
||||
<span></span><span></span><span></span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="msg-content"
|
||||
class="content markdown-body"
|
||||
v-html="renderMarkdown(displayStreamingContent)"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isReady" class="ready-hint">
|
||||
<div v-if="isReady" class="ready">
|
||||
<Icon icon="lucide:check-circle" :width="16" />
|
||||
提示词已优化,可以生成代码了!
|
||||
AI 觉得要求已经写清楚了,可以生成网页了
|
||||
</div>
|
||||
|
||||
<div class="guidance-input">
|
||||
<div class="draft">
|
||||
<div class="draft-label">你的要求</div>
|
||||
<n-input
|
||||
v-model:value="draftPrompt"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 5 }"
|
||||
placeholder="修改你的提示词后发送..."
|
||||
:autosize="{ minRows: 3, maxRows: 6 }"
|
||||
placeholder="按 AI 的建议把要求改得更具体:要什么内容、什么颜色和布局、点了以后会怎样……"
|
||||
:disabled="streaming"
|
||||
@keydown.enter.exact.prevent="send"
|
||||
/>
|
||||
<n-flex justify="space-between" align="center" style="margin-top: 8px">
|
||||
<div>
|
||||
<n-button
|
||||
v-if="streaming"
|
||||
type="error"
|
||||
size="small"
|
||||
@click="stopGuidance"
|
||||
>
|
||||
停止
|
||||
</n-button>
|
||||
<n-flex justify="space-between" align="center" class="draft-actions">
|
||||
<n-flex align="center" :size="8">
|
||||
<n-button v-if="streaming" size="small" @click="stopGuidance">停止</n-button>
|
||||
<n-button
|
||||
v-else
|
||||
size="small"
|
||||
:disabled="!draftPrompt.trim() || !connected"
|
||||
:disabled="!draftPrompt.trim() || !connected || draftPrompt.trim() === lastSent"
|
||||
@click="send"
|
||||
>
|
||||
发送
|
||||
让 AI 再看看
|
||||
</n-button>
|
||||
</div>
|
||||
<span class="muted">Enter 发送,Shift + Enter 换行</span>
|
||||
</n-flex>
|
||||
<n-button
|
||||
:type="isReady ? 'primary' : 'default'"
|
||||
size="small"
|
||||
:disabled="!draftPrompt.trim()"
|
||||
:disabled="!draftPrompt.trim() || streaming"
|
||||
@click="generate"
|
||||
>
|
||||
<template #icon>
|
||||
<Icon icon="lucide:play" :width="14" />
|
||||
</template>
|
||||
生成代码
|
||||
<template #icon><Icon icon="lucide:play" :width="14" /></template>
|
||||
用这段生成网页
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
@@ -106,13 +98,23 @@ const emit = defineEmits<{
|
||||
const draftPrompt = ref("")
|
||||
const messagesRef = ref<HTMLElement>()
|
||||
|
||||
// 第几版要求:只数学生发的
|
||||
function versionOf(index: number) {
|
||||
return messages.value.slice(0, index + 1).filter((m) => m.role === "user").length
|
||||
}
|
||||
|
||||
// 没改过就不用再发一遍
|
||||
const lastSent = computed(
|
||||
() => [...messages.value].reverse().find((m) => m.role === "user")?.content.trim() ?? "",
|
||||
)
|
||||
|
||||
const displayStreamingContent = computed(() =>
|
||||
streamingContent.value.replace(/^\[READY\]\n?/, "")
|
||||
)
|
||||
|
||||
function send() {
|
||||
const text = draftPrompt.value.trim()
|
||||
if (!text || streaming.value) return
|
||||
if (!text || streaming.value || text === lastSent.value) return
|
||||
sendGuidance(text)
|
||||
}
|
||||
|
||||
@@ -141,87 +143,113 @@ watch([() => messages.value.length, streamingContent], () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.guidance-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
.intro {
|
||||
margin: 0 0 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
color: var(--wp-muted);
|
||||
}
|
||||
|
||||
.guidance-messages {
|
||||
min-height: 160px;
|
||||
max-height: 360px;
|
||||
.messages {
|
||||
min-height: 120px;
|
||||
max-height: 45vh;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.guidance-msg .msg-role {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
.who {
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--wp-muted);
|
||||
}
|
||||
|
||||
.guidance-msg.user .msg-role {
|
||||
color: var(--wp-css);
|
||||
}
|
||||
|
||||
.guidance-msg.assistant .msg-role {
|
||||
.msg.assistant .who {
|
||||
color: var(--wp-brand);
|
||||
}
|
||||
|
||||
.guidance-msg.assistant .msg-content {
|
||||
background: var(--wp-css-soft);
|
||||
border-left: 3px solid var(--wp-css);
|
||||
.content {
|
||||
padding: 8px 12px;
|
||||
border-radius: 0 6px 6px 0;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.guidance-msg.user .msg-content {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
line-height: 1.7;
|
||||
color: var(--wp-ink);
|
||||
}
|
||||
|
||||
.msg-content :deep(p) {
|
||||
margin: 0;
|
||||
.msg.user .content {
|
||||
border: 1px solid var(--wp-line);
|
||||
background: var(--wp-paper);
|
||||
}
|
||||
|
||||
.ready-hint {
|
||||
.msg.assistant .content {
|
||||
border-left: 3px solid var(--wp-brand);
|
||||
border-radius: 0 8px 8px 0;
|
||||
background: var(--wp-brand-soft);
|
||||
}
|
||||
|
||||
.content :deep(p) {
|
||||
margin: 0 0 6px;
|
||||
}
|
||||
|
||||
.content :deep(:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ready {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--wp-brand);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-top: 12px;
|
||||
padding: 6px 10px;
|
||||
background: var(--wp-brand-soft);
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--wp-brand-soft);
|
||||
font-size: 13px;
|
||||
color: var(--wp-brand);
|
||||
background: var(--wp-brand-soft);
|
||||
}
|
||||
|
||||
.typing-indicator {
|
||||
.draft {
|
||||
margin-top: 14px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--wp-line);
|
||||
}
|
||||
|
||||
.draft-label {
|
||||
margin-bottom: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.draft-actions {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.muted {
|
||||
font-size: 12px;
|
||||
color: var(--wp-faint);
|
||||
}
|
||||
|
||||
.typing {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
padding: 8px 0;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.typing-indicator span {
|
||||
.typing span {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--wp-css);
|
||||
background: var(--wp-brand);
|
||||
animation: bounce 1.4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.typing-indicator span:nth-child(2) {
|
||||
.typing span:nth-child(2) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.typing-indicator span:nth-child(3) {
|
||||
.typing span:nth-child(3) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user