Files
happy-life-star/docs/0517-UI设计 更新.md
peanut ee5a6aba5d feat: 小程序脚本首页重构 + 社交数据导入 + TTS 播放优化
- 后端:新增社交数据导入/审批/洞察生成 API(SocialContent/SocialInsight)
- 后端:优化脚本上下文服务,TTS 服务增强
- 小程序:重构脚本首页布局,新增社交导入页面
- 小程序:新增 useTtsPlayer composable,移除旧 ScriptAudioPlayer 组件
- 小程序:新增社交导入服务,优化请求服务
- SQL:新增社交数据导入建表脚本
- 文档:补充设计文档和实施计划

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 07:18:02 +08:00

37 KiB
Raw Permalink Blame History

0517-UI设计 更新

0517新需求:

爽⽂⽣成界⾯与⼈⽣轨迹界⾯顺序进⾏调换,⼈⽣轨迹不变,爽⽂⽣成为⾸⻚,新的交互设计如下:

⾸⻚

图⽚

今天有什么心愿想实现,想实现

按住说话,即刻如愿

....

vue代码

代码块
     <template>
       <main class="page-shell">
         <section class="phone-screen">
           <div class="space-bg"></div>
           <div class="planet planet-left"></div>
           <div class="planet planet-right"></div>
           <div class="star-field">
             <span
               v-for="star in stars"
               :key="star.id"
               class="star"
               :style="{
                 left: star.left + '%',
                 top: star.top + '%',
                 width: star.size + 'px',
                 height: star.size + 'px',
                 opacity: star.opacity
               }"
             />
           </div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<div class="content">
             <header class="status-bar">
               <span>9:41</span>
               <div class="status-icons">
                 <span class="signal">▂▃▅</span>
                 <span class="wifi">⌁</span>
                 <span class="battery">▰</span>
               </div>
             </header>
             <button class="history-button" type="button">
               <Menu :size="25" :stroke-width="2.2" />
               <span>历史</span>
             </button>
             <section class="hero">
               <h1>
                 今天有什么<span>⼼愿</span><br />
                 <em>想实现</em>
               </h1>
             </section>
             <section class="voice-section">
               <div class="orbit orbit-1"></div>
               <div class="orbit orbit-2"></div>
               <div class="orbit orbit-3"></div>
               <button class="mic-button" type="button" aria-label="按住说话">
                 <Mic :size="76" :stroke-width="2.4" />
               </button>
               <p class="voice-tip">按住说话,即刻如愿</p>
               <div class="wave">
                 <span v-for="i in 5" :key="i"></span>
               </div>
             </section>
             <section class="text-input-wrap">
               <Pencil :size="25" :stroke-width="2.2" />
               <input placeholder="说说你的⼼愿..." />
               <Keyboard :size="25" :stroke-width="2" />
             </section>
             <section class="inspiration">
               <div class="inspiration-head">
                 <h2>灵感⼀下 ✨</h2>
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<button type="button">
                    <RefreshCw :size="18" />
                    换⼀换
                  </button>
                </div>
                <div class="prompt-grid">
                  <button
                    v-for="prompt in prompts"
                    :key="prompt"
                    type="button"
                    class="prompt-chip"
                  >
                    <SparkleIcon />
                    <span>{{ prompt }}</span>
                    <ChevronRight :size="20" />
                  </button>
                </div>
              </section>
            </div>
          </section>
        </main>
     </template>
     <script setup>
     import { Menu, Mic, Pencil, Keyboard, RefreshCw, ChevronRight } from 'lucide-
     vue-next'
     const prompts = [
        '如果⽼板突然夸我...',
        '如果今天中彩票...',
        '如果我勇敢⼀点...',
        '如果他突然联系我...',
        '如果今天是超幸运的⼀天...',
        '如果我重回18岁...',
     ]
     const stars = Array.from({ length: 90 }, (_, index) => ({
        id: index,
        left: (index * 37 + 11) % 100,
        top: (index * 53 + 7) % 100,
        size: index % 9 === 0 ? 3 : index % 4 === 0 ? 2 : 1,
        opacity: 0.2 + (((index * 13) % 60) / 100),
     }))
     </script>
     <script>
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
const SparkleIcon = {
        name: 'SparkleIcon',
        template: `
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-
      hidden="true">
            <path d="M12 2L14.2 8.2L20 10.5L14.2 12.8L12 19L9.8 12.8L4 10.5L9.8
      8.2L12 2Z" fill="#C084FC"/>
          </svg>
        `,
      }
      export default {
        components: { SparkleIcon },
      }
      </script>
      <style scoped>
      * {
        box-sizing: border-box;
      }
      .page-shell {
        min-height: 100vh;
        width: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        background: #05010e;
        color: #fff;
        font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica
      Neue', Arial, sans-serif;
      }
      .phone-screen {
        position: relative;
        width: 430px;
        height: 932px;
        overflow: hidden;
        background: #080219;
      }
      .space-bg {
        position: absolute;
        inset: 0;
        background:
          radial-gradient(circle at 52% 43%, rgba(116, 41, 210, 0.42), transparent
      18%),
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
radial-gradient(circle at 50% 48%, rgba(92, 24, 181, 0.22), transparent
      32%),
          radial-gradient(circle at 42% 12%, rgba(84, 39, 166, 0.28), transparent
      28%),
          radial-gradient(circle at 78% 42%, rgba(123, 44, 201, 0.22), transparent
      24%),
          linear-gradient(180deg, #10042d 0%, #08021c 45%, #070116 100%);
      }
      .space-bg::before {
        content: '';
        position: absolute;
        inset: 245px -80px 150px;
        background:
          radial-gradient(ellipse at center, rgba(194, 100, 255, 0.34), transparent
      30%),
          conic-gradient(from 15deg, transparent, rgba(145, 75, 255, 0.42),
      transparent, rgba(91, 26, 194, 0.36), transparent);
        filter: blur(1px);
        opacity: 0.8;
        transform: rotate(-8deg);
      }
      .space-bg::after {
        content: '';
        position: absolute;
        inset: 0;
        background: radial-gradient(circle at 50% 43%, transparent 0 22%, rgba(6, 1,
      18, 0.18) 38%, rgba(3, 0, 12, 0.75) 100%);
      }
      .star-field {
        position: absolute;
        inset: 0;
        pointer-events: none;
      }
      .star {
        position: absolute;
        display: block;
        border-radius: 50%;
        background: rgba(255, 255, 255, 0.92);
        box-shadow: 0 0 8px rgba(198, 160, 255, 0.95);
      }
      .planet {
        position: absolute;
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
border-radius: 50%;
        pointer-events: none;
      }
      .planet-left {
        left: 14px;
        top: 239px;
        width: 62px;
        height: 62px;
        background: radial-gradient(circle at 35% 30%, #8c48ff, #48139a 60%,
      #210759);
        box-shadow: 0 0 30px rgba(124, 58, 237, 0.45);
      }
      .planet-left::after {
        content: '';
        position: absolute;
        left: -18px;
        top: 27px;
        width: 98px;
        height: 18px;
        border: 5px solid rgba(147, 51, 234, 0.5);
        border-radius: 50%;
        transform: rotate(-17deg);
      }
      .planet-right {
        right: -48px;
        top: 174px;
        width: 128px;
        height: 128px;
        background: radial-gradient(circle at 35% 30%, #7651d8, #351178 60%,
      #110633);
        box-shadow: inset -18px -12px 28px rgba(5, 1, 18, 0.6), 0 0 55px rgba(124,
      58, 237, 0.45);
        opacity: 0.8;
      }
      .content {
        position: relative;
        z-index: 2;
        height: 100%;
        padding: 16px 26px 30px;
      }
      .status-bar {
        height: 32px;
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
display: flex;
        align-items: center;
        justify-content: space-between;
        font-size: 18px;
        font-weight: 700;
        letter-spacing: 0.02em;
      }
      .status-icons {
        display: flex;
        align-items: center;
        gap: 5px;
        font-size: 13px;
      }
      .battery {
        border: 2px solid rgba(255, 255, 255, 0.9);
        border-radius: 5px;
        padding: 0 4px;
        font-size: 10px;
      }
      .history-button {
        position: absolute;
        top: 90px;
        left: 22px;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 5px;
        border: 0;
        background: transparent;
        color: rgba(255, 255, 255, 0.88);
        font-size: 14px;
      }
      .history-button::after {
        content: '';
        position: absolute;
        right: 1px;
        top: 0;
        width: 5px;
        height: 5px;
        border-radius: 50%;
        background: #9b5cff;
        box-shadow: 0 0 10px #9b5cff;
      }
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
.hero {
        margin-top: 185px;
        text-align: center;
      }
      .hero h1 {
        margin: 0;
        color: #fff;
        font-size: 38px;
        line-height: 1.35;
        font-weight: 800;
        letter-spacing: 0.01em;
        text-shadow: 0 0 20px rgba(148, 71, 255, 0.18);
      }
      .hero h1 span {
        color: #d18aff;
        text-shadow: 0 0 18px rgba(209, 138, 255, 0.45);
      }
      .hero h1 em {
        position: relative;
        font-style: normal;
      }
      .hero h1 em::before {
        content: '✦';
        position: absolute;
        left: -28px;
        top: 8px;
        color: #ffd86b;
        font-size: 17px;
        text-shadow: 0 0 12px rgba(255, 216, 107, 0.8);
      }
      .hero h1 em::after {
        content: '✦';
        position: absolute;
        right: -38px;
        top: 20px;
        color: #c084fc;
        font-size: 15px;
        text-shadow: 0 0 12px rgba(192, 132, 252, 0.8);
      }
      .voice-section {
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
position: relative;
        margin: 77px auto 0;
        display: flex;
        flex-direction: column;
        align-items: center;
      }
      .orbit {
        position: absolute;
        border-radius: 50%;
        border: 1px solid rgba(192, 132, 252, 0.15);
        pointer-events: none;
      }
      .orbit-1 {
        top: -28px;
        width: 232px;
        height: 232px;
      }
      .orbit-2 {
        top: -43px;
        width: 262px;
        height: 262px;
      }
      .orbit-3 {
        top: -61px;
        width: 296px;
        height: 296px;
      }
      .mic-button {
        position: relative;
        z-index: 2;
        width: 178px;
        height: 178px;
        border: 0;
        border-radius: 50%;
        color: white;
        display: grid;
        place-items: center;
        background:
          radial-gradient(circle at 32% 25%, rgba(255, 203, 245, 0.95), transparent
      23%),
          linear-gradient(145deg, #f1a0ff 0%, #934dff 48%, #4d1ccb 100%);
        box-shadow:
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
inset 0 1px 18px rgba(255, 255, 255, 0.25),
          0 0 0 1px rgba(255, 255, 255, 0.22),
          0 0 36px rgba(169, 85, 247, 0.75),
          0 0 90px rgba(102, 41, 201, 0.55);
      }
      .voice-tip {
        margin: 32px 0 0;
        color: #e8ccff;
        font-size: 18px;
        font-weight: 500;
        text-shadow: 0 0 14px rgba(168, 85, 247, 0.42);
      }
      .wave {
        margin-top: 13px;
        display: flex;
        align-items: center;
        gap: 5px;
        height: 16px;
      }
      .wave span {
        width: 4px;
        border-radius: 99px;
        background: #8d55ff;
        box-shadow: 0 0 10px rgba(141, 85, 255, 0.8);
      }
      .wave span:nth-child(1),
      .wave span:nth-child(5) {
        height: 4px;
        opacity: 0.7;
      }
      .wave span:nth-child(2),
      .wave span:nth-child(4) {
        height: 9px;
        opacity: 0.9;
      }
      .wave span:nth-child(3) {
        height: 15px;
      }
      .text-input-wrap {
        margin-top: 25px;
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
height: 54px;
        display: flex;
        align-items: center;
        gap: 13px;
        padding: 0 17px;
        border-radius: 26px;
        border: 1px solid rgba(168, 85, 247, 0.42);
        background: linear-gradient(90deg, rgba(43, 19, 83, 0.72), rgba(32, 14, 61,
      0.66));
        box-shadow: inset 0 0 26px rgba(162, 89, 255, 0.08), 0 0 22px rgba(116, 52,
      202, 0.12);
        color: #c084fc;
      }
      .text-input-wrap input {
        min-width: 0;
        flex: 1;
        border: 0;
        outline: none;
        background: transparent;
        color: #f5eaff;
        font-size: 17px;
      }
      .text-input-wrap input::placeholder {
        color: rgba(216, 180, 254, 0.48);
      }
      .inspiration {
        margin-top: 38px;
      }
      .inspiration-head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 17px;
      }
      .inspiration-head h2 {
        margin: 0;
        font-size: 22px;
        font-weight: 700;
      }
      .inspiration-head button {
        display: flex;
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
align-items: center;
        gap: 5px;
        border: 0;
        background: transparent;
        color: rgba(216, 180, 254, 0.85);
        font-size: 15px;
      }
      .prompt-grid {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 13px 12px;
      }
      .prompt-chip {
        min-width: 0;
        height: 50px;
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 0 13px;
        border-radius: 18px;
        border: 1px solid rgba(168, 85, 247, 0.22);
        background: linear-gradient(180deg, rgba(48, 24, 89, 0.78), rgba(32, 14, 62,
      0.76));
        box-shadow: inset 0 0 18px rgba(180, 96, 255, 0.05), 0 0 16px rgba(101, 42,
      190, 0.08);
        color: #f3e8ff;
        font-size: 14px;
        text-align: left;
      }
      .prompt-chip span {
        flex: 1;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      @media (max-width: 480px) {
        .phone-screen {
          width: 100vw;
          height: 100vh;
        }
        .content {
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
padding-left: 24px;
          padding-right: 24px;
        }
      }
      </style>
519
520
521
522
523

⾊卡

背景

主背景(夜空⿊紫)

代码块
   #080219
1

深层背景

代码块
   #05010E
1

顶部紫云

代码块
   rgba(84,39,166,0.28)
1

中⼼星云辉光

代码块
   rgba(116,41,210,0.42)
1

中⼼扩散光

代码块
   rgba(92,24,181,0.22)
1

辅助星云

代码块
   rgba(123,44,201,0.22)
1

主品牌⾊(如愿紫)

Primary Purple

代码块

#8B36DB 1

亮紫(强调)

代码块

#C084FC 1

按钮紫

代码块

#934DFF 1

深按钮紫

代码块

#4D1CCB 1

粉紫⾼光

代码块

#F1A0FF 1

星球装饰⾊

星球紫

代码块

#8C48FF 1

深星球紫

代码块

#48139A 1

深阴影紫

代码块

#210759 1

⾦⾊星星

代码块

#FFD86B 1

⽂本颜⾊

⼀级标题

代码块

#FFFFFF 1

⾼亮词

代码块

#D18AFF 1

正⽂

代码块
   rgba(255,255,255,0.92)
1

辅助⽂字

代码块
   rgba(255,255,255,0.75)
1

placeholder

代码块
   rgba(216,180,254,0.48)
1

功能按钮⽂字

代码块
   #E8CCFF
1

输⼊框

背景

代码块
   rgba(43,19,83,0.72)
1

渐变终点

代码块
   rgba(32,14,61,0.66)
1
代码块
   rgba(168,85,247,0.42)
1

灵感卡⽚

背景

代码块
   rgba(48,24,89,0.78)
1

背景2

代码块
   rgba(32,14,62,0.76)
1

边框

代码块
   rgba(168,85,247,0.22)
1

⻨克⻛球

渐变:

代码块
    linear-gradient(
    145deg,
    #F1A0FF 0%,
    #934DFF 48%,
    #4D1CCB 100%
    )
1
2
3
4
5
6

外发光:

代码块rgba(169,85,247,0.75)
1

超外发光:

代码块

rgba(102,41,201,0.55) 1

字号系统

字体:

代码块

PingFang SC 1

Android:

代码块

Noto Sans SC 1

状态栏

代码块

font-size:18px 1

font-weight:700 2

左侧历史

代码块

font-size:14px 1

font-weight:400 2

⼤标题

"今天有什么⼼愿想实现"

代码块
    font-size:38px
    font-weight:800
    line-height:1.35
    letter-spacing:0.01em
1
2
3
4

如果再做更强视觉:

代码块
   font-size:42px
1

但⼿机上现在 38 更稳。

⾼亮词(⼼愿)

代码块

font-size:38px font-weight:800 color:#D18AFF 1 2 3

⻨克⻛下⽅⽂案

"按住说话,即刻如愿"

代码块

font-size:18px font-weight:500 line-height:28px 1 2 3

输⼊框

代码块

font-size:17px font-weight:400 1 2

placeholder:

代码块

font-size:17px 1

opacity:48% 2

灵感⼀下

代码块

font-size:22px 1

font-weight:700 2

换⼀换

代码块

font-size:15px 1

font-weight:400 2

灵感卡⽚

代码块

font-size:14px 1

font-weight:400 2

line-height:20px 3

圆⻆

输⼊框:

代码块
   26px
1

灵感卡:

代码块

18px 1

⻨克⻛:

代码块

50% 1

阴影系统

⻨克⻛:

代码块
box-shadow:
1
  • 0 0 36px rgba(169,85,247,0.75), 2
  • 0 0 90px rgba(102,41,201,0.55) 3

输⼊框:

代码块
  • box-shadow: 1
  • 0 0 22px rgba(116,52,202,0.12) 2

卡⽚:

代码块

box-shadow: 1

生成页

图片

历史

爽文生成 🖈

写下你的想法, AI帮你重写人生

图 我的剧本

删除

改成关闭icon

如果老板今天突然夸我 15:11 ~

心愿实现中…… 15:11

→ 心愿已实现,故事已为你展开

《那个终于被看见的人》

职场逆袭 成长

被认可

你推开会议室的门。

空气突然安静了。

老板放下手里的文件、抬头看向你。

那一刻、所有人的目光都落在你身上。

"这个方案、是你做的?"

你愣了一下。

他接着说:

"做得很好。"

这三个字,像一道光,照亮了你整个下午。

vue代码

代码块
     <template>
       <main class="page-shell">
         <section class="phone-screen">
           <div class="bg-layer"></div>
           <div class="stars-layer">
             <span
               v-for="star in stars"
               :key="star.id"
               class="star"
               :style="{
                 left: star.left + '%',
                 top: star.top + '%',
                 width: star.size + 'px',
                 height: star.size + 'px',
                 opacity: star.opacity
               }"
             />
           </div>
           <div class="content">
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<header class="status-bar">
               <span>9:41</span>
               <div class="status-icons">
                 <span>▂▃▅</span>
                 <span>⌁</span>
                 <span class="battery">▰</span>
               </div>
             </header>
             <nav class="top-nav">
               <button class="history-button" type="button">
                 <Menu :size="24" :stroke-width="2.2" />
                 <span>历史</span>
               </button>
               <button class="close-button" type="button" aria-label="关闭">
                 <X :size="22" />
               </button>
             </nav>
             <section class="chat-area">
               <div class="user-bubble">
                 如果⽼板今天突然夸我
                 <span class="message-time">15:11 ✓</span>
               </div>
               <div class="system-bubble compact">
                 <span>⼼愿实现中……</span>
                 <small>15:11</small>
               </div>
               <div class="system-bubble">
                 ✨ ⼼愿已实现,故事已为你展开
               </div>
               <article class="story-card">
                 <div class="story-cover">
                   <button class="expand-button" type="button" aria-label="全屏阅读">
                     <Maximize2 :size="19" />
                   </button>
                   <div class="cover-lines">
                     <span></span>
                     <span></span>
                   </div>
                   <div class="story-title-block">
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<h1>《那个终于被看⻅的⼈》</h1>
                     <div class="tags">
                       <span v-for="tag in tags" :key="tag">{{ tag }}</span>
                     </div>
                   </div>
                 </div>
                 <div class="story-content">
                   <p v-for="line in storyLines" :key="line">{{ line }}</p>
                   <button class="read-more" type="button">下拉继续阅读⌄</button>
                 </div>
               </article>
               <div class="action-row">
                 <button type="button">
                   <RotateCcw :size="17" />
                   换个⽅向
                 </button>
                 <button type="button">
                   <MessageSquare :size="17" />
                   不像我
                 </button>
                 <button type="button">
                   <Volume2 :size="17" />
                   语⾳播放
                 </button>
               </div>
               <div class="system-bubble question-bubble">
                 现实⾥的⽼板,<br />是不是也做过什么让你印象深刻的事?
               </div>
             </section>
             <footer class="input-bar">
               <input placeholder="今天有什么想改变的⼈⽣⽚段?" />
               <button type="button" aria-label="语⾳输⼊">
                 <Mic :size="25" />
               </button>
             </footer>
           </div>
         </section>
       </main>
     </template>
     <script setup>
     import {
       Menu,
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
Mic,
       Volume2,
       MessageSquare,
       RotateCcw,
       Maximize2,
       X,
     } from 'lucide-vue-next'
     const tags = ['职场逆袭', '成⻓', '被认可']
     const storyLines = [
       '你推开会议室的⻔。',
       '空⽓突然安静了。',
       '⽼板放下⼿⾥的⽂件,抬头看向你。',
       '那⼀刻,所有⼈的⽬光都落在你⾝上。',
       '"这个⽅案,是你做的?"',
       '你愣了⼀下。',
       '他接着说:',
       '"做得很好。"',
       '这三个字,像⼀道光,照亮了你整个下午。',
       '那⼀刻,你突然明⽩——',
       '不是你不够好,只是你⼀直没被看⻅。',
     ]
     const stars = Array.from({ length: 46 }, (_, index) => ({
       id: index,
       left: (index * 37) % 100,
       top: (index * 53) % 100,
       size: index % 4 === 0 ? 2 : 1,
       opacity: 0.25 + (((index * 7) % 50) / 100),
     }))
     </script>
     <style scoped>
     * {
       box-sizing: border-box;
     }
     .page-shell {
       min-height: 100vh;
       width: 100%;
       display: flex;
       align-items: center;
       justify-content: center;
       padding: 16px;
       background: #090216;
       color: #fff;
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica
      Neue', Arial, sans-serif;
      }
      .phone-screen {
        position: relative;
        width: 430px;
        height: 932px;
        overflow: hidden;
        border-radius: 42px;
        border: 1px solid rgba(255, 255, 255, 0.1);
        background: #0b031c;
        box-shadow: 0 24px 80px rgba(0, 0, 0, 0.55);
      }
      .bg-layer {
        position: absolute;
        inset: 0;
        background:
          radial-gradient(circle at 50% 10%, rgba(104, 47, 180, 0.35), transparent
      26%),
          radial-gradient(circle at 18% 45%, rgba(139, 55, 214, 0.24), transparent
      20%),
          radial-gradient(circle at 85% 65%, rgba(97, 35, 177, 0.22), transparent
      24%),
          linear-gradient(180deg, #12062d 0%, #090216 55%, #05010e 100%);
      }
      .stars-layer {
        position: absolute;
        inset: 0;
        opacity: 0.72;
      }
      .star {
        position: absolute;
        display: block;
        border-radius: 999px;
        background: rgba(255, 255, 255, 0.8);
        box-shadow: 0 0 8px rgba(196, 159, 255, 0.9);
      }
      .content {
        position: relative;
        z-index: 2;
        height: 100%;
        display: flex;
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
flex-direction: column;
        padding: 16px 20px 20px;
      }
      .status-bar {
        display: flex;
        align-items: center;
        justify-content: space-between;
        font-size: 15px;
        font-weight: 600;
      }
      .status-icons {
        display: flex;
        align-items: center;
        gap: 4px;
        font-size: 12px;
        opacity: 0.9;
      }
      .battery {
        border: 1px solid rgba(255, 255, 255, 0.9);
        border-radius: 3px;
        padding: 0 3px;
        font-size: 10px;
      }
      .top-nav {
        margin-top: 28px;
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
      }
      .history-button,
      .close-button,
      .action-row button,
      .input-bar button,
      .expand-button,
      .read-more {
        border: 0;
        color: inherit;
        font: inherit;
        cursor: pointer;
      }
      .history-button {
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
display: flex;
        flex-direction: column;
        align-items: center;
        gap: 4px;
        background: transparent;
        color: rgba(255, 255, 255, 0.9);
        font-size: 14px;
      }
      .close-button {
        display: grid;
        place-items: center;
        width: 42px;
        height: 42px;
        border-radius: 14px;
        border: 1px solid rgba(216, 132, 255, 0.55);
        background: rgba(50, 15, 77, 0.45);
        color: #f1ccff;
        box-shadow: 0 0 18px rgba(168, 85, 247, 0.18);
      }
      .chat-area {
        flex: 1;
        min-height: 0;
        overflow: hidden;
        padding-top: 12px;
      }
      .user-bubble {
        width: fit-content;
        max-width: 285px;
        margin-left: auto;
        padding: 12px 20px;
        border-radius: 20px 20px 6px 20px;
        border: 1px solid rgba(216, 180, 254, 0.45);
        background: linear-gradient(90deg, #5c1bb0, #8b36db);
        font-size: 17px;
        box-shadow: 0 0 18px rgba(168, 85, 247, 0.25);
      }
      .message-time {
        margin-left: 10px;
        color: rgba(243, 232, 255, 0.75);
        font-size: 14px;
      }
      .system-bubble {
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
width: fit-content;
        max-width: 340px;
        margin-top: 12px;
        padding: 12px 16px;
        border-radius: 18px;
        background: rgba(255, 255, 255, 0.07);
        backdrop-filter: blur(12px);
        font-size: 17px;
        line-height: 1.55;
      }
      .system-bubble.compact small {
        display: block;
        margin-top: 4px;
        color: rgba(255, 255, 255, 0.65);
        font-size: 14px;
      }
      .story-card {
        margin-top: 12px;
        overflow: hidden;
        border-radius: 26px;
        border: 1px solid rgba(192, 132, 252, 0.55);
        background: rgba(16, 8, 34, 0.72);
        box-shadow: 0 0 30px rgba(125, 55, 205, 0.18);
        backdrop-filter: blur(12px);
      }
      .story-cover {
        position: relative;
        height: 190px;
        overflow: hidden;
        background:
          radial-gradient(circle at 75% 35%, rgba(255, 198, 92, 0.34), transparent
      8%),
          linear-gradient(180deg, rgba(10, 5, 20, 0.05), rgba(8, 4, 18, 0.88)),
          linear-gradient(90deg, #0b0920, #1b0c30 42%, #090416);
      }
      .story-cover::before {
        content: '';
        position: absolute;
        inset: 0;
        opacity: 0.55;
        background:
          linear-gradient(90deg, transparent 0 18%, rgba(200, 170, 255, 0.15) 18%
      18.4%, transparent 18.4% 38%, rgba(200, 170, 255, 0.13) 38% 38.4%, transparent
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
38.4% 100%),
          linear-gradient(0deg, transparent 0 44%, rgba(200, 170, 255, 0.16) 44%
      44.4%, transparent 44.4% 100%);
      }
      .expand-button {
        position: absolute;
        z-index: 3;
        right: 12px;
        top: 12px;
        display: grid;
        place-items: center;
        width: 34px;
        height: 34px;
        border-radius: 999px;
        background: rgba(0, 0, 0, 0.25);
        backdrop-filter: blur(8px);
      }
      .story-title-block {
        position: absolute;
        z-index: 2;
        left: 22px;
        top: 26px;
      }
      .story-title-block h1 {
        margin: 0;
        font-size: 30px;
        line-height: 1.2;
        letter-spacing: 0.02em;
      }
      .tags {
        display: flex;
        gap: 8px;
        margin-top: 10px;
      }
      .tags span {
        border-radius: 999px;
        background: rgba(168, 85, 247, 0.22);
        padding: 5px 12px;
        color: #f3e8ff;
        font-size: 14px;
      }
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
.story-content {
        padding: 18px 24px 22px;
        color: rgba(255, 255, 255, 0.92);
        font-size: 16px;
        line-height: 1.78;
      }
      .story-content p {
        margin: 0 0 2px;
      }
      .read-more {
        display: block;
        margin: 12px auto 0;
        background: transparent;
        color: #e9d5ff;
      }
      .action-row {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: 12px;
        margin-top: 12px;
      }
      .action-row button {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
        border-radius: 18px;
        border: 1px solid rgba(192, 132, 252, 0.35);
        background: rgba(88, 28, 135, 0.18);
        padding: 12px 6px;
        color: #f3e8ff;
        font-size: 14px;
      }
      .question-bubble {
        width: 330px;
        margin-top: 14px;
        font-size: 16px;
      }
      .input-bar {
        margin-top: auto;
        display: flex;
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
align-items: center;
        gap: 12px;
        border-radius: 999px;
        border: 1px solid rgba(168, 85, 247, 0.25);
        background: rgba(20, 9, 34, 0.9);
        padding: 10px 10px 10px 18px;
        box-shadow: 0 0 20px rgba(119, 57, 211, 0.15);
      }
      .input-bar input {
        min-width: 0;
        flex: 1;
        border: 0;
        outline: none;
        background: transparent;
        color: #f3e8ff;
        font-size: 16px;
      }
      .input-bar input::placeholder {
        color: rgba(216, 180, 254, 0.46);
      }
      .input-bar button {
        display: grid;
        place-items: center;
        width: 48px;
        height: 48px;
        border-radius: 999px;
        background: linear-gradient(135deg, #8c44f2, #5f1db8);
        box-shadow: 0 0 22px rgba(168, 85, 247, 0.55);
      }
      @media (max-width: 480px) {
        .page-shell {
          padding: 0;
          background: #0b031c;
        }
        .phone-screen {
          width: 100vw;
          height: 100vh;
          border-radius: 0;
          border: 0;
        }
      }
      </style>
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482

⾊卡

背景层

主背景(最深)

代码块

#090216 1

次背景

代码块

#0B031C 1

底部渐变暗层

代码块

#05010E 1

宇宙紫辉光(顶部)

代码块

rgba(104,47,180,0.35) 1

星云紫

代码块

rgba(139,55,214,0.24) 1

辅助紫

代码块

rgba(97,35,177,0.22)
1

主品牌⾊(如愿紫)

Primary Purple

代码块

#8B36DB 1

深紫渐变起点

代码块

#5C1BB0 1

按钮渐变终点

代码块

#8C44F2 1

按钮渐变起点

代码块

#5F1DB8 1

⽂本⾊

⼀级⽂字(标题)

代码块

#FFFFFF 1

opacity:100% 2

⼆级⽂字(正⽂)

代码块
   rgba(255,255,255,0.92)
1

三级⽂字

代码块
   rgba(255,255,255,0.75)
1

辅助说明

代码块
   rgba(255,255,255,0.55)
1

placeholder

代码块
   rgba(216,180,254,0.46)
1

卡⽚颜⾊

故事卡背景

代码块
   rgba(16,8,34,0.72)
1

对话框背景

代码块
   rgba(255,255,255,0.07)
1

按钮背景

代码块
   rgba(88,28,135,0.18)
1

标签背景

代码块
   rgba(168,85,247,0.22)
1

描边

故事卡边框

代码块
   rgba(192,132,252,0.55)
1

按钮边框

代码块
   rgba(192,132,252,0.35)
1

输⼊框边框

代码块
   rgba(168,85,247,0.25)
1

阴影

主发光

代码块
   box-shadow:
   0 0 22px rgba(168,85,247,0.55)
1
2

故事卡发光

代码块 box-shadow: 0 0 30px rgba(125,55,205,0.18) 1 2

按钮发光

代码块

  • box-shadow: 1
  • 0 0 18px rgba(168,85,247,0.25) 2

字号规范

建议使⽤:

代码块

PingFang SC 1

iOS:

代码块

PingFang SC 1

Android:

代码块

Noto Sans SC 1

状态栏时间

代码块

font-size:15px 1

⽤⼾输⼊⽓泡

例如:

如果⽼板今天突然夸我

代码块

font-size:17px font-weight:500 line-height:26px 1 2 3

系统消息

例如:

⼼愿实现中……

代码块

font-size:17px font-weight:500 1 2

时间:

代码块

font-size:14px opacity:0.65 1 2

⼩说标题

代码块

font-size:30px
    font-weight:700
    line-height:42px
1
2
3

例如:

《那个终于被看⻅的⼈》

标签⽂字

代码块

font-size:14px font-weight:500 1 2

⼩说正⽂

代码块

font-size:16px font-weight:400 line-height:1.78 1 2 3

功能按钮

代码块

font-size:14px font-weight:500 1 2

例如:

  • 换个⽅向
  • 不像我
  • 语⾳播放

输⼊框

代码块 font-size:16px font-weight:400 1 2

placeholder:

代码块
  • font-size:16px 1
  • opacity:46% 2

圆⻆规范

⼤卡⽚:

代码块

26px 1

聊天⽓泡:

代码块

1820px 1

按钮:

代码块

18px 1

输⼊栏:

代码块

999px 1

麦克风按钮:

代码块

1 50%

这一套已经基本是一个完整的 如愿星球 Design Token(设计系统v1),后面 Figma 可以直接建变量。