678 lines
14 KiB
Vue
678 lines
14 KiB
Vue
<template>
|
|
<view class="daily-page">
|
|
<NavBar title="每日精选" color="#333" />
|
|
|
|
<!-- Header -->
|
|
<view class="page-header">
|
|
<view class="header-left">
|
|
<uni-icons type="sun-filled" size="24" color="#ff9800" />
|
|
<text class="header-title">{{ greetingTitle }}</text>
|
|
</view>
|
|
<view class="streak-badge">
|
|
<text>已连续问候</text>
|
|
<text class="streak-count">{{ streakDays }}</text>
|
|
<text>天</text>
|
|
<text class="fire-icon">🔥</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- Main Card -->
|
|
<view class="main-card-container">
|
|
<view class="quote-card">
|
|
<view class="quote-icon">❝</view>
|
|
<view class="quote-content">
|
|
<text class="quote-text">{{ currentQuote.text }}</text>
|
|
<text class="quote-highlight" v-if="currentQuote.highlight">{{
|
|
currentQuote.highlight
|
|
}}</text>
|
|
</view>
|
|
<view class="quote-divider"></view>
|
|
<view class="quote-author" v-if="authorName">
|
|
<text>—— {{ authorName }} 的专属问候</text>
|
|
</view>
|
|
|
|
<view class="card-actions">
|
|
<view class="refresh-btn" @tap="refreshQuote">
|
|
<uni-icons type="loop" size="16" color="#666" />
|
|
<text>换一句</text>
|
|
</view>
|
|
|
|
<view class="author-edit-box">
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
v-model="authorName"
|
|
placeholder="输入名字生成专属问候"
|
|
/>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- Categories -->
|
|
<view class="category-section">
|
|
<view class="category-grid">
|
|
<view
|
|
v-for="(cat, index) in categories"
|
|
:key="index"
|
|
class="category-item"
|
|
@tap="selectCategory(cat.id)"
|
|
>
|
|
<view
|
|
class="cat-icon-box"
|
|
:class="{ active: currentCategory === cat.id }"
|
|
:style="{ background: cat.bg }"
|
|
>
|
|
<text class="cat-emoji">{{ cat.emoji }}</text>
|
|
</view>
|
|
<text
|
|
class="cat-name"
|
|
:class="{ active: currentCategory === cat.id }"
|
|
>{{ cat.name }}</text
|
|
>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- Hot List -->
|
|
<view class="hot-list-section">
|
|
<view class="section-title-row">
|
|
<view class="title-bar"></view>
|
|
<text class="section-title">今日最热榜单</text>
|
|
<text class="section-subtitle">REAL-TIME DATA</text>
|
|
</view>
|
|
|
|
<view class="hot-list">
|
|
<view
|
|
v-for="(item, index) in hotList"
|
|
:key="index"
|
|
class="hot-item"
|
|
@tap="useHotItem(item)"
|
|
>
|
|
<view class="rank-icon">
|
|
<uni-icons
|
|
v-if="index === 0"
|
|
type="vip-filled"
|
|
size="24"
|
|
color="#ffbc00"
|
|
/>
|
|
<uni-icons
|
|
v-else-if="index === 1"
|
|
type="vip-filled"
|
|
size="24"
|
|
color="#b0bec5"
|
|
/>
|
|
<uni-icons v-else type="vip-filled" size="24" color="#cd7f32" />
|
|
</view>
|
|
<view class="hot-content">
|
|
<text class="hot-text">{{ item.text }}</text>
|
|
<view class="hot-meta">
|
|
<text class="fire">🔥</text>
|
|
<text class="hot-count">{{ item.count }}w 人正在使用</text>
|
|
</view>
|
|
</view>
|
|
<uni-icons type="right" size="16" color="#ccc" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- Bottom Actions -->
|
|
<view class="bottom-actions safe-area-bottom">
|
|
<view class="action-btn-group">
|
|
<button class="save-btn" @tap="saveCard">
|
|
<view class="icon-circle">
|
|
<uni-icons type="download" size="20" color="#333" />
|
|
</view>
|
|
<text class="btn-text">保存</text>
|
|
</button>
|
|
<button class="send-btn" @tap="sendGreeting">
|
|
<uni-icons type="paperplane-filled" size="20" color="#fff" />
|
|
<text>立即发送今日问候</text>
|
|
</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted, watch } from "vue";
|
|
import { getBavBarHeight } from "@/utils/system";
|
|
import NavBar from "@/components/NavBar/NavBar.vue";
|
|
|
|
const navBarHeight = getBavBarHeight();
|
|
const streakDays = ref(5);
|
|
|
|
const dateStr = computed(() => {
|
|
const date = new Date();
|
|
return `${date.getFullYear()} 灵马送福 · 每日精选`;
|
|
});
|
|
|
|
const greetingTitle = computed(() => {
|
|
const hour = new Date().getHours();
|
|
if (hour < 9) return "早安,今天也要好运";
|
|
if (hour < 12) return "上午好,元气满满";
|
|
if (hour < 14) return "午安,记得休息";
|
|
if (hour < 18) return "下午好,继续加油";
|
|
return "晚安,好梦相伴";
|
|
});
|
|
|
|
const categories = [
|
|
{ id: "vitality", name: "元气", emoji: "😊", bg: "#fff8e1" },
|
|
{ id: "luck", name: "好运", emoji: "🍀", bg: "#e8f5e9" },
|
|
{ id: "gentle", name: "温柔", emoji: "❤️", bg: "#ffebee" },
|
|
{ id: "work", name: "工作", emoji: "💼", bg: "#e3f2fd" },
|
|
{ id: "funny", name: "搞笑", emoji: "😂", bg: "#fff3e0" },
|
|
];
|
|
|
|
const currentCategory = ref("vitality");
|
|
|
|
const quotes = {
|
|
vitality: [
|
|
{
|
|
text: "愿你眼中有星辰\n心中有山海\n每一步都走在",
|
|
highlight: "开满鲜花的路上",
|
|
author: "陈小明",
|
|
},
|
|
{
|
|
text: "生活原本沉闷\n但跑起来就有风",
|
|
highlight: "做自己的小太阳",
|
|
author: "李华",
|
|
},
|
|
],
|
|
luck: [
|
|
{
|
|
text: "好运正在派送中\n请保持心情舒畅",
|
|
highlight: "万事顺遂",
|
|
author: "锦鲤",
|
|
},
|
|
],
|
|
gentle: [
|
|
{
|
|
text: "温柔是宝藏\n你也是",
|
|
highlight: "岁月静好",
|
|
author: "微风",
|
|
},
|
|
],
|
|
work: [
|
|
{
|
|
text: "打工人的意志\n是钢铁铸成的",
|
|
highlight: "搞钱要紧",
|
|
author: "打工人",
|
|
},
|
|
],
|
|
funny: [
|
|
{
|
|
text: "间歇性踌躇满志\n持续性混吃等死",
|
|
highlight: "这就是人生",
|
|
author: "咸鱼",
|
|
},
|
|
],
|
|
};
|
|
|
|
const currentQuote = ref(quotes.vitality[0]);
|
|
const authorName = ref("");
|
|
|
|
// Remove automatic sync with quote author to allow custom input to persist
|
|
// watch(
|
|
// currentQuote,
|
|
// (newVal) => {
|
|
// authorName.value = newVal.author;
|
|
// },
|
|
// { deep: true }
|
|
// );
|
|
|
|
const hotList = ref([
|
|
{ text: "新的一年,愿灵马带走烦恼...", count: "2.4" },
|
|
{ text: "事事顺意,岁岁平安。", count: "1.8" },
|
|
]);
|
|
|
|
const selectCategory = (id) => {
|
|
currentCategory.value = id;
|
|
refreshQuote();
|
|
};
|
|
|
|
const refreshQuote = () => {
|
|
const list = quotes[currentCategory.value] || quotes.vitality;
|
|
const randomIndex = Math.floor(Math.random() * list.length);
|
|
currentQuote.value = list[randomIndex];
|
|
};
|
|
|
|
const useHotItem = (item) => {
|
|
currentQuote.value = {
|
|
text: item.text,
|
|
highlight: "",
|
|
author: "热榜推荐",
|
|
};
|
|
};
|
|
|
|
const saveCard = () => {
|
|
uni.showToast({ title: "保存功能开发中", icon: "none" });
|
|
};
|
|
|
|
const sendGreeting = () => {
|
|
uni.navigateTo({
|
|
url: `/pages/make/index?scene=daily&content=${encodeURIComponent(
|
|
currentQuote.value.text,
|
|
)}&author=${encodeURIComponent(authorName.value)}`,
|
|
});
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.daily-page {
|
|
min-height: 100vh;
|
|
background: #fbfbf9;
|
|
padding-bottom: 200rpx;
|
|
}
|
|
|
|
.page-header {
|
|
padding: 0 32rpx;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 15rpx;
|
|
}
|
|
|
|
.header-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12rpx;
|
|
}
|
|
|
|
.header-title {
|
|
font-size: 36rpx;
|
|
font-weight: 800;
|
|
color: #333;
|
|
}
|
|
|
|
.streak-badge {
|
|
display: flex;
|
|
align-items: center;
|
|
background: #fff;
|
|
padding: 6rpx 20rpx;
|
|
border-radius: 30rpx;
|
|
font-size: 22rpx;
|
|
color: #666;
|
|
border: 1rpx solid #eee;
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
|
|
|
.streak-count {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #ff3b30;
|
|
margin: 0 4rpx;
|
|
}
|
|
|
|
.fire-icon {
|
|
margin-left: 4rpx;
|
|
font-size: 24rpx;
|
|
}
|
|
}
|
|
|
|
.sub-header {
|
|
padding: 0 32rpx;
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-bottom: 40rpx;
|
|
}
|
|
|
|
/* Main Card */
|
|
.main-card-container {
|
|
padding: 0 40rpx;
|
|
margin-bottom: 60rpx;
|
|
}
|
|
|
|
.quote-card {
|
|
background: #fff;
|
|
border-radius: 40rpx;
|
|
padding: 60rpx 40rpx 40rpx;
|
|
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.08);
|
|
border: 4rpx solid #f5e6d3; /* Gold-ish border */
|
|
position: relative;
|
|
min-height: 600rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
/* Inner Border Effect */
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 12rpx;
|
|
border: 2rpx solid #f9f3e8;
|
|
border-radius: 32rpx;
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
|
|
.quote-icon {
|
|
font-size: 80rpx;
|
|
color: #f5e6d3;
|
|
margin-bottom: 40rpx;
|
|
line-height: 1;
|
|
}
|
|
|
|
.quote-content {
|
|
text-align: center;
|
|
margin-bottom: 60rpx;
|
|
}
|
|
|
|
.quote-text {
|
|
font-size: 40rpx;
|
|
color: #333;
|
|
font-weight: bold;
|
|
line-height: 1.6;
|
|
font-family: "Songti SC", serif;
|
|
display: block;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.quote-highlight {
|
|
display: block;
|
|
font-size: 44rpx;
|
|
color: #d81e06;
|
|
font-weight: 800;
|
|
margin-top: 20rpx;
|
|
font-family: "Songti SC", serif;
|
|
}
|
|
|
|
.quote-divider {
|
|
width: 80rpx;
|
|
height: 2rpx;
|
|
background: #eee;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.quote-author {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-bottom: 60rpx;
|
|
font-style: italic;
|
|
}
|
|
|
|
.card-actions {
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
z-index: 2;
|
|
}
|
|
|
|
.refresh-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12rpx;
|
|
padding: 16rpx 32rpx;
|
|
background: #fff;
|
|
border: 1rpx solid #eee;
|
|
border-radius: 40rpx;
|
|
font-size: 26rpx;
|
|
color: #666;
|
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
|
|
&:active {
|
|
background: #f5f5f5;
|
|
}
|
|
}
|
|
|
|
.author-edit-box {
|
|
flex: 1;
|
|
margin-left: 24rpx;
|
|
background: #f9f9f9;
|
|
border: 2rpx solid #eee;
|
|
border-radius: 40rpx;
|
|
height: 80rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 24rpx;
|
|
transition: all 0.3s ease;
|
|
|
|
&:active,
|
|
&:focus-within {
|
|
background: #fff;
|
|
border-color: #ff9800;
|
|
box-shadow: 0 4rpx 16rpx rgba(255, 152, 0, 0.15);
|
|
}
|
|
|
|
.label {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-right: 12rpx;
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.input {
|
|
flex: 1;
|
|
height: 100%;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
}
|
|
}
|
|
|
|
/* Categories */
|
|
.category-section {
|
|
padding: 0 32rpx;
|
|
margin-bottom: 60rpx;
|
|
}
|
|
|
|
.category-grid {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.category-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
.cat-icon-box {
|
|
width: 100rpx;
|
|
height: 100rpx;
|
|
border-radius: 30rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: all 0.2s;
|
|
border: 2rpx solid transparent;
|
|
|
|
&.active {
|
|
transform: translateY(-4rpx);
|
|
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.08);
|
|
border-color: rgba(0, 0, 0, 0.05);
|
|
}
|
|
}
|
|
|
|
.cat-emoji {
|
|
font-size: 48rpx;
|
|
}
|
|
|
|
.cat-name {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
font-weight: 500;
|
|
|
|
&.active {
|
|
color: #333;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
|
|
/* Hot List */
|
|
.hot-list-section {
|
|
padding: 0 32rpx;
|
|
margin-bottom: 40rpx;
|
|
}
|
|
|
|
.section-title-row {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.title-bar {
|
|
width: 8rpx;
|
|
height: 32rpx;
|
|
background: #d4a017;
|
|
border-radius: 4rpx;
|
|
margin-right: 16rpx;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-right: 16rpx;
|
|
}
|
|
|
|
.section-subtitle {
|
|
font-size: 20rpx;
|
|
color: #ccc;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.hot-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 24rpx;
|
|
}
|
|
|
|
.hot-item {
|
|
background: #fff;
|
|
border-radius: 24rpx;
|
|
padding: 24rpx 32rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.02);
|
|
|
|
&:active {
|
|
background: #f9f9f9;
|
|
}
|
|
}
|
|
|
|
.rank-icon {
|
|
margin-right: 24rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.hot-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.hot-text {
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
margin-bottom: 8rpx;
|
|
display: block;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 450rpx;
|
|
}
|
|
|
|
.hot-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 22rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.fire {
|
|
color: #ff3b30;
|
|
margin-right: 4rpx;
|
|
font-size: 20rpx;
|
|
}
|
|
|
|
/* Bottom Actions */
|
|
.bottom-actions {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: #fff; /* Glass effect if supported */
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(20rpx);
|
|
padding: 20rpx 32rpx;
|
|
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
|
z-index: 100;
|
|
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
|
}
|
|
|
|
.action-btn-group {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 24rpx;
|
|
}
|
|
|
|
.save-btn {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: transparent;
|
|
padding: 0;
|
|
margin: 0;
|
|
line-height: 1;
|
|
border: none;
|
|
width: 100rpx;
|
|
|
|
&::after {
|
|
border: none;
|
|
}
|
|
|
|
.icon-circle {
|
|
width: 80rpx;
|
|
height: 80rpx;
|
|
background: #f5f5f5;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 8rpx;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.btn-text {
|
|
font-size: 20rpx;
|
|
color: #666;
|
|
font-weight: 500;
|
|
}
|
|
|
|
&:active .icon-circle {
|
|
background: #eee;
|
|
transform: scale(0.95);
|
|
}
|
|
}
|
|
|
|
.send-btn {
|
|
flex: 1;
|
|
height: 96rpx;
|
|
border-radius: 48rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 16rpx;
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
border: none;
|
|
background: #8e0000; /* Deep Red/Brown */
|
|
color: #fff;
|
|
background: linear-gradient(135deg, #8e0000 0%, #600000 100%);
|
|
box-shadow: 0 8rpx 24rpx rgba(142, 0, 0, 0.3);
|
|
transition: all 0.2s;
|
|
|
|
&::after {
|
|
border: none;
|
|
}
|
|
|
|
&:active {
|
|
transform: scale(0.98);
|
|
box-shadow: 0 4rpx 12rpx rgba(142, 0, 0, 0.2);
|
|
}
|
|
}
|
|
</style>
|