406 lines
8.4 KiB
Vue
406 lines
8.4 KiB
Vue
<template>
|
||
<div class="spring-couple-container">
|
||
<!-- 背景图 -->
|
||
<div class="background">
|
||
<img
|
||
v-lazy="backgroundImage"
|
||
alt="四合院背景"
|
||
@error="handleImageError"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 春联展示区 -->
|
||
<div class="couplet-display" v-if="couplet">
|
||
<div class="couplet-item top-couplet">{{ couplet.top }}</div>
|
||
<div class="couplet-item bottom-couplet">{{ couplet.bottom }}</div>
|
||
<div class="couplet-item横批">{{ couplet.横批 }}</div>
|
||
</div>
|
||
|
||
<!-- 输入区域 -->
|
||
<div class="input-section">
|
||
<h2 class="title">AI春联生成</h2>
|
||
<p class="subtitle">输入两个字的关键词,生成个性化春联</p>
|
||
|
||
<div class="input-container">
|
||
<input
|
||
v-model="keyword"
|
||
type="text"
|
||
placeholder="请输入关键词(如:吉祥、如意)"
|
||
maxlength="2"
|
||
@input="handleKeywordInput"
|
||
class="keyword-input"
|
||
/>
|
||
<button
|
||
@click="generateCouple"
|
||
:disabled="loading || keyword.length < 2"
|
||
class="generate-btn"
|
||
>
|
||
<span v-if="loading">生成中...</span>
|
||
<span v-else>生成春联</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 推荐关键词 -->
|
||
<div class="recommended-keywords">
|
||
<span
|
||
v-for="word in recommendedWords"
|
||
:key="word"
|
||
@click="selectKeyword(word)"
|
||
class="keyword-tag"
|
||
>
|
||
{{ word }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分享按钮 -->
|
||
<div class="share-section" v-if="couplet">
|
||
<button @click="shareCouple" class="share-btn">
|
||
📤 分享春联
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 加载动画 -->
|
||
<div v-if="loading" class="loading-overlay">
|
||
<uni-spinner type="circle" size="48" color="#fff"></uni-spinner>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive } from 'vue';
|
||
import { generateSpringCouple } from '../api/ai';
|
||
import { share } from '../utils/share';
|
||
|
||
// 响应式数据
|
||
const keyword = ref('');
|
||
const couplet = ref(null);
|
||
const loading = ref(false);
|
||
const backgroundImage = ref('https://placeholder.pics/svg/640x1136/E8F5E9/4CAF50/四合院背景');
|
||
|
||
// 推荐关键词
|
||
const recommendedWords = [
|
||
'吉祥', '如意', '幸福', '安康', '财源', '富贵', '团圆', '快乐',
|
||
'平安', '健康', '顺利', '成功', '美满', '和谐', '兴旺', '发达'
|
||
];
|
||
|
||
// 处理关键词输入
|
||
const handleKeywordInput = () => {
|
||
// 限制关键词长度为2个字符
|
||
if (keyword.value.length > 2) {
|
||
keyword.value = keyword.value.slice(0, 2);
|
||
}
|
||
};
|
||
|
||
// 选择推荐关键词
|
||
const selectKeyword = (word) => {
|
||
keyword.value = word;
|
||
generateCouple();
|
||
};
|
||
|
||
// 生成春联
|
||
const generateCouple = async () => {
|
||
if (!keyword.value || keyword.value.length < 2) {
|
||
uni.uni.showToast({title: '请输入两个字的关键词', duration: 2000});
|
||
return;
|
||
}
|
||
|
||
loading.value = true;
|
||
|
||
try {
|
||
const result = await generateSpringCouple(keyword.value);
|
||
|
||
if (result.code === 200) {
|
||
couplet.value = result.data;
|
||
uni.uni.showToast({title: '春联生成成功', duration: 2000});
|
||
// 添加生成动画效果
|
||
setTimeout(() => {
|
||
const coupletElements = document.querySelectorAll('.couplet-item');
|
||
coupletElements.forEach((el, index) => {
|
||
el.style.opacity = '0';
|
||
el.style.transform = 'translateY(20px)';
|
||
|
||
setTimeout(() => {
|
||
el.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
|
||
el.style.opacity = '1';
|
||
el.style.transform = 'translateY(0)';
|
||
}, index * 200);
|
||
});
|
||
}, 100);
|
||
} else {
|
||
uni.uni.showToast({title: '生成失败,请重试', duration: 2000});
|
||
}
|
||
} catch (error) {
|
||
console.error('生成春联失败:', error);
|
||
uni.uni.showToast({title: '网络异常,请稍后重试', duration: 2000});
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 分享春联
|
||
const shareCouple = () => {
|
||
if (!couplet.value) {
|
||
uni.uni.showToast({title: '请先生成春联', duration: 2000});
|
||
return;
|
||
}
|
||
|
||
const shareText = `
|
||
我用AI生成了一副春联:
|
||
${couplet.value.top}
|
||
${couplet.value.bottom}
|
||
${couplet.value.横批}
|
||
#2026新春东城商圈#
|
||
`;
|
||
|
||
// 调用分享工具函数
|
||
share({
|
||
title: 'AI春联生成',
|
||
text: shareText,
|
||
image: backgroundImage.value
|
||
});
|
||
};
|
||
|
||
// 处理图片加载错误
|
||
const handleImageError = (event) => {
|
||
event.target.src = 'https://placeholder.pics/svg/640x1136/E8F5E9/4CAF50/四合院背景';
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.spring-couple-container {
|
||
position: relative;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 20px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.background {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: -1;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.background img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.couplet-display {
|
||
position: relative;
|
||
width: 100%;
|
||
max-width: 600px;
|
||
margin: 40px 0;
|
||
padding: 60px 20px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
z-index: 1;
|
||
}
|
||
|
||
.couplet-item {
|
||
font-family: 'STKaiti', 'Kaiti SC', serif;
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
color: #e67e22;
|
||
text-align: center;
|
||
line-height: 1.8;
|
||
writing-mode: vertical-rl;
|
||
text-orientation: upright;
|
||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||
opacity: 0;
|
||
}
|
||
|
||
.top-couplet,
|
||
.bottom-couplet {
|
||
width: 60px;
|
||
height: 240px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.横批 {
|
||
position: absolute;
|
||
top: 20px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
writing-mode: horizontal-tb;
|
||
font-size: 32px;
|
||
width: 200px;
|
||
}
|
||
|
||
.input-section {
|
||
background: rgba(255, 255, 255, 0.9);
|
||
border-radius: 20px;
|
||
padding: 30px;
|
||
width: 100%;
|
||
max-width: 500px;
|
||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||
backdrop-filter: blur(10px);
|
||
z-index: 2;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.title {
|
||
font-size: 28px;
|
||
color: #e67e22;
|
||
margin-bottom: 10px;
|
||
text-align: center;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 16px;
|
||
color: #666;
|
||
margin-bottom: 25px;
|
||
text-align: center;
|
||
}
|
||
|
||
.input-container {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.keyword-input {
|
||
flex: 1;
|
||
padding: 15px;
|
||
border: 2px solid #ddd;
|
||
border-radius: 10px;
|
||
font-size: 18px;
|
||
text-align: center;
|
||
transition: border-color 0.3s ease;
|
||
}
|
||
|
||
.keyword-input:focus {
|
||
outline: none;
|
||
border-color: #e67e22;
|
||
}
|
||
|
||
.generate-btn {
|
||
padding: 15px 25px;
|
||
background: linear-gradient(135deg, #e67e22, #d35400);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 10px;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.generate-btn:hover:not(:disabled) {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(230, 126, 34, 0.4);
|
||
}
|
||
|
||
.generate-btn:disabled {
|
||
background: #ddd;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.recommended-keywords {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
justify-content: center;
|
||
}
|
||
|
||
.keyword-tag {
|
||
padding: 8px 16px;
|
||
background: #f39c12;
|
||
color: white;
|
||
border-radius: 20px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.keyword-tag:hover {
|
||
background: #e67e22;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.share-section {
|
||
margin-top: 20px;
|
||
margin-bottom: 20px;
|
||
z-index: 2;
|
||
}
|
||
|
||
.share-btn {
|
||
padding: 15px 30px;
|
||
background: linear-gradient(135deg, #3498db, #2980b9);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 25px;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.share-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.4);
|
||
}
|
||
|
||
.loading-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 999;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 480px) {
|
||
.spring-couple-container {
|
||
padding: 10px;
|
||
}
|
||
|
||
.couplet-item {
|
||
font-size: 20px;
|
||
}
|
||
|
||
.top-couplet,
|
||
.bottom-couplet {
|
||
height: 200px;
|
||
}
|
||
|
||
.input-section {
|
||
padding: 20px;
|
||
}
|
||
|
||
.title {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.keyword-input,
|
||
.generate-btn {
|
||
font-size: 16px;
|
||
padding: 12px;
|
||
}
|
||
|
||
.input-container {
|
||
flex-direction: column;
|
||
}
|
||
}
|
||
</style>
|