diff --git a/docs/plans/2026-03-07-script-view-optimization.md b/docs/plans/2026-03-07-script-view-optimization.md
new file mode 100644
index 0000000..71efa6c
--- /dev/null
+++ b/docs/plans/2026-03-07-script-view-optimization.md
@@ -0,0 +1,185 @@
+# 剧本生成器页面优化设计
+
+## 概述
+
+优化小程序剧本生成器页面(ScriptView.vue)的布局和样式,解决以下问题:
+1. 输入框宽度过大导致超出屏幕
+2. 叙事风格和篇幅参数排列过于紧凑(原型为上下两行,实现为并排)
+3. NPC 表单缺少外层容器和 padding
+4. 字体大小、间距与原型不一致
+5. 整体视觉不够精致
+
+## 问题清单
+
+### 1. 输入框溢出
+- **问题**:`.glass-input` 和 `.glass-picker` 设置 `width: 100%` 但未正确处理 box-sizing
+- **影响**:在小屏幕设备上输入框可能超出容器
+
+### 2. 参数行布局错误
+- **当前实现**:`.params-row` 使用 `grid-template-columns: 1fr 1fr` 并排显示
+- **原型设计**:使用两个独立的 `space-y-2` 垂直堆叠区域
+- **差异**:原型中"叙事风格"和"故事篇幅"是上下两个独立区域,每个区域内标签和选项垂直排列
+
+### 3. NPC 表单缺少容器
+- **当前实现**:`.npc-form` 直接裸露,无边框背景
+- **原型设计**:使用带 `bg-white/5 border border-white/10 rounded-2xl p-4` 的容器包裹
+- **影响**:视觉层次感缺失,与原型不一致
+
+### 4. 字体尺寸过大
+| 元素 | 当前 | 原型 | 建议 |
+|------|------|------|------|
+| section-title | 22rpx | ~10px (约 18rpx) | 18rpx |
+| label | 18rpx | ~10px (约 18rpx) | 16rpx |
+| input text | 26rpx | ~11px (约 20rpx) | 22rpx |
+| param-label | 18rpx | ~9px (约 16rpx) | 16rpx |
+| param-option | 22rpx | ~10px (约 18rpx) | 18rpx |
+
+### 5. 圆角不一致
+| 元素 | 当前 | 原型 | 建议 |
+|------|------|------|------|
+| section-card | 默认 | rounded-3xl (约 48rpx) | 48rpx |
+| 主容器 | 默认 | rounded-[2rem] (约 64rpx) | 64rpx |
+| 输入框 | 16rpx | rounded-xl (约 24rpx) | 24rpx |
+| 参数选项 | 16rpx | rounded-xl | 24rpx |
+
+## 设计方案
+
+### 1. 布局结构调整
+
+#### params-row 重构
+```css
+/* 原方案:并排 grid */
+.params-row {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+}
+
+/* 新方案:垂直堆叠 */
+.params-section {
+ margin-bottom: 24rpx;
+}
+
+.param-row {
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+}
+```
+
+#### NPC 表单添加容器
+```html
+
+
+
+
+
+
+```
+
+### 2. 尺寸优化
+
+| 属性 | 原值 | 新值 |
+|------|------|------|
+| `.section-card` padding | 32rpx | 40rpx |
+| `.section-card` border-radius | 默认 | 48rpx |
+| 主容器 border-radius | 默认 | 64rpx |
+| `.glass-input` height | 80rpx | 72rpx |
+| `.glass-input` font-size | 26rpx | 22rpx |
+| `.glass-input` padding | 0 24rpx | 0 20rpx |
+| `.label` font-size | 18rpx | 16rpx |
+| `.param-label` font-size | 18rpx | 16rpx |
+| `.param-option` font-size | 22rpx | 18rpx |
+| `.param-option` padding | 10rpx 20rpx | 8rpx 16rpx |
+
+### 3. 间距优化
+
+| 元素 | 原间距 | 新间距 |
+|------|--------|--------|
+| `.section-card` gap | 32rpx | 40rpx |
+| `.input-group` margin-bottom | 24rpx | 32rpx |
+| `.label` margin-bottom | 16rpx | 12rpx |
+| `.param-option` gap | 12rpx | 8rpx |
+| NPC 容器内间距 | 无 | 16rpx |
+
+### 4. 视觉优化
+
+- 添加 `box-sizing: border-box` 到所有输入元素
+- NPC 容器添加玻璃态背景边框
+- 参数选项使用更紧凑的排列
+- 调整生成按钮高度:96rpx → 88rpx
+
+## 修改文件
+
+- `mini-program/src/pages/main/ScriptView.vue`
+
+## 关键代码变更
+
+### 1. params-row 结构变更
+```html
+
+
+ ...
+ ...
+
+
+
+
+
+ 叙事风格
+ ...
+
+
+
+
+ 故事篇幅
+ ...
+
+
+```
+
+### 2. NPC 容器添加
+```html
+
+
+
+
+
+
+```
+
+### 3. CSS 关键修改
+```css
+/* 所有输入元素添加 */
+*, *::before, *::after {
+ box-sizing: border-box;
+}
+
+/* NPC 容器样式 */
+.npc-container {
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 32rpx;
+ padding: 32rpx;
+ margin-bottom: 16rpx;
+}
+
+/* 参数行垂直排列 */
+.params-section {
+ margin-bottom: 24rpx;
+}
+
+.param-row {
+ display: flex;
+ flex-direction: column;
+ gap: 12rpx;
+}
+```
+
+## 验收标准
+
+- [ ] 所有输入框在小屏幕(320px 宽度)下不溢出
+- [ ] 叙事风格和篇幅参数垂直排列,各有独立标签
+- [ ] NPC 表单有明显的容器边框和背景
+- [ ] 字体大小适配移动端,无明显过大
+- [ ] 圆角与原型一致,视觉精致
+- [ ] 整体布局紧凑,层次分明
diff --git a/mini-program/src/pages/main/ScriptView.vue b/mini-program/src/pages/main/ScriptView.vue
index 3e86a65..fc3003c 100644
--- a/mini-program/src/pages/main/ScriptView.vue
+++ b/mini-program/src/pages/main/ScriptView.vue
@@ -1,13 +1,13 @@
剧本生成器
-
+
-
+
-
-
+
+
剧本主题
-
+
-
-
-
+
+
+
+ {{ npcConfig.role || '角色' }}
+
+
+ {{ npcConfig.relation || '关系' }}
+
+
+
+
-
- {{ npcConfig.role || '角色' }}
-
-
- {{ npcConfig.relation || '关系' }}
-
-
-
-
+
-
-
-
+
+
+
叙事风格
-
-
+
+
+
+
故事篇幅
-
+
-
+
{{ script.title }}
{{ script.theme || '追光者' }}
-
+
{{ getScriptSummary(script) }}
-
+
-
+
🎬
尚未生成剧本,定义你的未来篇章
-
+
正在采集星海中的深紫色碎屑...
@@ -233,9 +237,9 @@ const removeNpc = (index) => {
const generateScript = async () => {
if (!scriptConfig.theme || isGenerating.value) return
-
+
isGenerating.value = true
-
+
try {
await store.createScript({
theme: scriptConfig.theme,
@@ -271,39 +275,44 @@ onMounted(async () => {
.script-view {
display: flex;
flex-direction: column;
- gap: 32rpx;
+ gap: 24rpx;
min-height: 100%;
}
.page-title {
- font-size: 36rpx;
- font-weight: 400;
+ font-size: 32rpx;
+ font-weight: 300;
color: rgba(255, 255, 255, 0.9);
- margin-bottom: 8rpx;
letter-spacing: 4rpx;
}
.section-card {
+ padding: 24rpx;
+ border-radius: 32rpx;
+}
+
+.glass-card-main {
padding: 32rpx;
+ border-radius: 48rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
- margin-bottom: 24rpx;
+ margin-bottom: 20rpx;
}
.section-title {
- font-size: 22rpx;
+ font-size: 18rpx;
color: rgba(192, 132, 252, 0.6);
font-weight: 600;
- letter-spacing: 4rpx;
+ letter-spacing: 3rpx;
text-transform: uppercase;
}
.section-hint {
- font-size: 16rpx;
+ font-size: 14rpx;
color: rgba(255, 255, 255, 0.35);
font-style: italic;
}
@@ -311,59 +320,73 @@ onMounted(async () => {
.profile-grid {
display: grid;
grid-template-columns: 1fr 1fr;
- gap: 16rpx;
+ gap: 12rpx;
}
.glass-input, .glass-picker {
width: 100%;
- height: 80rpx;
+ height: 72rpx;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 16rpx;
- padding: 0 24rpx;
+ border-radius: 24rpx;
+ padding: 0 20rpx;
color: rgba(255, 255, 255, 0.9);
- font-size: 26rpx;
+ font-size: 22rpx;
+ box-sizing: border-box;
}
.glass-input::placeholder {
color: rgba(255, 255, 255, 0.3);
}
+.theme-input {
+ height: 80rpx;
+ font-size: 24rpx;
+}
+
.picker-value {
- line-height: 80rpx;
+ line-height: 72rpx;
color: rgba(255, 255, 255, 0.9);
}
.input-group {
- margin-bottom: 24rpx;
+ margin-bottom: 28rpx;
}
.label {
display: block;
- font-size: 18rpx;
+ font-size: 16rpx;
color: rgba(255, 255, 255, 0.35);
font-weight: 600;
- letter-spacing: 4rpx;
+ letter-spacing: 3rpx;
text-transform: uppercase;
- margin-bottom: 16rpx;
+ margin-bottom: 12rpx;
}
.npc-header {
display: flex;
justify-content: space-between;
align-items: center;
- margin-bottom: 16rpx;
+ margin-bottom: 12rpx;
}
.add-btn {
- font-size: 20rpx;
+ font-size: 18rpx;
color: #C084FC;
border: 1px solid rgba(192, 132, 252, 0.3);
- padding: 8rpx 16rpx;
+ padding: 6rpx 12rpx;
border-radius: 12rpx;
background: transparent;
}
+.npc-container {
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 32rpx;
+ padding: 24rpx;
+ margin-bottom: 16rpx;
+}
+
.npc-form {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
@@ -372,26 +395,26 @@ onMounted(async () => {
}
.npc-input, .npc-picker {
- height: 72rpx;
+ height: 64rpx;
padding: 0 16rpx;
- font-size: 24rpx;
+ font-size: 20rpx;
}
.npc-picker .picker-value {
- line-height: 72rpx;
- font-size: 24rpx;
+ line-height: 64rpx;
+ font-size: 20rpx;
}
.glass-textarea {
width: 100%;
- height: 120rpx;
+ height: 100rpx;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 16rpx;
- padding: 20rpx;
+ border-radius: 24rpx;
+ padding: 16rpx;
color: rgba(255, 255, 255, 0.9);
- font-size: 24rpx;
- margin-bottom: 16rpx;
+ font-size: 20rpx;
+ box-sizing: border-box;
}
.npc-list {
@@ -414,41 +437,38 @@ onMounted(async () => {
.delete-btn {
color: rgba(255, 255, 255, 0.4);
- font-size: 28rpx;
+ font-size: 24rpx;
padding: 0 4rpx;
}
-.params-row {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 24rpx;
- margin-bottom: 32rpx;
+.params-section {
+ margin-bottom: 24rpx;
}
-.param-group {
+.param-row {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.param-label {
- font-size: 18rpx;
+ font-size: 16rpx;
color: rgba(255, 255, 255, 0.35);
- margin-left: 8rpx;
+ margin-left: 4rpx;
}
.param-options {
display: flex;
flex-wrap: wrap;
- gap: 12rpx;
+ gap: 8rpx;
}
.param-option {
- padding: 10rpx 20rpx;
+ padding: 8rpx 16rpx;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 16rpx;
- font-size: 22rpx;
+ border-radius: 20rpx;
+ font-size: 18rpx;
color: rgba(255, 255, 255, 0.5);
}
@@ -460,19 +480,21 @@ onMounted(async () => {
.generate-btn {
width: 100%;
- height: 96rpx;
- box-shadow: 0 8rpx 40rpx rgba(168, 85, 247, 0.3);
+ height: 88rpx;
+ border-radius: 32rpx;
+ box-shadow: 0 8rpx 32rpx rgba(168, 85, 247, 0.3);
}
.scripts-list {
display: flex;
flex-direction: column;
- gap: 24rpx;
+ gap: 20rpx;
}
.script-card {
- padding: 32rpx;
+ padding: 24rpx;
border-left: 4rpx solid transparent;
+ border-radius: 32rpx;
}
.script-card.selected {
@@ -483,30 +505,30 @@ onMounted(async () => {
display: flex;
justify-content: space-between;
align-items: center;
- margin-bottom: 16rpx;
+ margin-bottom: 12rpx;
}
.script-title {
- font-size: 32rpx;
+ font-size: 28rpx;
font-weight: 500;
color: rgba(255, 255, 255, 0.9);
}
.script-persona {
- font-size: 18rpx;
+ font-size: 16rpx;
color: rgba(168, 85, 247, 0.6);
background: rgba(168, 85, 247, 0.1);
- padding: 6rpx 16rpx;
+ padding: 6rpx 12rpx;
border-radius: 12rpx;
border: 1px solid rgba(168, 85, 247, 0.2);
}
.script-summary {
display: block;
- font-size: 24rpx;
+ font-size: 22rpx;
color: rgba(255, 255, 255, 0.5);
line-height: 1.6;
- margin-bottom: 24rpx;
+ margin-bottom: 16rpx;
}
.script-footer {
@@ -516,48 +538,52 @@ onMounted(async () => {
}
.script-style {
- font-size: 20rpx;
+ font-size: 18rpx;
color: #C084FC;
}
.select-btn {
- font-size: 24rpx;
+ font-size: 20rpx;
color: #C084FC;
font-weight: 600;
background: transparent;
+ border: none;
+ padding: 0;
}
.empty-state {
- padding: 80rpx 48rpx;
+ padding: 64rpx 40rpx;
display: flex;
flex-direction: column;
align-items: center;
- gap: 24rpx;
+ gap: 20rpx;
opacity: 0.5;
+ border-radius: 48rpx;
}
.empty-icon {
- font-size: 64rpx;
+ font-size: 56rpx;
}
.empty-text {
- font-size: 24rpx;
+ font-size: 22rpx;
color: rgba(255, 255, 255, 0.5);
text-align: center;
}
.generating-state {
- padding: 80rpx 48rpx;
+ padding: 64rpx 40rpx;
display: flex;
flex-direction: column;
align-items: center;
- gap: 32rpx;
+ gap: 24rpx;
+ border-radius: 48rpx;
}
.spinner {
- width: 64rpx;
- height: 64rpx;
- border: 4rpx solid #A855F7;
+ width: 56rpx;
+ height: 56rpx;
+ border: 3rpx solid #A855F7;
border-top-color: transparent;
border-radius: 50%;
animation: spin 0.8s linear infinite;
@@ -569,7 +595,7 @@ onMounted(async () => {
}
.generating-text {
- font-size: 26rpx;
+ font-size: 24rpx;
color: rgba(192, 132, 252, 0.6);
font-style: italic;
letter-spacing: 2rpx;