Oroqen-Vue/src/views/Home.vue
2025-08-10 20:42:49 +08:00

784 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="home">
<!-- 英雄区域 -->
<section class="hero">
<div class="hero-background ethnic-pattern">
<div class="container">
<div class="hero-content">
<div class="hero-text">
<h1 class="hero-title">
传承千年文化
<br>
<span class="highlight">鄂伦春族的瑰宝</span>
</h1>
<p class="hero-description">
探索中国东北森林深处的古老民族文化感受狩猎文明的独特魅力
体验桦皮工艺传统刺绣等非物质文化遗产的精湛技艺
</p>
<div class="hero-actions">
<router-link to="/culture" class="btn-primary hero-btn">
探索文化
</router-link>
<router-link to="/products" class="btn-secondary hero-btn">
精品商城
</router-link>
</div>
</div>
<div class="hero-image">
<div class="hero-image-placeholder">
<svg viewBox="0 0 400 300" class="hero-svg">
<!-- 森林背景 -->
<rect width="400" height="300" fill="#2d5016" opacity="0.1"/>
<!-- 传统建筑轮廓 -->
<path d="M50 250 L150 180 L250 250 L350 180 L350 280 L50 280 Z"
fill="var(--secondary-color)" opacity="0.6"/>
<!-- 装饰图案 -->
<circle cx="200" cy="150" r="40" fill="var(--primary-color)" opacity="0.3"/>
<text x="200" y="160" text-anchor="middle" fill="var(--primary-color)"
font-size="24" font-weight="bold"></text>
</svg>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 特色板块 -->
<section class="features section">
<div class="container">
<h2 class="section-title">文化瑰宝</h2>
<div class="features-grid">
<div class="feature-card card" @click="$router.push('/culture')">
<div class="feature-icon">
<svg viewBox="0 0 64 64" width="48" height="48">
<circle cx="32" cy="32" r="28" fill="var(--primary-color)" opacity="0.2"/>
<path d="M20 32 L32 20 L44 32 L32 44 Z" fill="var(--primary-color)"/>
</svg>
</div>
<h3 class="feature-title">传统文化</h3>
<p class="feature-description">
深入了解鄂伦春族的狩猎文化节庆仪式饮食习俗和居住文化
</p>
</div>
<div class="feature-card card" @click="$router.push('/heritage')">
<div class="feature-icon">
<svg viewBox="0 0 64 64" width="48" height="48">
<circle cx="32" cy="32" r="28" fill="var(--secondary-color)" opacity="0.2"/>
<rect x="24" y="24" width="16" height="16" fill="var(--secondary-color)"/>
</svg>
</div>
<h3 class="feature-title">非遗传承</h3>
<p class="feature-description">
桦皮制作技艺传统刺绣民族歌舞等非物质文化遗产的传承与保护
</p>
</div>
<div class="feature-card card" @click="$router.push('/products')">
<div class="feature-icon">
<svg viewBox="0 0 64 64" width="48" height="48">
<circle cx="32" cy="32" r="28" fill="var(--accent-color)" opacity="0.3"/>
<path d="M16 24 L48 24 L44 48 L20 48 Z" fill="var(--primary-color)"/>
</svg>
</div>
<h3 class="feature-title">特色产品</h3>
<p class="feature-description">
精美手工艺品传统服饰特色食品和现代文创产品
</p>
</div>
</div>
</div>
</section>
<!-- 热门产品 -->
<section class="popular-products section" style="background-color: var(--background-light);">
<div class="container">
<h2 class="section-title">热门产品</h2>
<div class="products-grid">
<div v-for="product in featuredProducts" :key="product.id"
class="product-card card" @click="$router.push(`/product/${product.id}`)">
<div class="product-image">
<div class="product-image-placeholder">
<svg viewBox="0 0 200 150" width="100%" height="150">
<rect width="200" height="150" :fill="product.color" opacity="0.3"/>
<text x="100" y="80" text-anchor="middle" :fill="product.color"
font-size="14" font-weight="bold">{{ product.name }}</text>
</svg>
</div>
</div>
<div class="product-info">
<h3 class="product-name">{{ product.name }}</h3>
<p class="product-description">{{ product.description }}</p>
<div class="product-footer">
<span class="product-price">¥{{ product.price }}</span>
<el-button type="primary" size="small" @click.stop="addToCart(product)">
加入购物车
</el-button>
</div>
</div>
</div>
</div>
<div class="text-center" style="margin-top: 2rem;">
<router-link to="/products" class="btn-primary">查看更多产品</router-link>
</div>
</div>
</section>
<!-- 文化故事 -->
<section class="culture-stories section">
<div class="container">
<h2 class="section-title">文化故事</h2>
<div class="stories-grid">
<div v-for="story in stories" :key="story.id" class="story-card card">
<div class="story-image">
<div class="story-image-placeholder">
<svg viewBox="0 0 300 200" width="100%" height="200">
<rect width="300" height="200" :fill="story.color" opacity="0.2"/>
<circle cx="150" cy="100" r="40" :fill="story.color" opacity="0.5"/>
<text x="150" y="110" text-anchor="middle" :fill="story.color"
font-size="16" font-weight="bold">{{ story.title.charAt(0) }}</text>
</svg>
</div>
</div>
<div class="story-content">
<h3 class="story-title">{{ story.title }}</h3>
<p class="story-excerpt">{{ story.excerpt }}</p>
<div class="story-meta">
<span class="story-date">{{ story.date }}</span>
<a href="#" @click.prevent="showStoryDetail(story)" class="story-link">阅读更多</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 统计数据 -->
<section class="stats section" style="background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));">
<div class="container">
<div class="stats-grid">
<div class="stat-item">
<div class="stat-number">1000+</div>
<div class="stat-label">文化资料</div>
</div>
<div class="stat-item">
<div class="stat-number">50+</div>
<div class="stat-label">非遗项目</div>
</div>
<div class="stat-item">
<div class="stat-number">200+</div>
<div class="stat-label">特色产品</div>
</div>
<div class="stat-item">
<div class="stat-number">10000+</div>
<div class="stat-label">用户关注</div>
</div>
</div>
</div>
</section>
</div>
<!-- 文化故事详情卡片 -->
<el-dialog
v-model="storyDetailVisible"
:title="selectedStory?.title"
width="70%"
top="5vh"
class="story-detail-dialog"
>
<div v-if="selectedStory" class="story-detail-content">
<!-- 故事头部信息 -->
<div class="story-header">
<div class="story-image-placeholder">
<svg width="100%" height="200" viewBox="0 0 400 200">
<rect width="400" height="200" :fill="selectedStory.color" opacity="0.2"/>
<circle cx="200" cy="100" r="50" :fill="selectedStory.color" opacity="0.5"/>
<text x="200" y="110" text-anchor="middle" :fill="selectedStory.color"
font-size="20" font-weight="bold">{{ selectedStory.title.substring(0, 2) }}</text>
</svg>
</div>
<div class="story-meta-info">
<p class="story-date">发布时间:{{ selectedStory.date }}</p>
<p class="story-category" v-if="selectedStory.categoryName">分类:{{ selectedStory.categoryName }}</p>
</div>
</div>
<!-- 故事内容 -->
<div class="story-content-section">
<h3>内容简介</h3>
<p class="story-description">{{ selectedStory.excerpt }}</p>
<div v-if="storyDetailData.content" class="story-full-content">
<h3>详细内容</h3>
<div class="content-text" v-html="storyDetailData.content"></div>
</div>
<div v-if="storyDetailData.tags" class="story-tags">
<h3>相关标签</h3>
<div class="tags-container">
<span class="tag" v-for="tag in parseTags(storyDetailData.tags)" :key="tag">
{{ tag }}
</span>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="story-actions">
<el-button type="primary" @click="goToFullDetail">查看完整详情</el-button>
<el-button @click="storyDetailVisible = false">关闭</el-button>
</div>
</div>
<div v-if="storyLoading" class="loading-container">
<el-icon class="is-loading"><Loading /></el-icon>
<p>加载中...</p>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useMainStore } from '../stores'
import { ElMessage } from 'element-plus'
import { Loading } from '@element-plus/icons-vue'
import { productApi } from '../api/product'
import { cultureApi } from '../api/culture'
import { cartApi } from '../api/cart'
const router = useRouter()
const store = useMainStore()
// 特色产品数据
const featuredProducts = ref<any[]>([])
const loading = ref(false)
// 文化故事数据
const stories = ref<any[]>([])
// 故事详情相关
const storyDetailVisible = ref(false)
const selectedStory = ref<any>(null)
const storyDetailData = ref<any>({})
const storyLoading = ref(false)
// 获取热门产品
const getFeaturedProducts = async () => {
try {
loading.value = true
const response = await productApi.getProductList({
pageNo: 1,
pageSize: 4,
status: 1 // 只获取上架的产品
})
if (response && response.records) {
featuredProducts.value = response.records.map((product: any) => ({
...product,
color: getRandomColor()
}))
}
} catch (error) {
console.error('获取产品数据失败:', error)
ElMessage.error('获取产品数据失败,请稍后重试')
featuredProducts.value = []
} finally {
loading.value = false
}
}
// 获取推荐文化内容
const getRecommendedStories = async () => {
try {
const response = await cultureApi.getRecommendedCulture()
if (response && response.length > 0) {
stories.value = response.slice(0, 3).map((story: any) => ({
id: story.id,
title: story.title,
excerpt: story.content ? story.content.substring(0, 100) + '...' : story.description,
date: story.createTime ? new Date(story.createTime).toLocaleDateString() : '2024-01-01',
color: getRandomColor()
}))
}
} catch (error) {
console.error('获取文化内容失败:', error)
ElMessage.error('获取文化内容失败,请稍后重试')
stories.value = []
}
}
// 获取随机颜色
const getRandomColor = () => {
const colors = ['#8b4513', '#2d5016', '#8b4513', '#2d5016']
return colors[Math.floor(Math.random() * colors.length)]
}
// 添加到购物车
const addToCart = async (product: any) => {
try {
// 这里使用默认用户ID实际应用中应该从用户状态获取
const userId = '1'
await cartApi.addToCart(userId, product.id, 1)
store.addToCart(product)
ElMessage.success('已添加到购物车')
} catch (error) {
console.error('添加到购物车失败:', error)
ElMessage.error('添加到购物车失败,请稍后重试')
}
}
// 显示故事详情
const showStoryDetail = async (story: any) => {
selectedStory.value = story
storyDetailVisible.value = true
storyLoading.value = true
try {
// 获取完整的故事详情
const response = await cultureApi.getCultureById(story.id)
if (response) {
storyDetailData.value = response
}
} catch (error) {
console.error('获取故事详情失败:', error)
ElMessage.error('获取故事详情失败,请稍后重试')
} finally {
storyLoading.value = false
}
}
// 跳转到完整详情页
const goToFullDetail = () => {
if (selectedStory.value) {
router.push(`/culture/${selectedStory.value.id}`)
storyDetailVisible.value = false
}
}
// 解析标签
const parseTags = (tags: string) => {
if (!tags) return []
return tags.split(',').map(tag => tag.trim()).filter(tag => tag)
}
// 组件挂载时获取数据
onMounted(() => {
getFeaturedProducts()
getRecommendedStories()
})
</script>
<style scoped>
/* 英雄区域 */
.hero {
position: relative;
min-height: 600px;
display: flex;
align-items: center;
overflow: hidden;
}
.hero-background {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(45, 80, 22, 0.9), rgba(139, 69, 19, 0.8));
}
.hero-content {
position: relative;
z-index: 2;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
align-items: center;
min-height: 500px;
}
.hero-title {
font-size: 3.5rem;
font-weight: bold;
color: white;
margin-bottom: 1.5rem;
line-height: 1.2;
}
.highlight {
color: var(--accent-color);
}
.hero-description {
font-size: 1.2rem;
color: rgba(255, 255, 255, 0.9);
margin-bottom: 2rem;
line-height: 1.6;
}
.hero-actions {
display: flex;
gap: 1rem;
}
.hero-btn {
padding: 15px 30px;
font-size: 1.1rem;
text-decoration: none;
border-radius: 8px;
transition: all 0.3s ease;
}
.btn-secondary {
background: transparent;
color: white;
border: 2px solid white;
}
.btn-secondary:hover {
background: white;
color: var(--primary-color);
}
.hero-image-placeholder {
width: 100%;
height: 400px;
background: rgba(255, 255, 255, 0.1);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.hero-svg {
width: 100%;
height: 100%;
}
/* 特色板块 */
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
.feature-card {
padding: 2rem;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
}
.feature-card:hover {
transform: translateY(-10px);
}
.feature-icon {
margin-bottom: 1.5rem;
display: flex;
justify-content: center;
}
.feature-title {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-color);
margin-bottom: 1rem;
}
.feature-description {
color: var(--text-light);
line-height: 1.6;
}
/* 产品网格 */
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
}
.product-card {
cursor: pointer;
transition: all 0.3s ease;
}
.product-image-placeholder {
width: 100%;
height: 150px;
background: var(--background-light);
border-radius: 8px 8px 0 0;
display: flex;
align-items: center;
justify-content: center;
}
.product-info {
padding: 1.5rem;
}
.product-name {
font-size: 1.2rem;
font-weight: bold;
color: var(--primary-color);
margin-bottom: 0.5rem;
}
.product-description {
color: var(--text-light);
margin-bottom: 1rem;
font-size: 0.9rem;
}
.product-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.product-price {
font-size: 1.3rem;
font-weight: bold;
color: var(--secondary-color);
}
/* 文化故事 */
.stories-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 2rem;
}
.story-card {
overflow: hidden;
}
.story-image-placeholder {
width: 100%;
height: 200px;
background: var(--background-light);
}
.story-content {
padding: 1.5rem;
}
.story-title {
font-size: 1.3rem;
font-weight: bold;
color: var(--primary-color);
margin-bottom: 1rem;
}
.story-excerpt {
color: var(--text-light);
line-height: 1.6;
margin-bottom: 1rem;
}
.story-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.story-date {
color: var(--text-light);
font-size: 0.9rem;
}
.story-link {
color: var(--primary-color);
text-decoration: none;
font-weight: 500;
}
.story-link:hover {
text-decoration: underline;
}
/* 统计数据 */
.stats {
color: white;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 2rem;
}
.stat-item {
text-align: center;
}
.stat-number {
font-size: 3rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.stat-label {
font-size: 1.1rem;
opacity: 0.9;
}
.text-center {
text-align: center;
}
/* 故事详情对话框样式 */
.story-detail-dialog :deep(.el-dialog) {
border-radius: 12px;
}
.story-detail-content {
max-height: 70vh;
overflow-y: auto;
}
.story-header {
margin-bottom: 2rem;
}
.story-image-placeholder {
width: 100%;
margin-bottom: 1rem;
border-radius: 8px;
overflow: hidden;
}
.story-meta-info {
display: flex;
gap: 2rem;
color: var(--text-light);
font-size: 0.9rem;
}
.story-content-section {
margin-bottom: 2rem;
}
.story-content-section h3 {
color: var(--primary-color);
font-size: 1.2rem;
margin-bottom: 1rem;
border-bottom: 2px solid var(--primary-color);
padding-bottom: 0.5rem;
}
.story-description {
color: var(--text-color);
line-height: 1.6;
margin-bottom: 1.5rem;
}
.story-full-content {
margin-top: 1.5rem;
}
.content-text {
color: var(--text-color);
line-height: 1.8;
font-size: 1rem;
}
.story-tags {
margin-top: 1.5rem;
}
.tags-container {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.tag {
background: var(--primary-color);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 15px;
font-size: 0.8rem;
font-weight: 500;
}
.story-actions {
display: flex;
justify-content: flex-end;
gap: 1rem;
margin-top: 2rem;
padding-top: 1rem;
border-top: 1px solid var(--border-color);
}
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 3rem;
color: var(--text-light);
}
.loading-container .el-icon {
font-size: 2rem;
margin-bottom: 1rem;
}
/* 响应式设计 */
@media (max-width: 768px) {
.hero-content {
grid-template-columns: 1fr;
text-align: center;
gap: 2rem;
}
.hero-title {
font-size: 2.5rem;
}
.hero-actions {
justify-content: center;
}
.features-grid,
.products-grid,
.stories-grid {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
.story-detail-dialog :deep(.el-dialog) {
width: 95% !important;
margin: 5vh auto;
}
.story-meta-info {
flex-direction: column;
gap: 0.5rem;
}
.story-actions {
flex-direction: column;
}
}
@media (max-width: 480px) {
.hero-title {
font-size: 2rem;
}
.hero-actions {
flex-direction: column;
align-items: center;
}
.stats-grid {
grid-template-columns: 1fr;
}
}
</style>