取名小程序

This commit is contained in:
cjd
2025-11-06 19:23:37 +08:00
parent 200c29ac09
commit 1f9f8cd083
10 changed files with 253 additions and 38 deletions

View File

@@ -51,7 +51,7 @@
position: relative;
z-index: 2;
height: 100vh;
padding: 48px 32px 120px;
padding: 18px 12px 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
@@ -90,7 +90,8 @@
.favorite-card {
position: relative;
background: linear-gradient(135deg, rgba(22, 28, 54, 0.85), rgba(32, 38, 68, 0.75));
border-radius: 24px;
border-radius: 14px;
margin-top: 10px;
padding: 26px 24px;
border: 1px solid rgba(255, 255, 255, 0.08);
overflow: hidden;

View File

@@ -1,9 +1,15 @@
const namingStore = require("../../store/namingStore");
const namingStore = require("../../store/namingStore");
const messages = require("../../constants/messages");
const { validateSurname, generateName } = require("../../services/namingService");
const { showRewardedVideoAd } = require("../../utils/adService");
const config = require("../../config/index");
const LOADING_MESSAGES = [
"AI 正在推演八字,请稍候...",
"结合天干地支,为您筛选契合佳名...",
"耐心等待,吉名正在汇聚灵感..."
];
function showToast(title) {
if (typeof tt === "undefined" || !tt.showToast) {
console.warn("Toast:", title);
@@ -25,7 +31,9 @@ Page({
birthTime: "",
nameLength: "double",
isSubmitting: false,
quotaRemaining: null
quotaRemaining: null,
isLoading: false,
loadingMessage: ""
},
onLoad() {
const { form, quota } = namingStore.getState();
@@ -53,11 +61,13 @@ Page({
}
validateSurname(surname)
.then((response) => {
if (!response || response.isValid === false) {
showToast(messages.INVALID_SURNAME);
const isValid = response ? response.isValid !== false : true;
const message = response && response.message ? response.message : messages.INVALID_SURNAME;
if (!isValid) {
showToast(message);
this.setData({
surname: "",
surnameError: messages.INVALID_SURNAME
surnameError: message
});
namingStore.setForm({ surname: "" });
} else {
@@ -132,6 +142,7 @@ Page({
} else {
showToast("广告加载失败,请稍后重试");
}
this.hideLoadingOverlay();
this.setData({ isSubmitting: false });
});
},
@@ -143,7 +154,8 @@ Page({
birthTime: this.data.birthTime || "",
nameLength: this.data.nameLength
};
generateName(payload)
this.showLoadingOverlay();
return generateName(payload)
.then((response) => {
const results = response && Array.isArray(response.results) ? response.results : [];
const analysis = response && response.analysis ? response.analysis : null;
@@ -177,11 +189,35 @@ Page({
tt.navigateTo({ url: "/pages/result/index" });
}
})
.catch(() => {
showToast(messages.GENERATION_FAILED);
.catch((error) => {
const message = error && error.data && error.data.message;
if (message === "CONTENT_RISK") {
showToast(messages.CONTENT_RISK);
} else if (message === "INVALID_SURNAME") {
showToast(messages.INVALID_SURNAME);
} else {
showToast(messages.GENERATION_FAILED);
}
})
.finally(() => {
this.hideLoadingOverlay();
this.setData({ isSubmitting: false });
});
},
showLoadingOverlay() {
const hint = LOADING_MESSAGES[Math.floor(Math.random() * LOADING_MESSAGES.length)];
this.setData({
isLoading: true,
loadingMessage: hint
});
},
hideLoadingOverlay() {
if (!this.data.isLoading) {
return;
}
this.setData({
isLoading: false,
loadingMessage: ""
});
}
});

View File

@@ -5,6 +5,14 @@
<view class="glow glow-three"></view>
</view>
<view class="loading-overlay" tt:if="{{isLoading}}">
<view class="loading-dialog">
<view class="loading-spinner"></view>
<text class="loading-text">{{loadingMessage}}</text>
<text class="loading-subtext">推演需片刻,感谢耐心等待</text>
</view>
</view>
<scroll-view class="content" scroll-y="true">
<view class="title-block">
<text class="title">玄名殿</text>

View File

@@ -4,6 +4,54 @@
overflow: hidden;
}
.loading-overlay {
position: fixed;
inset: 0;
z-index: 20;
background: rgba(6, 10, 22, 0.6);
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(6px);
}
.loading-dialog {
width: 82%;
max-width: 320px;
padding: 26px 24px;
border-radius: 22px;
background: linear-gradient(180deg, rgba(22, 28, 52, 0.95), rgba(12, 18, 36, 0.88));
border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 24px 48px rgba(8, 10, 24, 0.4);
display: flex;
flex-direction: column;
gap: 14px;
align-items: center;
color: rgba(255, 244, 227, 0.9);
text-align: center;
}
.loading-spinner {
width: 40px;
height: 40px;
border-radius: 50%;
border: 3px solid rgba(255, 244, 227, 0.25);
border-top-color: #ffd8a0;
animation: spin 1s linear infinite;
}
.loading-text {
font-size: 15px;
letter-spacing: 2px;
line-height: 1.6;
}
.loading-subtext {
font-size: 12px;
color: rgba(255, 244, 227, 0.6);
letter-spacing: 1px;
}
.glow-layer {
position: absolute;
inset: 0;
@@ -56,6 +104,15 @@
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.content {
position: relative;
z-index: 2;

View File

@@ -15,7 +15,8 @@ function showToast(title) {
Page({
data: {
results: [],
matchSummary: ""
matchSummary: "",
elementsChart: []
},
onLoad() {
const { results, analysis } = namingStore.getState();
@@ -28,15 +29,39 @@ Page({
}, 1000);
return;
}
const summary = analysis && (analysis.matchSummary || analysis.MatchSummary) ? analysis.matchSummary || analysis.MatchSummary : "";
const summary = analysis && (analysis.matchSummary || analysis.MatchSummary)
? analysis.matchSummary || analysis.MatchSummary
: "";
const elements = Array.isArray(analysis && (analysis.elementDistribution || analysis.ElementDistribution))
? analysis.elementDistribution || analysis.ElementDistribution
: [];
const total = elements.reduce((acc, current) => {
const value = Number(current.count ?? current.Count ?? 0);
return acc + (Number.isNaN(value) ? 0 : value);
}, 0);
const chart = elements.map((item) => {
const elementName = item.element || item.Element || "";
const countValue = Number(item.count ?? item.Count ?? 0);
const count = Number.isNaN(countValue) ? 0 : countValue;
const percent = total > 0 ? Math.round((count / total) * 100) : 0;
const widthPercent = percent > 0 ? Math.max(percent, 6) : 0;
return {
label: elementName || "未知",
count,
percent,
percentText: `${percent}%`,
percentWidth: `${widthPercent}%`
};
});
const normalized = results.map((item) => ({
name: item.name,
meaning: item.meaning || "寓意待补充",
elementReason: item.elementReason || "五行流转相济"
meaning: item.meaning || item.Meaning || "寓意待补充",
elementReason: item.elementReason || item.ElementReason || "五行流转相济"
}));
this.setData({
results: normalized,
matchSummary: summary
matchSummary: summary,
elementsChart: chart
});
},
handleFavorite(event) {

View File

@@ -1,4 +1,4 @@
<view class="page">
<view class="page">
<view class="glow-layer">
<view class="glow glow-one"></view>
<view class="glow glow-two"></view>
@@ -12,8 +12,22 @@
<view class="summary" tt:if="{{matchSummary}}">
<text class="summary-text">{{matchSummary}}</text>
<view class="chart-title">五行占比</view>
<block tt:for="{{elementsChart}}" tt:for-item="item" tt:key="label">
<view class="chart-row">
<view class="chart-label">{{item.label}}</view>
<view class="chart-bar">
<view class="chart-bar-fill" style="width: {{item.percentWidth}};"></view>
</view>
<text class="chart-percent">{{item.count}}{{item.percentText}}</text>
</view>
</block>
</view>
<!-- <view class="element-section" tt:if="{{elementsChart.length}}">
</view> -->
<view class="empty" tt:if="{{!results.length}}">
<text>暂无生成结果,请返回主页重新生成。</text>
<button class="primary back-button" bindtap="handleBack">返回主页</button>
@@ -23,19 +37,23 @@
<view class="tip">轻触收藏按钮即可将心仪姓名收入星册</view>
<block tt:for="{{results}}" tt:for-index="index" tt:for-item="item" tt:key="name">
<view class="result-card">
<text class="result-line">{{item.name}}{{item.meaning}}{{item.elementReason}}</text>
<button
class="collect-button"
size="mini"
bindtap="handleFavorite"
data-name="{{item.name}}"
data-meaning="{{item.meaning}}"
>
收藏
</button>
<view class="result-header">
<text class="result-name">{{item.name}}</text>
<button
class="collect-button"
size="mini"
bindtap="handleFavorite"
data-name="{{item.name}}"
data-meaning="{{item.meaning}}"
>
收藏
</button>
</view>
<text class="result-meaning">{{item.meaning}}</text>
<text class="result-reason">{{item.elementReason}}</text>
</view>
</block>
<button class="primary back-button" bindtap="handleBack">再测一批</button>
</view>
</scroll-view>
</view>
</view>

View File

@@ -102,6 +102,56 @@
margin-bottom: 8px;
}
.element-section {
display: flex;
flex-direction: column;
gap: 12px;
background: rgba(18, 24, 46, 0.65);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
padding: 16px 18px;
color: rgba(255, 244, 227, 0.85);
}
.chart-title {
font-size: 14px;
letter-spacing: 3px;
text-align: center;
}
.chart-row {
display: flex;
align-items: center;
gap: 10px;
}
.chart-label {
width: 42px;
font-size: 13px;
color: rgba(255, 244, 227, 0.78);
}
.chart-bar {
flex: 1;
height: 10px;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
overflow: hidden;
}
.chart-bar-fill {
height: 100%;
background: linear-gradient(90deg, rgba(255, 205, 146, 0.9), rgba(112, 184, 255, 0.9));
border-radius: 10px;
}
.chart-percent {
font-size: 12px;
color: rgba(255, 244, 227, 0.7);
min-width: 70px;
text-align: right;
}
.result-card {
display: flex;
flex-direction: column;
@@ -115,11 +165,19 @@
backdrop-filter: blur(16px);
}
.result-line {
font-size: 15px;
color: rgba(255, 244, 227, 0.9);
line-height: 1.8;
word-break: break-all;
.result-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.result-name {
font-size: 18px;
color: #fdf0da;
font-weight: 600;
letter-spacing: 3px;
flex: 1;
}
.collect-button {
@@ -130,7 +188,18 @@
background: rgba(255, 255, 255, 0.12);
color: #fbe6ce;
border: 1px solid rgba(255, 255, 255, 0.18);
align-self: flex-end;
}
.result-meaning {
font-size: 14px;
color: rgba(255, 244, 227, 0.86);
line-height: 1.8;
}
.result-reason {
font-size: 13px;
color: rgba(255, 244, 227, 0.7);
line-height: 1.6;
}
.back-button {