feat: 完成见素起名小程序核心功能

- 实现 AI 起名功能(Kimi API 接入)
- 添加用户收藏功能(MySQL 数据库)
- 实现海报生成与分享
- 添加音效和触觉反馈
- 配置生产环境部署(WAR 包 + Nginx)
- 支持多种起名模式(经典、诗词、自然、现代)
- 实现分批加载优化体验
This commit is contained in:
王鹏
2026-04-17 15:34:51 +08:00
parent 1a749cdf71
commit be1f5722ab
136 changed files with 3322 additions and 420 deletions

View File

@@ -1,25 +1,98 @@
Page({
data: {
keyword: ''
keyword: '',
surname: '',
activeMode: 'baby',
isGenerating: false,
modes: [
{ key: 'baby', label: '宝宝' },
{ key: 'persona', label: '人设' },
{ key: 'classic', label: '拾遗' }
]
},
onLoad() {
// 页面加载时检查是否需要重置状态
},
onShow() {
// 每次显示页面时重置生成状态
this.setData({
isGenerating: false
});
},
// 选择模式
selectMode(e) {
const mode = e.currentTarget.dataset.mode;
this.setData({
activeMode: mode
});
},
// 姓氏输入
onSurnameInput(e) {
this.setData({
surname: e.detail.value
});
},
// 关键词输入
onInput(e) {
this.setData({
keyword: e.detail.value
});
},
startNaming() {
if (!this.data.keyword.trim()) {
// 开始生成(带水墨动画)
startGenerate() {
const { keyword, surname, activeMode } = this.data;
// 非空校验
if (!keyword.trim()) {
const placeholderMap = {
'baby': '请输入期待',
'persona': '请输入人设',
'classic': '请输入关键词'
};
wx.showToast({
title: '请输入一抹期待',
title: placeholderMap[activeMode] || '请输入内容',
icon: 'none'
});
return;
}
// 宝宝模式需要姓氏
if (activeMode === 'baby' && !surname.trim()) {
wx.showToast({
title: '请输入姓氏',
icon: 'none'
});
return;
}
wx.navigateTo({
url: `/pages/index/index?keyword=${encodeURIComponent(this.data.keyword)}`
// 触发水墨动画
this.setData({
isGenerating: true
});
// 播放水滴声
getApp().playAudio('inkDrop');
// 触觉反馈
wx.vibrateShort({ type: 'light' });
// 延迟 1000ms 后跳转,确保仪式感完整
setTimeout(() => {
// 构建跳转 URL
let url = `/pages/index/index?keyword=${encodeURIComponent(keyword)}&mode=${activeMode}`;
if (activeMode === 'baby' && surname) {
url += `&surname=${encodeURIComponent(surname)}`;
}
wx.navigateTo({
url: url
});
}, 1000);
}
});
});

View File

@@ -1,12 +1,45 @@
<view class="container">
<view class="content">
<!-- 水墨动画遮罩 -->
<view class="ink-overlay {{isGenerating ? 'active' : ''}}">
<view class="ink-circle"></view>
</view>
<view class="content {{isGenerating ? 'fade-out' : ''}}">
<view class="title">见素</view>
<view class="subtitle">回归名字的诗意与留白</view>
<!-- 模式选择 -->
<view class="mode-selector">
<view
wx:for="{{modes}}"
wx:key="key"
class="mode-item {{activeMode === item.key ? 'active' : ''}}"
bindtap="selectMode"
data-mode="{{item.key}}"
>
{{item.label}}
<view class="mode-dot" wx:if="{{activeMode === item.key}}"></view>
</view>
</view>
<!-- 姓氏输入(仅在宝宝模式显示) -->
<view class="surname-section" wx:if="{{activeMode === 'baby'}}">
<input
class="surname-input"
placeholder="姓氏"
placeholder-class="placeholder"
bindinput="onSurnameInput"
value="{{surname}}"
maxlength="2"
/>
<view class="line"></view>
</view>
<!-- 关键词输入 -->
<view class="input-section">
<input
class="keyword-input"
placeholder="输入关键词,如:自由、清冷"
placeholder="{{activeMode === 'baby' ? '期待,如:自由、清冷' : activeMode === 'persona' ? '人设,如:温柔、坚韧' : '关键词,如:自由、清冷'}}"
placeholder-class="placeholder"
bindinput="onInput"
value="{{keyword}}"
@@ -15,11 +48,11 @@
</view>
<view class="action-section">
<button class="generate-btn" bindtap="startNaming">感悟名字</button>
<button class="generate-btn" bindtap="startGenerate">感悟名字</button>
</view>
</view>
<view class="footer">
<view class="footer {{isGenerating ? 'fade-out' : ''}}">
<text>© 见素 · 审美溢价</text>
</view>
</view>
</view>

View File

@@ -9,14 +9,67 @@ page {
flex-direction: column;
padding: 0 80rpx;
overflow: hidden;
position: relative;
}
/* 水墨动画遮罩 */
.ink-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100;
opacity: 0;
visibility: hidden;
overflow: hidden;
background: transparent;
}
.ink-overlay.active {
opacity: 1;
visibility: visible;
}
.ink-circle {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background-color: #000000;
border-radius: 50%;
transform: translate(-50%, -50%);
}
.ink-overlay.active .ink-circle {
animation: inkSpread 1.8s cubic-bezier(0.25, 0.1, 0.25, 1) forwards;
}
@keyframes inkSpread {
0% {
width: 0;
height: 0;
opacity: 1;
}
100% {
width: 350vmax;
height: 350vmax;
opacity: 1;
}
}
.content {
margin-top: 35vh;
margin-top: 30vh;
display: flex;
flex-direction: column;
align-items: center;
animation: contentFadeIn 1.5s cubic-bezier(0.19, 1, 0.22, 1);
transition: opacity 0.5s ease;
}
.content.fade-out {
opacity: 0;
}
.title {
@@ -33,12 +86,61 @@ page {
font-size: 22rpx;
color: #D0D0D0;
letter-spacing: 6rpx;
margin-bottom: 150rpx;
margin-bottom: 80rpx;
}
/* 模式选择器 */
.mode-selector {
display: flex;
flex-direction: row;
justify-content: center;
margin-bottom: 60rpx;
gap: 60rpx;
}
.mode-item {
font-size: 24rpx;
color: #B0B0B0;
letter-spacing: 4rpx;
padding: 10rpx 0;
position: relative;
transition: all 0.3s ease;
}
.mode-item.active {
color: #2D2D2D;
}
.mode-dot {
position: absolute;
bottom: -4rpx;
left: 50%;
transform: translateX(-50%);
width: 6rpx;
height: 6rpx;
background-color: #2D2D2D;
border-radius: 50%;
}
/* 姓氏输入 */
.surname-section {
width: 100%;
margin-bottom: 40rpx;
}
.surname-input {
width: 100%;
height: 80rpx;
text-align: center;
font-size: 32rpx;
color: #4A4A4A;
letter-spacing: 4rpx;
}
/* 关键词输入 */
.input-section {
width: 100%;
margin-bottom: 80rpx;
margin-bottom: 60rpx;
}
.keyword-input {
@@ -63,6 +165,7 @@ page {
transition: width 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.surname-section:focus-within .line,
.input-section:focus-within .line {
width: 80rpx;
background-color: #2D2D2D;
@@ -75,16 +178,18 @@ page {
margin-top: 40rpx;
}
/* 全黑背景、无圆角、加宽字间距的按钮 */
.generate-btn {
background: none !important;
color: #A0A0A0 !important;
background-color: #1a1a1a !important;
color: #ffffff !important;
font-size: 24rpx !important;
font-weight: 200 !important;
padding: 20rpx 60rpx !important;
letter-spacing: 12rpx !important;
text-indent: 12rpx !important;
transition: all 0.3s;
font-weight: 300 !important;
padding: 24rpx 80rpx !important;
letter-spacing: 16rpx !important;
text-indent: 16rpx !important;
transition: all 0.3s ease;
border: none !important;
border-radius: 0 !important;
}
.generate-btn::after {
@@ -92,7 +197,7 @@ page {
}
.generate-btn:active {
color: #2D2D2D !important;
background-color: #333333 !important;
transform: scale(0.98);
}
@@ -103,6 +208,11 @@ page {
right: 0;
text-align: center;
animation: footerFadeIn 3s ease-in;
transition: opacity 0.5s ease;
}
.footer.fade-out {
opacity: 0;
}
.footer text {
@@ -120,4 +230,4 @@ page {
0% { opacity: 0; }
70% { opacity: 0; }
100% { opacity: 1; }
}
}