AI创作弹窗 v3:需求维度块+一体输入条(图标钮·去取消)

按用户 UI 要求收紧弹窗体验:

HTML(data-pages.js confirmAiScript):
- 弹窗加高 width 720→760 + height:min(840px,92vh) flex column
- 需求区改为 req-field 维度块(rf-title 标题在上,rf-val 内容在下),CSS 2 列 grid
- 输入条一体化:req-bar = [reqCreate ✓图标] [req-input-box 圆角胶囊(内嵌 textarea + 右侧 reqSend ↑图标)]
- 取消按钮整段删除(保留右上 × 关闭);提交创作改用 #reqCreate id + svg check 图标
- 附 ICON_PATHS 加 arrow-up/check 路径

CSS(style.css .req-* 段全重写 L209-272):
- .req-modal flex column gap:10px
- .req-sheet grid 2 列 + .req-field 卡片底(empty 浅色)
- .req-input-box 圆角 12 + focus-within halo + 无边框 textarea + 34x34 primary 蓝圆发送钮
- .req-send busy 状态:disabled 灰 + 14x14 白色旋转 spinner(@keyframes req-spin)
- .req-create 44x44 描边 ✓ 图标钮(hover 浅蓝底)

附带红利:弹窗提交按钮改 id 后,与账号列表 [data-create] 同名选择器歧义自然消失(之前 CDP 冒烟需 .req-modal [data-create] 限定,现改 #reqCreate 直接命中)

冒烟 cdp-req-v3-smoke.mjs 24s 一轮真打磨+提交捕获全绿:fields:10/empty:10/modalH:784/sendIsIcon:true/createIsIcon:true/hasCancel:false + 提交{label:创作指令, skills:[短视频脚本创作], hasPersona:true, reqRows:6, modalClosed:true}
This commit is contained in:
maogeigei committed 2026-09-02 16:11:00 +08:00
1 parent 1390268585
commit 305c8f1a18
5 files changed
+79 -35

No files matched your search

@@ -166,6 +166,8 @@ const ICON_PATHS = {
'image': '<rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/>',
'arrow-left': '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
'x': '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
'arrow-up': '<path d="m5 12 7-7 7 7"/><path d="M12 19V5"/>',
'check': '<path d="M20 6 9 17l-5-5"/>',
'check-square': '<polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>',
'trash': '<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/>',
'zap': '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
@@ -218,31 +218,29 @@ async function confirmAiScript(accountName, btn) {
const mask = document.createElement('div'); mask.className = 'modal-mask';
mask.innerHTML = `<div class="modal req-modal">
<div class="modal-title-row"><span>AI创作 · ${esc(accountName)}</span><button class="btn" data-x title="关闭">${svgIcon('x', 14)}</button></div>
<div class="req-head">在下方输入想法,AI 会边聊边把需求整理到「创作需求」框里;聊够了点「提交创作」。</div>
<div class="modal-title-row"><span>AI创作 · ${esc(accountName)}</span><button class="btn btn-icon" data-x title="关闭">${svgIcon('x', 16)}</button></div>
<div class="req-head">在下方输入想法,AI 会边聊边把需求整理到上方「创作需求」里;打磨满意后,点输入框左侧的 ✓ 提交创作。</div>
<div class="req-sheet" id="reqSheet"></div>
<div class="req-dialog" id="reqDialog"></div>
<div class="req-input-row">
<textarea id="reqInput" class="req-input" rows="2" placeholder="说说你的想法,例如:想拍一个亲子互动的搞笑短剧…&#10;(Enter 发送,Shift+Enter 换行)"></textarea>
<button type="button" class="primary req-send" id="reqSend">发送</button>
</div>
<div class="modal-actions">
<button class="ghost" data-cancel>取消</button>
<button class="primary" data-create>提交创作</button>
<div class="req-bar">
<button type="button" class="req-create" id="reqCreate" title="提交创作(生成完整脚本)">${svgIcon('check', 20)}</button>
<div class="req-input-box">
<textarea id="reqInput" class="req-input" rows="1" placeholder="说说你的想法,例如:想拍一个亲子互动的搞笑短剧…&#10;(Enter 发送,Shift+Enter 换行)"></textarea>
<button type="button" class="req-send" id="reqSend" title="发送">${svgIcon('arrow-up', 18)}</button>
</div>
</div>
</div>`;
document.body.appendChild(mask);
const close = () => mask.remove();
mask.addEventListener('click', (e) => { if (e.target === mask) close(); });
mask.querySelector('[data-x]').addEventListener('click', close);
mask.querySelector('[data-cancel]').addEventListener('click', close);
const q = (s) => mask.querySelector(s);
/* —— 需求框渲染:10 行字段,空值灰显 —— */
/* —— 需求框渲染:10 个维度块,维度名作标题、内容在下方 —— */
const renderSheet = () => {
const rows = REQ_FIELDS.map(([k, label]) => {
const v = String(w[k] || '').trim();
return `<div class="req-sheet-row${v ? '' : ' empty'}"><span class="rs-label">${label}</span><span class="rs-val">${v ? esc(v) : '(待定 · 由 AI 按人设自决)'}</span></div>`;
return `<div class="req-field${v ? '' : ' empty'}"><div class="rf-title">${label}</div><div class="rf-val">${v ? esc(v) : '(待定 · 由 AI 按人设自决)'}</div></div>`;
}).join('');
q('#reqSheet').innerHTML = rows;
};
@@ -256,10 +254,10 @@ async function confirmAiScript(accountName, btn) {
: `<div class="req-dialog-empty">在这里输入一个初步想法,AI 会引导你补齐形态 / 时长 / 情绪 / 受众 / 情节 / 结尾 / 植入等需求;每条输入都会即时更新上方的创作需求框。</div>`;
el.scrollTop = el.scrollHeight;
};
const setBusy = (b, txt) => {
const setBusy = (b) => {
busy = b;
const s = q('#reqSend');
if (s) { s.disabled = b; s.textContent = b ? (txt || '整理中…') : '发送'; }
if (s) { s.disabled = b; s.title = b ? 'AI 整理中…' : '发送'; s.innerHTML = b ? '<span class="req-send-busy"></span>' : svgIcon('arrow-up', 18); }
};
/* —— 发送一轮:调 /api/ai/clarify(同步打磨,返回 reply + ==REQ== 段) —— */
const sendTurn = async (raw) => {
@@ -312,7 +310,7 @@ async function confirmAiScript(accountName, btn) {
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendTurn(inputEl.value); }
});
q('#reqSend').addEventListener('click', () => sendTurn(inputEl.value));
mask.querySelector('[data-create]').addEventListener('click', async () => {
q('#reqCreate').addEventListener('click', async () => {
if (busy) return;
await loadPersona();
const prompt = buildCreatePrompt(accountName, w, personaBlock(), pHas);
@@ -206,23 +206,28 @@ main { height: calc(100vh - 54px); }
color: var(--dim); font-size: 13px; word-break: break-all;
padding: 0 2px 4px; margin-bottom: 6px;
}
/* AI创作 弹窗(09-02 v2):需求框 + 对话流 + 输入框 对话式打磨 */
.req-modal { width: 720px; max-width: 96vw; }
.req-head { color: var(--dim); font-size: 13px; margin-bottom: 10px; line-height: 1.6; }
/* AI创作 弹窗(09-02 v3):需求维度块 + 对话流 + 一体输入条(发送图标内嵌,参考 WorkBuddy 输入框) */
.req-modal {
width: 760px; max-width: 96vw; height: min(840px, 92vh); box-sizing: border-box;
display: flex; flex-direction: column; gap: 10px;
}
.req-modal .modal-title-row { flex: none; margin-bottom: 0; }
.req-head { flex: none; color: var(--dim); font-size: 13px; line-height: 1.6; }
/* 需求区:10 个维度块(维度名=标题在上,内容在下),双列网格,超高内部滚动 */
.req-sheet {
border: 1px solid var(--border); border-radius: 8px; background: #fbfcfe;
max-height: 218px; overflow: auto; padding: 2px 12px;
flex: none; display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px;
max-height: 290px; overflow: auto; align-content: start; padding: 2px;
}
.req-sheet-row {
display: flex; gap: 10px; padding: 6px 0; border-bottom: 1px dashed #eceff3;
font-size: 14px; line-height: 1.55;
.req-field {
background: #f6f8fb; border: 1px solid #edf0f4; border-radius: 10px;
padding: 7px 11px 8px; min-height: 42px;
}
.req-sheet-row:last-child { border-bottom: none; }
.req-sheet-row.empty .rs-val { color: #c6ccd6; }
.rs-label { flex: none; width: 108px; color: var(--dim); font-size: 13px; padding-top: 1px; }
.rs-val { flex: 1; word-break: break-word; }
.req-field.empty { background: #fafbfd; }
.rf-title { font-size: 12px; color: #7a8494; font-weight: 600; letter-spacing: .2px; margin-bottom: 2px; }
.rf-val { font-size: 14px; color: var(--text); line-height: 1.5; word-break: break-word; white-space: pre-wrap; }
.req-field.empty .rf-val { color: #c6ccd6; font-size: 13px; }
.req-dialog {
margin-top: 10px; max-height: 172px; overflow: auto;
flex: 1 1 auto; min-height: 130px; overflow: auto;
display: flex; flex-direction: column; gap: 6px; padding: 2px;
}
.req-dialog-empty { color: var(--dim); font-size: 13px; line-height: 1.7; padding: 2px; }
@@ -235,14 +240,39 @@ main { height: calc(100vh - 54px); }
}
.req-msg.mine .req-bubble { background: var(--primary); color: #fff; border-bottom-right-radius: 3px; }
.req-msg.ai .req-bubble { background: #f0f2f5; color: var(--text); border-bottom-left-radius: 3px; }
.req-input-row { display: flex; gap: 8px; margin-top: 10px; align-items: flex-end; }
.req-input {
flex: 1; border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px;
font-size: 14px; font-family: inherit; line-height: 1.5; box-sizing: border-box;
resize: vertical; min-height: 44px; max-height: 132px;
/* 底部功能条:提交创作图标(左)+ 一体输入条(圆角块内嵌 textarea + 右侧发送圆钮) */
.req-bar { flex: none; display: flex; align-items: flex-end; gap: 10px; }
.req-create {
flex: none; width: 44px; height: 44px; border-radius: 12px;
border: 1px solid var(--border); background: #fff; color: var(--primary);
display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.req-input:focus { outline: none; border-color: var(--primary); }
.req-send { min-width: 68px; }
.req-create:hover { border-color: var(--primary); background: var(--primary-soft); }
.req-input-box {
flex: 1; display: flex; align-items: flex-end; gap: 6px;
border: 1px solid var(--border); border-radius: 12px; padding: 5px 5px 5px 13px;
background: #fff; transition: border-color .15s, box-shadow .15s;
}
.req-input-box:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.req-input {
flex: 1; border: none; outline: none; resize: none; background: transparent;
font-size: 14px; font-family: inherit; line-height: 1.5;
min-height: 30px; max-height: 100px; padding: 6px 0 5px;
}
.req-input::placeholder { color: #a6aebc; }
.req-send {
flex: none; width: 34px; height: 34px; border-radius: 9px; border: none;
background: var(--primary); color: #fff; cursor: pointer;
display: flex; align-items: center; justify-content: center;
}
.req-send:hover:not(:disabled) { background: #2a5fd0; }
.req-send:disabled { background: #c7cedb; cursor: default; }
.req-send-busy {
width: 14px; height: 14px; border-radius: 50%;
border: 2px solid rgba(255,255,255,.45); border-top-color: #fff;
display: inline-block; animation: req-spin .8s linear infinite;
}
@keyframes req-spin { to { transform: rotate(360deg); } }
.root-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.root-item {
display: flex; align-items: center; gap: 8px;