forked from Qi/Oroqen-Vue
初始化
This commit is contained in:
commit
eb7ced99d4
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
1
.vercel/project.json
Normal file
1
.vercel/project.json
Normal file
@ -0,0 +1 @@
|
||||
{"projectName":"trae_r1dylz8j"}
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Vue 3 + TypeScript + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
|
9
auto-imports.d.ts
vendored
Normal file
9
auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
|
||||
}
|
33
components.d.ts
vendored
Normal file
33
components.d.ts
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElBadge: typeof import('element-plus/es')['ElBadge']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
Footer: typeof import('./src/components/Footer.vue')['default']
|
||||
Header: typeof import('./src/components/Header.vue')['default']
|
||||
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Vue + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
4031
package-lock.json
generated
Normal file
4031
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
package.json
Normal file
28
package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "oroqen-vue",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.18",
|
||||
"vue-router": "^4.2.5",
|
||||
"pinia": "^2.1.7",
|
||||
"axios": "^1.6.2",
|
||||
"element-plus": "^2.4.4",
|
||||
"@element-plus/icons-vue": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"typescript": "~5.4.0",
|
||||
"vite": "^5.2.0",
|
||||
"vue-tsc": "^2.0.6",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"unplugin-auto-import": "^0.16.7"
|
||||
}
|
||||
}
|
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
34
src/App.vue
Normal file
34
src/App.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<Header />
|
||||
<main class="main-content">
|
||||
<router-view />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import Header from './components/Header.vue'
|
||||
import Footer from './components/Footer.vue'
|
||||
import { useMainStore } from './stores'
|
||||
|
||||
const store = useMainStore()
|
||||
|
||||
onMounted(() => {
|
||||
store.init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
1
src/assets/vue.svg
Normal file
1
src/assets/vue.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
After Width: | Height: | Size: 496 B |
295
src/components/Footer.vue
Normal file
295
src/components/Footer.vue
Normal file
@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<div class="footer-main">
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<!-- 关于我们 -->
|
||||
<div class="footer-section">
|
||||
<h3 class="footer-title">鄂伦春族文化传承平台</h3>
|
||||
<p class="footer-desc">
|
||||
致力于保护和传承鄂伦春族传统文化,展示民族瑰宝,
|
||||
为手工艺人提供展示平台,让更多人了解和喜爱鄂伦春族文化。
|
||||
</p>
|
||||
<div class="social-links">
|
||||
<a href="#" class="social-link">
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
</a>
|
||||
<a href="#" class="social-link">
|
||||
<el-icon><VideoCamera /></el-icon>
|
||||
</a>
|
||||
<a href="#" class="social-link">
|
||||
<el-icon><Share /></el-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速链接 -->
|
||||
<div class="footer-section">
|
||||
<h4 class="section-title">文化展示</h4>
|
||||
<ul class="footer-links">
|
||||
<li><router-link to="/culture">传统文化</router-link></li>
|
||||
<li><router-link to="/heritage">非遗传承</router-link></li>
|
||||
<li><a href="#traditions">民族习俗</a></li>
|
||||
<li><a href="#crafts">传统工艺</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer-section">
|
||||
<h4 class="section-title">产品服务</h4>
|
||||
<ul class="footer-links">
|
||||
<li><router-link to="/products">特色产品</router-link></li>
|
||||
<li><a href="#handicrafts">手工艺品</a></li>
|
||||
<li><a href="#food">特色食品</a></li>
|
||||
<li><a href="#cultural-products">文创产品</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer-section">
|
||||
<h4 class="section-title">帮助中心</h4>
|
||||
<ul class="footer-links">
|
||||
<li><a href="#shipping">配送说明</a></li>
|
||||
<li><a href="#returns">退换货政策</a></li>
|
||||
<li><a href="#payment">支付方式</a></li>
|
||||
<li><a href="#contact">联系我们</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 联系信息 -->
|
||||
<div class="footer-section">
|
||||
<h4 class="section-title">联系我们</h4>
|
||||
<div class="contact-info">
|
||||
<div class="contact-item">
|
||||
<el-icon><Location /></el-icon>
|
||||
<span>内蒙古自治区呼伦贝尔市</span>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<el-icon><Phone /></el-icon>
|
||||
<span>400-123-4567</span>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<el-icon><Message /></el-icon>
|
||||
<span>contact@oroqen-culture.com</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 版权信息 -->
|
||||
<div class="footer-bottom">
|
||||
<div class="container">
|
||||
<div class="footer-bottom-content">
|
||||
<div class="copyright">
|
||||
<p>© 2024 鄂伦春族文化传承平台. 保留所有权利.</p>
|
||||
<p>弘扬民族文化 · 传承非遗技艺 · 促进文化交流</p>
|
||||
</div>
|
||||
<div class="footer-links-bottom">
|
||||
<a href="#privacy">隐私政策</a>
|
||||
<a href="#terms">服务条款</a>
|
||||
<a href="#sitemap">网站地图</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ChatDotRound,
|
||||
VideoCamera,
|
||||
Share,
|
||||
Location,
|
||||
Phone,
|
||||
Message
|
||||
} from '@element-plus/icons-vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.footer {
|
||||
background: linear-gradient(135deg, var(--primary-color), #1a3d0f);
|
||||
color: white;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.footer-main {
|
||||
padding: 3rem 0 2rem;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr 1fr 1.5fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.footer-section h3.footer-title {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.footer-section h4.section-title {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--accent-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer-desc {
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1.5rem;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.social-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.social-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.social-link:hover {
|
||||
background: var(--secondary-color);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
text-decoration: none;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.contact-item .el-icon {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 1.5rem 0;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.footer-bottom-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.copyright p {
|
||||
margin: 0;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.copyright p:first-child {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.footer-links-bottom {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.footer-links-bottom a {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-links-bottom a:hover {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1024px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.footer-section:first-child {
|
||||
grid-column: 1 / -1;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.footer-bottom-content {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-links-bottom {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.footer-main {
|
||||
padding: 2rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer-links-bottom {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
369
src/components/Header.vue
Normal file
369
src/components/Header.vue
Normal file
@ -0,0 +1,369 @@
|
||||
<template>
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<!-- Logo -->
|
||||
<div class="logo">
|
||||
<router-link to="/" class="logo-link">
|
||||
<div class="logo-icon">鄂</div>
|
||||
<div class="logo-text">
|
||||
<div class="logo-title">鄂伦春族</div>
|
||||
<div class="logo-subtitle">文化传承平台</div>
|
||||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<!-- 导航菜单 -->
|
||||
<nav class="nav" :class="{ 'nav-open': mobileMenuOpen }">
|
||||
<router-link to="/" class="nav-link" @click="closeMobileMenu">首页</router-link>
|
||||
<router-link to="/culture" class="nav-link" @click="closeMobileMenu">传统文化</router-link>
|
||||
<router-link to="/heritage" class="nav-link" @click="closeMobileMenu">非遗传承</router-link>
|
||||
<router-link to="/products" class="nav-link" @click="closeMobileMenu">特色产品</router-link>
|
||||
<router-link to="/about" class="nav-link" @click="closeMobileMenu">关于我们</router-link>
|
||||
</nav>
|
||||
|
||||
<!-- 用户操作区 -->
|
||||
<div class="user-actions">
|
||||
<!-- 搜索 -->
|
||||
<div class="search-box">
|
||||
<el-input
|
||||
v-model="searchQuery"
|
||||
placeholder="搜索文化内容或产品"
|
||||
@keyup.enter="handleSearch"
|
||||
class="search-input"
|
||||
>
|
||||
<template #suffix>
|
||||
<el-icon @click="handleSearch" class="search-icon">
|
||||
<Search />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<!-- 购物车 -->
|
||||
<router-link to="/cart" class="cart-link">
|
||||
<el-badge :value="cartItemCount" :hidden="cartItemCount === 0">
|
||||
<el-icon size="24">
|
||||
<ShoppingCart />
|
||||
</el-icon>
|
||||
</el-badge>
|
||||
</router-link>
|
||||
|
||||
<!-- 用户菜单 -->
|
||||
<div class="user-menu">
|
||||
<el-dropdown v-if="isLoggedIn" @command="handleUserCommand">
|
||||
<div class="user-avatar">
|
||||
<el-avatar :src="user?.avatar" :size="32">
|
||||
{{ user?.username?.charAt(0) }}
|
||||
</el-avatar>
|
||||
<span class="username">{{ user?.username }}</span>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="profile">个人中心</el-dropdown-item>
|
||||
<el-dropdown-item command="orders">我的订单</el-dropdown-item>
|
||||
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button v-else @click="showLoginDialog = true" type="primary" size="small">
|
||||
登录
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 移动端菜单按钮 -->
|
||||
<button class="mobile-menu-btn" @click="toggleMobileMenu">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 登录对话框 -->
|
||||
<el-dialog v-model="showLoginDialog" title="用户登录" width="400px">
|
||||
<el-form :model="loginForm" label-width="80px">
|
||||
<el-form-item label="用户名">
|
||||
<el-input v-model="loginForm.username" placeholder="请输入用户名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input v-model="loginForm.password" type="password" placeholder="请输入密码" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showLoginDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleLogin">登录</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMainStore } from '../stores'
|
||||
import { Search, ShoppingCart } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useMainStore()
|
||||
|
||||
// 响应式数据
|
||||
const searchQuery = ref('')
|
||||
const mobileMenuOpen = ref(false)
|
||||
const showLoginDialog = ref(false)
|
||||
const loginForm = ref({
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
// 计算属性
|
||||
const isLoggedIn = computed(() => store.isLoggedIn)
|
||||
const user = computed(() => store.user)
|
||||
const cartItemCount = computed(() => store.cartItemCount)
|
||||
|
||||
// 方法
|
||||
const handleSearch = () => {
|
||||
if (searchQuery.value.trim()) {
|
||||
router.push(`/products?search=${encodeURIComponent(searchQuery.value)}`)
|
||||
searchQuery.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const toggleMobileMenu = () => {
|
||||
mobileMenuOpen.value = !mobileMenuOpen.value
|
||||
}
|
||||
|
||||
const closeMobileMenu = () => {
|
||||
mobileMenuOpen.value = false
|
||||
}
|
||||
|
||||
const handleUserCommand = (command: string) => {
|
||||
switch (command) {
|
||||
case 'profile':
|
||||
router.push('/profile')
|
||||
break
|
||||
case 'orders':
|
||||
router.push('/profile?tab=orders')
|
||||
break
|
||||
case 'logout':
|
||||
store.logout()
|
||||
ElMessage.success('已退出登录')
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogin = () => {
|
||||
if (!loginForm.value.username || !loginForm.value.password) {
|
||||
ElMessage.warning('请填写完整的登录信息')
|
||||
return
|
||||
}
|
||||
|
||||
// 模拟登录
|
||||
store.login({
|
||||
username: loginForm.value.username,
|
||||
email: `${loginForm.value.username}@example.com`
|
||||
})
|
||||
|
||||
showLoginDialog.value = false
|
||||
loginForm.value = { username: '', password: '' }
|
||||
ElMessage.success('登录成功')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
background: white;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
/* Logo */
|
||||
.logo-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.logo-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.logo-subtitle {
|
||||
font-size: 12px;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
/* 导航 */
|
||||
.nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-link:hover,
|
||||
.nav-link.router-link-active {
|
||||
color: var(--primary-color);
|
||||
background-color: rgba(45, 80, 22, 0.1);
|
||||
}
|
||||
|
||||
/* 用户操作区 */
|
||||
.user-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
cursor: pointer;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.search-icon:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.cart-link {
|
||||
color: var(--text-color);
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.cart-link:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 14px;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 移动端菜单按钮 */
|
||||
.mobile-menu-btn {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.mobile-menu-btn span {
|
||||
width: 20px;
|
||||
height: 2px;
|
||||
background: var(--text-color);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.search-box {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-menu-btn {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nav {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-open {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.username {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.logo-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
</style>
|
12
src/main.ts
Normal file
12
src/main.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import router from './router'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
|
||||
const app = createApp(App)
|
||||
const pinia = createPinia()
|
||||
|
||||
app.use(pinia)
|
||||
app.use(router)
|
||||
app.mount('#app')
|
59
src/router/index.ts
Normal file
59
src/router/index.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Home from '../views/Home.vue'
|
||||
import Culture from '../views/Culture.vue'
|
||||
import Heritage from '../views/Heritage.vue'
|
||||
import Products from '../views/Products.vue'
|
||||
import ProductDetail from '../views/ProductDetail.vue'
|
||||
import Cart from '../views/Cart.vue'
|
||||
import Profile from '../views/Profile.vue'
|
||||
import About from '../views/About.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/culture',
|
||||
name: 'Culture',
|
||||
component: Culture
|
||||
},
|
||||
{
|
||||
path: '/heritage',
|
||||
name: 'Heritage',
|
||||
component: Heritage
|
||||
},
|
||||
{
|
||||
path: '/products',
|
||||
name: 'Products',
|
||||
component: Products
|
||||
},
|
||||
{
|
||||
path: '/product/:id',
|
||||
name: 'ProductDetail',
|
||||
component: ProductDetail
|
||||
},
|
||||
{
|
||||
path: '/cart',
|
||||
name: 'Cart',
|
||||
component: Cart
|
||||
},
|
||||
{
|
||||
path: '/profile',
|
||||
name: 'Profile',
|
||||
component: Profile
|
||||
},
|
||||
{
|
||||
path: '/about',
|
||||
name: 'About',
|
||||
component: About
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
117
src/stores/index.ts
Normal file
117
src/stores/index.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export interface Product {
|
||||
id: number
|
||||
name: string
|
||||
price: number
|
||||
image: string
|
||||
description: string
|
||||
category: string
|
||||
stock: number
|
||||
craftsman?: string
|
||||
story?: string
|
||||
}
|
||||
|
||||
export interface CartItem extends Product {
|
||||
quantity: number
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id?: number
|
||||
username: string
|
||||
email?: string
|
||||
phone?: string
|
||||
avatar?: string
|
||||
}
|
||||
|
||||
export const useMainStore = defineStore('main', {
|
||||
state: () => ({
|
||||
user: null as User | null,
|
||||
cart: [] as CartItem[],
|
||||
isLoggedIn: false,
|
||||
products: [] as Product[],
|
||||
loading: false
|
||||
}),
|
||||
|
||||
getters: {
|
||||
cartTotal: (state) => {
|
||||
return state.cart.reduce((total, item) => total + item.price * item.quantity, 0)
|
||||
},
|
||||
cartItemCount: (state) => {
|
||||
return state.cart.reduce((total, item) => total + item.quantity, 0)
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
// 用户相关
|
||||
login(user: User) {
|
||||
this.user = user
|
||||
this.isLoggedIn = true
|
||||
localStorage.setItem('user', JSON.stringify(user))
|
||||
},
|
||||
|
||||
logout() {
|
||||
this.user = null
|
||||
this.isLoggedIn = false
|
||||
this.cart = []
|
||||
localStorage.removeItem('user')
|
||||
},
|
||||
|
||||
// 购物车相关
|
||||
addToCart(product: Product, quantity: number = 1) {
|
||||
const existingItem = this.cart.find(item => item.id === product.id)
|
||||
if (existingItem) {
|
||||
existingItem.quantity += quantity
|
||||
} else {
|
||||
this.cart.push({ ...product, quantity })
|
||||
}
|
||||
this.saveCart()
|
||||
},
|
||||
|
||||
removeFromCart(productId: number) {
|
||||
const index = this.cart.findIndex(item => item.id === productId)
|
||||
if (index > -1) {
|
||||
this.cart.splice(index, 1)
|
||||
this.saveCart()
|
||||
}
|
||||
},
|
||||
|
||||
updateCartQuantity(productId: number, quantity: number) {
|
||||
const item = this.cart.find(item => item.id === productId)
|
||||
if (item) {
|
||||
item.quantity = quantity
|
||||
if (quantity <= 0) {
|
||||
this.removeFromCart(productId)
|
||||
} else {
|
||||
this.saveCart()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clearCart() {
|
||||
this.cart = []
|
||||
this.saveCart()
|
||||
},
|
||||
|
||||
saveCart() {
|
||||
localStorage.setItem('cart', JSON.stringify(this.cart))
|
||||
},
|
||||
|
||||
loadCart() {
|
||||
const savedCart = localStorage.getItem('cart')
|
||||
if (savedCart) {
|
||||
this.cart = JSON.parse(savedCart)
|
||||
}
|
||||
},
|
||||
|
||||
// 初始化
|
||||
init() {
|
||||
const savedUser = localStorage.getItem('user')
|
||||
if (savedUser) {
|
||||
this.user = JSON.parse(savedUser)
|
||||
this.isLoggedIn = true
|
||||
}
|
||||
this.loadCart()
|
||||
}
|
||||
}
|
||||
})
|
131
src/style.css
Normal file
131
src/style.css
Normal file
@ -0,0 +1,131 @@
|
||||
:root {
|
||||
/* 鄂伦春族主题色彩 */
|
||||
--primary-color: #2d5016; /* 森林绿 */
|
||||
--secondary-color: #8b4513; /* 大地棕 */
|
||||
--accent-color: #f5f5dc; /* 冰雪白 */
|
||||
--text-color: #333;
|
||||
--text-light: #666;
|
||||
--border-color: #e0e0e0;
|
||||
--background-light: #fafafa;
|
||||
--shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color);
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#app {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 通用样式 */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
color: var(--primary-color);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.section-title::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60px;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 12px 24px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--shadow);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.loading::after {
|
||||
content: '';
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid var(--border-color);
|
||||
border-top: 4px solid var(--primary-color);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* 民族元素装饰 */
|
||||
.ethnic-pattern {
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%232d5016' fill-opacity='0.05'%3E%3Cpath d='M30 30c0-11.046-8.954-20-20-20s-20 8.954-20 20 8.954 20 20 20 20-8.954 20-20zm0 0c0 11.046 8.954 20 20 20s20-8.954 20-20-8.954-20-20-20-20 8.954-20 20z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
720
src/views/About.vue
Normal file
720
src/views/About.vue
Normal file
@ -0,0 +1,720 @@
|
||||
<template>
|
||||
<div class="about">
|
||||
<!-- 页面头部 -->
|
||||
<section class="page-header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<h1 class="page-title">关于我们</h1>
|
||||
<p class="page-subtitle">传承鄂伦春族文化,连接传统与现代</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 平台介绍 -->
|
||||
<section class="platform-intro section">
|
||||
<div class="container">
|
||||
<div class="intro-layout">
|
||||
<div class="intro-content">
|
||||
<h2 class="section-title">平台简介</h2>
|
||||
<p class="intro-text">
|
||||
鄂伦春族文化传承与产品销售平台致力于保护、传承和弘扬鄂伦春族深厚的文化底蕴。
|
||||
我们通过数字化手段,系统性地展示鄂伦春族的历史文化、传统习俗、非物质文化遗产,
|
||||
同时为鄂伦春族手工艺人提供产品展示和销售渠道,实现文化传承与经济发展的良性循环。
|
||||
</p>
|
||||
<p class="intro-text">
|
||||
平台汇聚了丰富的文化资源,包括传统狩猎文化、桦皮工艺、刺绣技艺、民族服饰等,
|
||||
每一件展示的产品都承载着深厚的文化内涵和匠人的精湛技艺。我们相信,通过现代科技
|
||||
与传统文化的结合,能够让更多人了解和欣赏鄂伦春族的独特魅力。
|
||||
</p>
|
||||
</div>
|
||||
<div class="intro-visual">
|
||||
<div class="visual-placeholder">
|
||||
<svg viewBox="0 0 400 300" width="100%" height="300">
|
||||
<defs>
|
||||
<pattern id="ethnic-pattern" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<rect width="40" height="40" fill="var(--primary-color)" opacity="0.1"/>
|
||||
<circle cx="20" cy="20" r="8" fill="var(--primary-color)" opacity="0.3"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="400" height="300" fill="url(#ethnic-pattern)"/>
|
||||
<circle cx="200" cy="150" r="60" fill="var(--primary-color)" opacity="0.6"/>
|
||||
<text x="200" y="160" text-anchor="middle" fill="var(--primary-color)"
|
||||
font-size="20" font-weight="bold">鄂伦春</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 我们的使命 -->
|
||||
<section class="mission section">
|
||||
<div class="container">
|
||||
<h2 class="section-title text-center">我们的使命</h2>
|
||||
<div class="mission-grid">
|
||||
<div class="mission-item card">
|
||||
<div class="mission-icon">
|
||||
<el-icon size="40" color="var(--primary-color)"><Collection /></el-icon>
|
||||
</div>
|
||||
<h3>文化保护</h3>
|
||||
<p>通过数字化手段保护和记录鄂伦春族珍贵的文化遗产,为后代留存宝贵的文化财富。</p>
|
||||
</div>
|
||||
<div class="mission-item card">
|
||||
<div class="mission-icon">
|
||||
<el-icon size="40" color="var(--primary-color)"><Share /></el-icon>
|
||||
</div>
|
||||
<h3>文化传播</h3>
|
||||
<p>让更多人了解和欣赏鄂伦春族文化,提升民族文化的影响力和认知度。</p>
|
||||
</div>
|
||||
<div class="mission-item card">
|
||||
<div class="mission-icon">
|
||||
<el-icon size="40" color="var(--primary-color)"><TrendCharts /></el-icon>
|
||||
</div>
|
||||
<h3>经济发展</h3>
|
||||
<p>为鄂伦春族手工艺人提供销售平台,促进民族文化产业的发展和传承人的经济收入。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 团队介绍 -->
|
||||
<section class="team section">
|
||||
<div class="container">
|
||||
<h2 class="section-title text-center">团队介绍</h2>
|
||||
<div class="team-grid">
|
||||
<div v-for="member in teamMembers" :key="member.id" class="team-member card">
|
||||
<div class="member-avatar">
|
||||
<el-icon size="50"><User /></el-icon>
|
||||
</div>
|
||||
<div class="member-info">
|
||||
<h3 class="member-name">{{ member.name }}</h3>
|
||||
<p class="member-title">{{ member.title }}</p>
|
||||
<p class="member-desc">{{ member.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 合作伙伴 -->
|
||||
<section class="partners section">
|
||||
<div class="container">
|
||||
<h2 class="section-title text-center">合作伙伴</h2>
|
||||
<div class="partners-grid">
|
||||
<div v-for="partner in partners" :key="partner.id" class="partner-item card">
|
||||
<div class="partner-logo">
|
||||
<div class="logo-placeholder">
|
||||
<svg viewBox="0 0 120 80" width="100%" height="80">
|
||||
<rect width="120" height="80" fill="var(--primary-color)" opacity="0.1"/>
|
||||
<text x="60" y="45" text-anchor="middle" fill="var(--primary-color)"
|
||||
font-size="12" font-weight="bold">{{ partner.name.substring(0, 4) }}</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="partner-info">
|
||||
<h4>{{ partner.name }}</h4>
|
||||
<p>{{ partner.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 发展历程 -->
|
||||
<section class="timeline section">
|
||||
<div class="container">
|
||||
<h2 class="section-title text-center">发展历程</h2>
|
||||
<div class="timeline-container">
|
||||
<div v-for="(event, index) in timelineEvents" :key="event.id" class="timeline-item">
|
||||
<div class="timeline-marker">
|
||||
<div class="marker-dot"></div>
|
||||
<div v-if="index < timelineEvents.length - 1" class="marker-line"></div>
|
||||
</div>
|
||||
<div class="timeline-content card">
|
||||
<div class="timeline-date">{{ event.date }}</div>
|
||||
<h3 class="timeline-title">{{ event.title }}</h3>
|
||||
<p class="timeline-desc">{{ event.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 联系我们 -->
|
||||
<section class="contact section">
|
||||
<div class="container">
|
||||
<h2 class="section-title text-center">联系我们</h2>
|
||||
<div class="contact-layout">
|
||||
<div class="contact-info">
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<el-icon size="24" color="var(--primary-color)"><Location /></el-icon>
|
||||
</div>
|
||||
<div class="contact-details">
|
||||
<h4>地址</h4>
|
||||
<p>黑龙江省大兴安岭地区呼玛县<br>鄂伦春族文化传承中心</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<el-icon size="24" color="var(--primary-color)"><Phone /></el-icon>
|
||||
</div>
|
||||
<div class="contact-details">
|
||||
<h4>电话</h4>
|
||||
<p>400-123-4567<br>0457-1234567</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<el-icon size="24" color="var(--primary-color)"><Message /></el-icon>
|
||||
</div>
|
||||
<div class="contact-details">
|
||||
<h4>邮箱</h4>
|
||||
<p>info@oroqen-culture.com<br>support@oroqen-culture.com</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<el-icon size="24" color="var(--primary-color)"><Clock /></el-icon>
|
||||
</div>
|
||||
<div class="contact-details">
|
||||
<h4>工作时间</h4>
|
||||
<p>周一至周五:9:00-18:00<br>周六至周日:10:00-17:00</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-form">
|
||||
<h3>留言咨询</h3>
|
||||
<el-form :model="contactForm" label-width="80px">
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="contactForm.name" placeholder="请输入您的姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱">
|
||||
<el-input v-model="contactForm.email" placeholder="请输入您的邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="电话">
|
||||
<el-input v-model="contactForm.phone" placeholder="请输入您的电话" />
|
||||
</el-form-item>
|
||||
<el-form-item label="主题">
|
||||
<el-input v-model="contactForm.subject" placeholder="请输入咨询主题" />
|
||||
</el-form-item>
|
||||
<el-form-item label="内容">
|
||||
<el-input
|
||||
v-model="contactForm.message"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入您的留言内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitContact" :loading="submitting">
|
||||
提交留言
|
||||
</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
Collection,
|
||||
Share,
|
||||
TrendCharts,
|
||||
User,
|
||||
Location,
|
||||
Phone,
|
||||
Message,
|
||||
Clock
|
||||
} from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// 响应式数据
|
||||
const submitting = ref(false)
|
||||
|
||||
// 团队成员
|
||||
const teamMembers = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '张文化',
|
||||
title: '项目负责人',
|
||||
description: '民族学博士,专注于鄂伦春族文化研究20余年,致力于民族文化的保护与传承工作。'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '李传承',
|
||||
title: '文化顾问',
|
||||
description: '鄂伦春族文化传承人,精通传统手工艺制作,为平台提供专业的文化指导。'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '王技术',
|
||||
title: '技术总监',
|
||||
description: '资深全栈开发工程师,专注于文化数字化技术应用,负责平台的技术架构与开发。'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '刘设计',
|
||||
title: '设计总监',
|
||||
description: '资深UI/UX设计师,擅长将传统文化元素与现代设计理念相结合。'
|
||||
}
|
||||
])
|
||||
|
||||
// 合作伙伴
|
||||
const partners = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '大兴安岭文化局',
|
||||
description: '提供政策支持和文化资源'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '鄂伦春族自治旗',
|
||||
description: '官方合作伙伴,提供文化认证'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '民族工艺合作社',
|
||||
description: '手工艺品供应商合作伙伴'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '文化研究院',
|
||||
description: '学术研究与文化咨询支持'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '非遗保护中心',
|
||||
description: '非物质文化遗产保护合作'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '民族博物馆',
|
||||
description: '文物资料与展览合作'
|
||||
}
|
||||
])
|
||||
|
||||
// 发展历程
|
||||
const timelineEvents = ref([
|
||||
{
|
||||
id: 1,
|
||||
date: '2023年3月',
|
||||
title: '项目启动',
|
||||
description: '项目正式启动,开始进行鄂伦春族文化资料收集和整理工作。'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: '2023年6月',
|
||||
title: '文化调研',
|
||||
description: '深入鄂伦春族聚居地进行实地调研,采访传承人,收集第一手文化资料。'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: '2023年9月',
|
||||
title: '平台开发',
|
||||
description: '开始平台的技术开发工作,设计用户界面,构建系统架构。'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: '2023年12月',
|
||||
title: '内容制作',
|
||||
description: '完成文化内容的数字化制作,包括图片、视频、音频等多媒体资源。'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
date: '2024年1月',
|
||||
title: '平台上线',
|
||||
description: '平台正式上线运营,开始为用户提供文化展示和产品销售服务。'
|
||||
}
|
||||
])
|
||||
|
||||
// 联系表单
|
||||
const contactForm = ref({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
subject: '',
|
||||
message: ''
|
||||
})
|
||||
|
||||
// 方法
|
||||
const submitContact = async () => {
|
||||
if (!contactForm.value.name || !contactForm.value.email || !contactForm.value.message) {
|
||||
ElMessage.warning('请填写必要信息')
|
||||
return
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
|
||||
// 模拟提交
|
||||
setTimeout(() => {
|
||||
submitting.value = false
|
||||
ElMessage.success('留言提交成功,我们会尽快回复您!')
|
||||
resetForm()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
contactForm.value = {
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
subject: '',
|
||||
message: ''
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面头部 */
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
padding: 4rem 0 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 平台介绍 */
|
||||
.intro-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.intro-text {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.8;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.visual-placeholder {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 使命 */
|
||||
.mission {
|
||||
background: var(--background-light);
|
||||
}
|
||||
|
||||
.mission-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.mission-item {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.mission-icon {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.mission-item h3 {
|
||||
color: var(--primary-color);
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mission-item p {
|
||||
color: var(--text-light);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 团队介绍 */
|
||||
.team-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.team-member {
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.member-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1.5rem;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
color: var(--primary-color);
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.member-title {
|
||||
color: var(--secondary-color);
|
||||
font-weight: 500;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.member-desc {
|
||||
color: var(--text-light);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 合作伙伴 */
|
||||
.partners {
|
||||
background: var(--background-light);
|
||||
}
|
||||
|
||||
.partners-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.partner-item {
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.partner-logo {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.logo-placeholder {
|
||||
width: 120px;
|
||||
height: 80px;
|
||||
margin: 0 auto;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.partner-info h4 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.partner-info p {
|
||||
color: var(--text-light);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 发展历程 */
|
||||
.timeline-container {
|
||||
max-width: 800px;
|
||||
margin: 3rem auto 0;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.timeline-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.marker-dot {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary-color);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.marker-line {
|
||||
width: 2px;
|
||||
height: 100px;
|
||||
background: var(--border-color);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.timeline-date {
|
||||
color: var(--secondary-color);
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.timeline-title {
|
||||
color: var(--primary-color);
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.timeline-desc {
|
||||
color: var(--text-light);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 联系我们 */
|
||||
.contact {
|
||||
background: var(--background-light);
|
||||
}
|
||||
|
||||
.contact-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.contact-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background: rgba(var(--primary-color-rgb), 0.1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contact-details h4 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.contact-details p {
|
||||
color: var(--text-light);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.contact-form {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.contact-form h3 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.intro-layout {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.mission-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.team-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
}
|
||||
|
||||
.partners-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.timeline-marker {
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.marker-line {
|
||||
width: 100px;
|
||||
height: 2px;
|
||||
margin-top: 0;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.contact-layout {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.mission-item,
|
||||
.team-member,
|
||||
.partner-item {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.contact-form {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
733
src/views/Cart.vue
Normal file
733
src/views/Cart.vue
Normal file
@ -0,0 +1,733 @@
|
||||
<template>
|
||||
<div class="cart">
|
||||
<!-- 页面头部 -->
|
||||
<section class="page-header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<h1 class="page-title">购物车</h1>
|
||||
<p class="page-subtitle">确认您的商品信息</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="container">
|
||||
<!-- 购物车内容 -->
|
||||
<div v-if="cartItems.length > 0" class="cart-content">
|
||||
<!-- 购物车列表 -->
|
||||
<div class="cart-list">
|
||||
<div class="cart-header">
|
||||
<div class="select-all">
|
||||
<el-checkbox
|
||||
v-model="selectAll"
|
||||
@change="handleSelectAll"
|
||||
:indeterminate="isIndeterminate"
|
||||
>
|
||||
全选
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<div class="header-labels">
|
||||
<span class="product-label">商品信息</span>
|
||||
<span class="price-label">单价</span>
|
||||
<span class="quantity-label">数量</span>
|
||||
<span class="total-label">小计</span>
|
||||
<span class="action-label">操作</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cart-items">
|
||||
<div
|
||||
v-for="item in cartItems"
|
||||
:key="item.id"
|
||||
class="cart-item"
|
||||
>
|
||||
<div class="item-select">
|
||||
<el-checkbox
|
||||
v-model="item.selected"
|
||||
@change="updateSelection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="item-product">
|
||||
<div class="product-image">
|
||||
<div class="image-placeholder">
|
||||
<svg viewBox="0 0 100 80" width="100%" height="80">
|
||||
<rect width="100" height="80" :fill="item.color" opacity="0.2"/>
|
||||
<circle cx="50" cy="40" r="20" :fill="item.color" opacity="0.5"/>
|
||||
<text x="50" y="45" text-anchor="middle" :fill="item.color"
|
||||
font-size="10" font-weight="bold">{{ item.name.substring(0, 2) }}</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<h3 class="product-name">{{ item.name }}</h3>
|
||||
<p class="product-specs">{{ item.specs || '规格:标准版' }}</p>
|
||||
<p class="product-craftsman" v-if="item.craftsman">
|
||||
<el-icon><User /></el-icon>
|
||||
{{ item.craftsman }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item-price">
|
||||
<span class="current-price">¥{{ item.price }}</span>
|
||||
<span v-if="item.originalPrice" class="original-price">¥{{ item.originalPrice }}</span>
|
||||
</div>
|
||||
|
||||
<div class="item-quantity">
|
||||
<div class="quantity-controls">
|
||||
<el-button
|
||||
size="small"
|
||||
@click="decreaseQuantity(item)"
|
||||
:disabled="item.quantity <= 1"
|
||||
>
|
||||
-
|
||||
</el-button>
|
||||
<span class="quantity">{{ item.quantity }}</span>
|
||||
<el-button
|
||||
size="small"
|
||||
@click="increaseQuantity(item)"
|
||||
:disabled="item.quantity >= item.stock"
|
||||
>
|
||||
+
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="stock-info">
|
||||
<span class="stock-text">库存{{ item.stock }}件</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item-total">
|
||||
<span class="total-price">¥{{ (item.price * item.quantity).toFixed(2) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="item-actions">
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="removeFromCart(item.id)"
|
||||
class="remove-btn"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 购物车汇总 -->
|
||||
<div class="cart-summary">
|
||||
<div class="summary-content">
|
||||
<div class="summary-info">
|
||||
<div class="selected-count">
|
||||
已选择 {{ selectedItems.length }} 件商品
|
||||
</div>
|
||||
<div class="total-amount">
|
||||
<span class="label">合计:</span>
|
||||
<span class="amount">¥{{ totalAmount.toFixed(2) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="large"
|
||||
:disabled="selectedItems.length === 0"
|
||||
@click="checkout"
|
||||
>
|
||||
结算 ({{ selectedItems.length }})
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空购物车 -->
|
||||
<div v-else class="empty-cart">
|
||||
<div class="empty-icon">
|
||||
<el-icon size="80" color="#ddd"><ShoppingCart /></el-icon>
|
||||
</div>
|
||||
<h3>购物车是空的</h3>
|
||||
<p>快去挑选您喜欢的商品吧!</p>
|
||||
<el-button type="primary" @click="$router.push('/products')">
|
||||
去购物
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 推荐商品 -->
|
||||
<div v-if="cartItems.length > 0" class="recommended-products">
|
||||
<h2 class="section-title">为您推荐</h2>
|
||||
<div class="products-grid">
|
||||
<div
|
||||
v-for="product in recommendedProducts"
|
||||
:key="product.id"
|
||||
class="product-card card"
|
||||
@click="$router.push(`/product/${product.id}`)"
|
||||
>
|
||||
<div class="product-image">
|
||||
<div class="image-placeholder">
|
||||
<svg viewBox="0 0 200 150" width="100%" height="150">
|
||||
<rect width="200" height="150" :fill="product.color" opacity="0.2"/>
|
||||
<circle cx="100" cy="75" r="30" :fill="product.color" opacity="0.5"/>
|
||||
<text x="100" y="80" text-anchor="middle" :fill="product.color"
|
||||
font-size="12" font-weight="bold">{{ product.name.substring(0, 2) }}</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product-info">
|
||||
<h3 class="product-name">{{ product.name }}</h3>
|
||||
<div class="product-price">¥{{ product.price }}</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.stop="addRecommendedToCart(product)"
|
||||
>
|
||||
加入购物车
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useMainStore } from '../stores'
|
||||
import { User, ShoppingCart } from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useMainStore()
|
||||
|
||||
// 响应式数据
|
||||
const selectAll = ref(false)
|
||||
|
||||
// 推荐商品
|
||||
const recommendedProducts = ref([
|
||||
{
|
||||
id: 2,
|
||||
name: '鄂伦春族刺绣挂画',
|
||||
price: 288,
|
||||
color: '#2d5016'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '手工木雕摆件',
|
||||
price: 128,
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '野生蓝莓干',
|
||||
price: 58,
|
||||
color: '#2d5016'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '鄂伦春文化笔记本',
|
||||
price: 35,
|
||||
color: '#8b4513'
|
||||
}
|
||||
])
|
||||
|
||||
// 计算属性
|
||||
const cartItems = computed(() => {
|
||||
return store.cartItems.map(item => ({
|
||||
...item,
|
||||
selected: item.selected !== false, // 默认选中
|
||||
color: getProductColor(item.id)
|
||||
}))
|
||||
})
|
||||
|
||||
const selectedItems = computed(() => {
|
||||
return cartItems.value.filter(item => item.selected)
|
||||
})
|
||||
|
||||
const totalAmount = computed(() => {
|
||||
return selectedItems.value.reduce((total, item) => {
|
||||
return total + (item.price * item.quantity)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
const isIndeterminate = computed(() => {
|
||||
const selectedCount = selectedItems.value.length
|
||||
return selectedCount > 0 && selectedCount < cartItems.value.length
|
||||
})
|
||||
|
||||
// 方法
|
||||
const getProductColor = (productId: number) => {
|
||||
const colors = ['#8b4513', '#2d5016', '#4a5568', '#2d3748']
|
||||
return colors[productId % colors.length]
|
||||
}
|
||||
|
||||
const handleSelectAll = (checked: boolean) => {
|
||||
cartItems.value.forEach(item => {
|
||||
item.selected = checked
|
||||
})
|
||||
updateCartSelection()
|
||||
}
|
||||
|
||||
const updateSelection = () => {
|
||||
const selectedCount = cartItems.value.filter(item => item.selected).length
|
||||
selectAll.value = selectedCount === cartItems.value.length
|
||||
updateCartSelection()
|
||||
}
|
||||
|
||||
const updateCartSelection = () => {
|
||||
// 更新store中的选中状态
|
||||
cartItems.value.forEach(item => {
|
||||
const storeItem = store.cartItems.find(si => si.id === item.id)
|
||||
if (storeItem) {
|
||||
storeItem.selected = item.selected
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const increaseQuantity = (item: any) => {
|
||||
if (item.quantity < item.stock) {
|
||||
store.updateCartItemQuantity(item.id, item.quantity + 1)
|
||||
} else {
|
||||
ElMessage.warning('库存不足')
|
||||
}
|
||||
}
|
||||
|
||||
const decreaseQuantity = (item: any) => {
|
||||
if (item.quantity > 1) {
|
||||
store.updateCartItemQuantity(item.id, item.quantity - 1)
|
||||
}
|
||||
}
|
||||
|
||||
const removeFromCart = async (itemId: number) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'确定要删除这件商品吗?',
|
||||
'确认删除',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
|
||||
store.removeFromCart(itemId)
|
||||
ElMessage.success('商品已删除')
|
||||
|
||||
// 重新计算全选状态
|
||||
updateSelection()
|
||||
} catch {
|
||||
// 用户取消删除
|
||||
}
|
||||
}
|
||||
|
||||
const addRecommendedToCart = (product: any) => {
|
||||
store.addToCart(product)
|
||||
ElMessage.success('已添加到购物车')
|
||||
}
|
||||
|
||||
const checkout = () => {
|
||||
if (selectedItems.value.length === 0) {
|
||||
ElMessage.warning('请选择要结算的商品')
|
||||
return
|
||||
}
|
||||
|
||||
// 这里应该跳转到结算页面
|
||||
ElMessage.success('跳转到结算页面')
|
||||
// router.push('/checkout')
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// 初始化选中状态
|
||||
updateSelection()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面头部 */
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
padding: 3rem 0 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 购物车内容 */
|
||||
.cart-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 300px;
|
||||
gap: 2rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
/* 购物车列表 */
|
||||
.cart-list {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cart-header {
|
||||
background: var(--background-light);
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.select-all {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.header-labels {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr 1fr 1fr;
|
||||
gap: 1rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.cart-items {
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.cart-item {
|
||||
padding: 1.5rem 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.cart-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.item-select {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.item-product {
|
||||
flex: 2;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.image-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--background-light);
|
||||
}
|
||||
|
||||
.product-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.product-specs {
|
||||
color: var(--text-light);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.product-craftsman {
|
||||
color: var(--text-light);
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.item-price {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.current-price {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.original-price {
|
||||
display: block;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-light);
|
||||
text-decoration: line-through;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.item-quantity {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.quantity-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.quantity {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
min-width: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stock-info {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.item-total {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.total-price {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.remove-btn:hover {
|
||||
color: var(--danger-color);
|
||||
background: rgba(218, 54, 51, 0.1);
|
||||
}
|
||||
|
||||
/* 购物车汇总 */
|
||||
.cart-summary {
|
||||
position: sticky;
|
||||
top: 2rem;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.summary-content {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.summary-info {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.selected-count {
|
||||
color: var(--text-light);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.total-amount {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.total-amount .label {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.total-amount .amount {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.summary-actions .el-button {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* 空购物车 */
|
||||
.empty-cart {
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.empty-cart h3 {
|
||||
font-size: 1.5rem;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.empty-cart p {
|
||||
color: var(--text-light);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
/* 推荐商品 */
|
||||
.recommended-products {
|
||||
margin-top: 3rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.products-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.product-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.product-card .product-image {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.product-card .product-info {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.product-card .product-name {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.product-card .product-price {
|
||||
font-size: 1.1rem;
|
||||
font-weight: bold;
|
||||
color: var(--secondary-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.product-card .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1024px) {
|
||||
.cart-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.cart-summary {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.cart-header {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.header-labels {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cart-item {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.item-select {
|
||||
width: auto;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.item-product {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.item-price,
|
||||
.item-quantity,
|
||||
.item-total,
|
||||
.item-actions {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.quantity-controls {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.products-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.cart-item {
|
||||
padding: 1rem 0.5rem;
|
||||
}
|
||||
|
||||
.summary-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.products-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
592
src/views/Culture.vue
Normal file
592
src/views/Culture.vue
Normal file
@ -0,0 +1,592 @@
|
||||
<template>
|
||||
<div class="culture">
|
||||
<!-- 页面头部 -->
|
||||
<section class="page-header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<h1 class="page-title">鄂伦春族传统文化</h1>
|
||||
<p class="page-subtitle">探索森林深处的古老文明,感受千年传承的文化瑰宝</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 民族概况 -->
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 class="section-title">民族概况</h2>
|
||||
<div class="overview-grid">
|
||||
<div class="overview-content">
|
||||
<div class="overview-item">
|
||||
<h3>历史起源</h3>
|
||||
<p>
|
||||
鄂伦春族是中国东北地区的古老民族,世代生活在大兴安岭的原始森林中。
|
||||
"鄂伦春"意为"山岭上的人",体现了这个民族与森林山岭的深厚联系。
|
||||
他们是中国最后的狩猎民族之一,保持着独特的森林文化传统。
|
||||
</p>
|
||||
</div>
|
||||
<div class="overview-item">
|
||||
<h3>人口分布</h3>
|
||||
<p>
|
||||
鄂伦春族主要分布在内蒙古自治区的呼伦贝尔市、兴安盟,
|
||||
以及黑龙江省的大兴安岭地区。总人口约8000余人,
|
||||
是中国人口较少的民族之一,但文化底蕴深厚。
|
||||
</p>
|
||||
</div>
|
||||
<div class="overview-item">
|
||||
<h3>语言文字</h3>
|
||||
<p>
|
||||
鄂伦春语属阿尔泰语系满-通古斯语族通古斯语支,
|
||||
有布拉格、胡玛、毕拉尔钦三种方言。由于历史原因,
|
||||
现在多数鄂伦春族人使用汉语,民族语言的保护和传承成为重要课题。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overview-image">
|
||||
<div class="image-placeholder">
|
||||
<svg viewBox="0 0 400 300" width="100%" height="300">
|
||||
<rect width="400" height="300" fill="var(--primary-color)" opacity="0.1"/>
|
||||
<path d="M50 250 L200 100 L350 250 L350 280 L50 280 Z"
|
||||
fill="var(--primary-color)" opacity="0.3"/>
|
||||
<circle cx="200" cy="150" r="30" fill="var(--secondary-color)" opacity="0.5"/>
|
||||
<text x="200" y="160" text-anchor="middle" fill="var(--primary-color)"
|
||||
font-size="20" font-weight="bold">森林之民</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 传统文化板块 -->
|
||||
<section class="section" style="background-color: var(--background-light);">
|
||||
<div class="container">
|
||||
<h2 class="section-title">传统文化</h2>
|
||||
<div class="culture-tabs">
|
||||
<div class="tab-buttons">
|
||||
<button
|
||||
v-for="tab in cultureTabs"
|
||||
:key="tab.id"
|
||||
:class="['tab-button', { active: activeTab === tab.id }]"
|
||||
@click="activeTab = tab.id"
|
||||
>
|
||||
{{ tab.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="tab-content">
|
||||
<div v-for="tab in cultureTabs" :key="tab.id" v-show="activeTab === tab.id" class="tab-panel">
|
||||
<div class="culture-detail">
|
||||
<div class="detail-content">
|
||||
<h3>{{ tab.title }}</h3>
|
||||
<p>{{ tab.description }}</p>
|
||||
<ul class="detail-list">
|
||||
<li v-for="item in tab.items" :key="item">{{ item }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="detail-image">
|
||||
<div class="image-placeholder">
|
||||
<svg viewBox="0 0 300 200" width="100%" height="200">
|
||||
<rect width="300" height="200" :fill="tab.color" opacity="0.2"/>
|
||||
<circle cx="150" cy="100" r="40" :fill="tab.color" opacity="0.5"/>
|
||||
<text x="150" y="110" text-anchor="middle" :fill="tab.color"
|
||||
font-size="16" font-weight="bold">{{ tab.name }}</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 多媒体资源库 -->
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 class="section-title">多媒体资源库</h2>
|
||||
<div class="media-categories">
|
||||
<div class="media-category card" @click="openMediaGallery('images')">
|
||||
<div class="media-icon">
|
||||
<el-icon size="48"><Picture /></el-icon>
|
||||
</div>
|
||||
<h3>图片库</h3>
|
||||
<p>传统生活场景、服饰、手工艺品等高清图片</p>
|
||||
<div class="media-count">1000+ 张图片</div>
|
||||
</div>
|
||||
|
||||
<div class="media-category card" @click="openMediaGallery('videos')">
|
||||
<div class="media-icon">
|
||||
<el-icon size="48"><VideoPlay /></el-icon>
|
||||
</div>
|
||||
<h3>视频库</h3>
|
||||
<p>非遗技艺制作过程、节庆活动记录等</p>
|
||||
<div class="media-count">200+ 个视频</div>
|
||||
</div>
|
||||
|
||||
<div class="media-category card" @click="openMediaGallery('audios')">
|
||||
<div class="media-icon">
|
||||
<el-icon size="48"><Microphone /></el-icon>
|
||||
</div>
|
||||
<h3>音频库</h3>
|
||||
<p>鄂伦春族民歌、民间故事录音等</p>
|
||||
<div class="media-count">300+ 个音频</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 文化传承人 -->
|
||||
<section class="section" style="background-color: var(--background-light);">
|
||||
<div class="container">
|
||||
<h2 class="section-title">文化传承人</h2>
|
||||
<div class="inheritors-grid">
|
||||
<div v-for="inheritor in inheritors" :key="inheritor.id" class="inheritor-card card">
|
||||
<div class="inheritor-avatar">
|
||||
<div class="avatar-placeholder">
|
||||
<svg viewBox="0 0 120 120" width="120" height="120">
|
||||
<circle cx="60" cy="60" r="50" fill="var(--primary-color)" opacity="0.2"/>
|
||||
<circle cx="60" cy="45" r="15" fill="var(--primary-color)" opacity="0.5"/>
|
||||
<path d="M30 85 Q60 70 90 85 L90 100 L30 100 Z"
|
||||
fill="var(--primary-color)" opacity="0.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inheritor-info">
|
||||
<h3 class="inheritor-name">{{ inheritor.name }}</h3>
|
||||
<p class="inheritor-title">{{ inheritor.title }}</p>
|
||||
<p class="inheritor-skill">{{ inheritor.skill }}</p>
|
||||
<p class="inheritor-description">{{ inheritor.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 媒体画廊对话框 -->
|
||||
<el-dialog v-model="mediaGalleryVisible" :title="mediaGalleryTitle" width="80%" top="5vh">
|
||||
<div class="media-gallery">
|
||||
<div class="gallery-grid">
|
||||
<div v-for="i in 12" :key="i" class="gallery-item">
|
||||
<div class="gallery-placeholder">
|
||||
<svg viewBox="0 0 200 150" width="100%" height="150">
|
||||
<rect width="200" height="150" fill="var(--primary-color)" opacity="0.1"/>
|
||||
<el-icon size="32" style="color: var(--primary-color);">
|
||||
<Picture v-if="currentMediaType === 'images'" />
|
||||
<VideoPlay v-else-if="currentMediaType === 'videos'" />
|
||||
<Microphone v-else />
|
||||
</el-icon>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="gallery-title">{{ currentMediaType === 'images' ? '传统服饰' : currentMediaType === 'videos' ? '桦皮制作' : '民族歌曲' }} {{ i }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { Picture, VideoPlay, Microphone } from '@element-plus/icons-vue'
|
||||
|
||||
// 响应式数据
|
||||
const activeTab = ref('hunting')
|
||||
const mediaGalleryVisible = ref(false)
|
||||
const mediaGalleryTitle = ref('')
|
||||
const currentMediaType = ref('')
|
||||
|
||||
// 文化标签页数据
|
||||
const cultureTabs = ref([
|
||||
{
|
||||
id: 'hunting',
|
||||
name: '狩猎文化',
|
||||
title: '森林中的狩猎传统',
|
||||
description: '鄂伦春族是中国最后的狩猎民族,狩猎不仅是生存方式,更是文化核心。他们对森林动物习性了如指掌,形成了独特的狩猎技巧和狩猎伦理。',
|
||||
items: [
|
||||
'传统狩猎工具:弓箭、猎枪、陷阱等',
|
||||
'狩猎技巧:追踪、伪装、团队协作',
|
||||
'狩猎伦理:保护幼崽、适度捕猎',
|
||||
'狩猎仪式:出猎前的祈祷和归来后的感恩'
|
||||
],
|
||||
color: '#2d5016'
|
||||
},
|
||||
{
|
||||
id: 'festival',
|
||||
name: '节庆仪式',
|
||||
title: '篝火节与传统庆典',
|
||||
description: '篝火节是鄂伦春族最重要的传统节日,族人们围着篝火载歌载舞,庆祝丰收,祈求平安。这些仪式承载着深厚的文化内涵和精神寄托。',
|
||||
items: [
|
||||
'篝火节:每年6月18日,最盛大的民族节日',
|
||||
'萨满仪式:祈福、治病、驱邪的宗教仪式',
|
||||
'成年礼:青年男女的成人仪式',
|
||||
'丰收庆典:庆祝狩猎和采集的丰收'
|
||||
],
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 'food',
|
||||
name: '饮食文化',
|
||||
title: '森林中的美食传统',
|
||||
description: '鄂伦春族的饮食以肉类为主,辅以野菜、野果。他们善于利用森林资源,创造出独特的烹饪方法和食品保存技术。',
|
||||
items: [
|
||||
'手把肉:传统的肉类烹饪方式',
|
||||
'柳蒿芽:春季重要的野菜食材',
|
||||
'烤肉串:篝火旁的传统美食',
|
||||
'肉干制作:传统的食品保存方法'
|
||||
],
|
||||
color: '#2d5016'
|
||||
},
|
||||
{
|
||||
id: 'housing',
|
||||
name: '居住文化',
|
||||
title: '撮罗子与森林居所',
|
||||
description: '撮罗子是鄂伦春族的传统居所,这种圆锥形建筑适应了游牧狩猎的生活方式,体现了人与自然和谐共处的智慧。',
|
||||
items: [
|
||||
'撮罗子结构:圆锥形框架,桦皮或兽皮覆盖',
|
||||
'建造技巧:快速搭建,便于迁移',
|
||||
'内部布局:火塘居中,功能区域明确',
|
||||
'现代演变:从传统居所到文化象征'
|
||||
],
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 'clothing',
|
||||
name: '服饰文化',
|
||||
title: '精美的传统服饰',
|
||||
description: '鄂伦春族服饰以兽皮为主要材料,装饰精美的刺绣图案。服饰不仅具有实用功能,更承载着丰富的文化内涵和审美价值。',
|
||||
items: [
|
||||
'皮袍:主要服装,保暖实用',
|
||||
'刺绣图案:花卉、动物、几何纹样',
|
||||
'头饰配件:帽子、头巾、发饰',
|
||||
'鞋靴制作:适应森林环境的鞋靴'
|
||||
],
|
||||
color: '#2d5016'
|
||||
}
|
||||
])
|
||||
|
||||
// 传承人数据
|
||||
const inheritors = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '关小云',
|
||||
title: '国家级非遗传承人',
|
||||
skill: '鄂伦春族桦皮制作技艺',
|
||||
description: '从事桦皮工艺制作40余年,作品精美,技艺精湛,致力于技艺传承和创新发展。'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '孟淑珍',
|
||||
title: '自治区级非遗传承人',
|
||||
skill: '鄂伦春族传统刺绣',
|
||||
description: '精通传统刺绣技艺,图案设计独特,色彩搭配和谐,培养了众多刺绣爱好者。'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '白热布',
|
||||
title: '民族文化研究专家',
|
||||
skill: '鄂伦春族民歌传承',
|
||||
description: '收集整理鄂伦春族民歌200余首,致力于民族音乐的保护和传播工作。'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '关金山',
|
||||
title: '传统工艺大师',
|
||||
skill: '木雕与狩猎工具制作',
|
||||
description: '掌握传统木雕技艺和狩猎工具制作方法,作品展现浓郁的民族特色。'
|
||||
}
|
||||
])
|
||||
|
||||
// 方法
|
||||
const openMediaGallery = (type: string) => {
|
||||
currentMediaType.value = type
|
||||
switch (type) {
|
||||
case 'images':
|
||||
mediaGalleryTitle.value = '图片库'
|
||||
break
|
||||
case 'videos':
|
||||
mediaGalleryTitle.value = '视频库'
|
||||
break
|
||||
case 'audios':
|
||||
mediaGalleryTitle.value = '音频库'
|
||||
break
|
||||
}
|
||||
mediaGalleryVisible.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面头部 */
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
padding: 4rem 0 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 民族概况 */
|
||||
.overview-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 3rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.overview-item {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.overview-item h3 {
|
||||
font-size: 1.3rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.overview-item p {
|
||||
line-height: 1.8;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.image-placeholder {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
background: var(--background-light);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 文化标签页 */
|
||||
.culture-tabs {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.tab-buttons {
|
||||
display: flex;
|
||||
background: var(--background-light);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
padding: 1rem 2rem;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-color);
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
|
||||
.tab-button:hover,
|
||||
.tab-button.active {
|
||||
color: var(--primary-color);
|
||||
background: white;
|
||||
border-bottom-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.culture-detail {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 2rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.detail-content h3 {
|
||||
font-size: 1.5rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.detail-content p {
|
||||
line-height: 1.8;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.detail-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.detail-list li {
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.detail-list li:before {
|
||||
content: '•';
|
||||
color: var(--primary-color);
|
||||
font-weight: bold;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* 多媒体资源 */
|
||||
.media-categories {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.media-category {
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.media-category:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.media-icon {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.media-category h3 {
|
||||
font-size: 1.3rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.media-category p {
|
||||
color: var(--text-light);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.media-count {
|
||||
font-weight: bold;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
/* 传承人 */
|
||||
.inheritors-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.inheritor-card {
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inheritor-avatar {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.inheritor-name {
|
||||
font-size: 1.3rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.inheritor-title {
|
||||
color: var(--secondary-color);
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.inheritor-skill {
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.inheritor-description {
|
||||
color: var(--text-light);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 媒体画廊 */
|
||||
.gallery-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gallery-placeholder {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
background: var(--background-light);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.gallery-placeholder:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.gallery-title {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.overview-grid,
|
||||
.culture-detail {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.tab-buttons {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.media-categories,
|
||||
.inheritors-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
}
|
||||
}
|
||||
</style>
|
763
src/views/Heritage.vue
Normal file
763
src/views/Heritage.vue
Normal file
@ -0,0 +1,763 @@
|
||||
<template>
|
||||
<div class="heritage">
|
||||
<!-- 页面头部 -->
|
||||
<section class="page-header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<h1 class="page-title">非物质文化遗产</h1>
|
||||
<p class="page-subtitle">传承千年技艺,保护民族瑰宝</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 非遗名录 -->
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 class="section-title">非遗名录</h2>
|
||||
<div class="heritage-levels">
|
||||
<div class="level-tabs">
|
||||
<button
|
||||
v-for="level in heritageLevels"
|
||||
:key="level.id"
|
||||
:class="['level-tab', { active: activeLevel === level.id }]"
|
||||
@click="activeLevel = level.id"
|
||||
>
|
||||
{{ level.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="heritage-list">
|
||||
<div v-for="item in currentHeritageList" :key="item.id" class="heritage-item card">
|
||||
<div class="heritage-image">
|
||||
<div class="image-placeholder">
|
||||
<svg viewBox="0 0 200 150" width="100%" height="150">
|
||||
<rect width="200" height="150" :fill="item.color" opacity="0.2"/>
|
||||
<circle cx="100" cy="75" r="30" :fill="item.color" opacity="0.5"/>
|
||||
<text x="100" y="85" text-anchor="middle" :fill="item.color"
|
||||
font-size="12" font-weight="bold">{{ item.name.substring(0, 2) }}</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="heritage-info">
|
||||
<h3 class="heritage-name">{{ item.name }}</h3>
|
||||
<p class="heritage-category">{{ item.category }}</p>
|
||||
<p class="heritage-description">{{ item.description }}</p>
|
||||
<div class="heritage-meta">
|
||||
<span class="heritage-year">{{ item.year }}年入选</span>
|
||||
<el-button type="primary" size="small" @click="viewDetails(item)">
|
||||
查看详情
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 技艺展示 -->
|
||||
<section class="section" style="background-color: var(--background-light);">
|
||||
<div class="container">
|
||||
<h2 class="section-title">技艺展示</h2>
|
||||
<div class="crafts-showcase">
|
||||
<div v-for="craft in crafts" :key="craft.id" class="craft-item">
|
||||
<div class="craft-media">
|
||||
<div class="media-placeholder">
|
||||
<svg viewBox="0 0 400 250" width="100%" height="250">
|
||||
<rect width="400" height="250" :fill="craft.color" opacity="0.1"/>
|
||||
<rect x="50" y="50" width="300" height="150" :fill="craft.color" opacity="0.3" rx="10"/>
|
||||
<circle cx="200" cy="125" r="40" fill="white" opacity="0.8"/>
|
||||
<polygon points="180,115 180,135 220,125" fill="var(--primary-color)"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="media-controls">
|
||||
<el-button type="primary" circle @click="playCraftVideo(craft)">
|
||||
<el-icon><VideoPlay /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="craft-content">
|
||||
<h3 class="craft-title">{{ craft.name }}</h3>
|
||||
<p class="craft-description">{{ craft.description }}</p>
|
||||
<div class="craft-steps">
|
||||
<h4>制作流程:</h4>
|
||||
<ol class="steps-list">
|
||||
<li v-for="step in craft.steps" :key="step">{{ step }}</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 传承人故事 -->
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 class="section-title">传承人故事</h2>
|
||||
<div class="inheritors-stories">
|
||||
<div v-for="story in inheritorStories" :key="story.id" class="story-card card">
|
||||
<div class="story-header">
|
||||
<div class="inheritor-avatar">
|
||||
<div class="avatar-placeholder">
|
||||
<svg viewBox="0 0 80 80" width="80" height="80">
|
||||
<circle cx="40" cy="40" r="35" fill="var(--primary-color)" opacity="0.2"/>
|
||||
<circle cx="40" cy="30" r="12" fill="var(--primary-color)" opacity="0.5"/>
|
||||
<path d="M20 60 Q40 50 60 60 L60 70 L20 70 Z"
|
||||
fill="var(--primary-color)" opacity="0.5"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inheritor-basic">
|
||||
<h3 class="inheritor-name">{{ story.name }}</h3>
|
||||
<p class="inheritor-title">{{ story.title }}</p>
|
||||
<p class="inheritor-age">{{ story.age }}岁 · {{ story.experience }}年从艺经验</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="story-content">
|
||||
<h4>传承故事</h4>
|
||||
<p>{{ story.story }}</p>
|
||||
<h4>代表作品</h4>
|
||||
<div class="works-grid">
|
||||
<div v-for="work in story.works" :key="work" class="work-item">
|
||||
<div class="work-image">
|
||||
<svg viewBox="0 0 100 80" width="100" height="80">
|
||||
<rect width="100" height="80" fill="var(--secondary-color)" opacity="0.2" rx="4"/>
|
||||
<text x="50" y="45" text-anchor="middle" fill="var(--secondary-color)"
|
||||
font-size="10">{{ work.substring(0, 2) }}</text>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="work-name">{{ work }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 保护现状 -->
|
||||
<section class="section" style="background-color: var(--background-light);">
|
||||
<div class="container">
|
||||
<h2 class="section-title">保护现状与成果</h2>
|
||||
<div class="protection-overview">
|
||||
<div class="protection-stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">15</div>
|
||||
<div class="stat-label">国家级非遗项目</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">28</div>
|
||||
<div class="stat-label">省级非遗项目</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">45</div>
|
||||
<div class="stat-label">代表性传承人</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">8</div>
|
||||
<div class="stat-label">传承基地</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="protection-measures">
|
||||
<h3>保护措施</h3>
|
||||
<div class="measures-grid">
|
||||
<div class="measure-item">
|
||||
<div class="measure-icon">
|
||||
<el-icon size="32"><School /></el-icon>
|
||||
</div>
|
||||
<h4>教育传承</h4>
|
||||
<p>在学校开设非遗课程,培养年轻一代的文化认同感</p>
|
||||
</div>
|
||||
<div class="measure-item">
|
||||
<div class="measure-icon">
|
||||
<el-icon size="32"><Camera /></el-icon>
|
||||
</div>
|
||||
<h4>数字化保护</h4>
|
||||
<p>建立数字档案,记录技艺流程和文化内涵</p>
|
||||
</div>
|
||||
<div class="measure-item">
|
||||
<div class="measure-icon">
|
||||
<el-icon size="32"><Trophy /></el-icon>
|
||||
</div>
|
||||
<h4>展示推广</h4>
|
||||
<p>举办展览活动,提高社会关注度和影响力</p>
|
||||
</div>
|
||||
<div class="measure-item">
|
||||
<div class="measure-icon">
|
||||
<el-icon size="32"><Money /></el-icon>
|
||||
</div>
|
||||
<h4>资金支持</h4>
|
||||
<p>设立专项资金,支持传承人和保护项目</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<el-dialog v-model="detailDialogVisible" :title="selectedHeritage?.name" width="70%" top="5vh">
|
||||
<div v-if="selectedHeritage" class="heritage-detail">
|
||||
<div class="detail-image">
|
||||
<div class="image-placeholder">
|
||||
<svg viewBox="0 0 400 200" width="100%" height="200">
|
||||
<rect width="400" height="200" :fill="selectedHeritage.color" opacity="0.2"/>
|
||||
<circle cx="200" cy="100" r="50" :fill="selectedHeritage.color" opacity="0.5"/>
|
||||
<text x="200" y="110" text-anchor="middle" :fill="selectedHeritage.color"
|
||||
font-size="20" font-weight="bold">{{ selectedHeritage.name.substring(0, 2) }}</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail-content">
|
||||
<h3>项目简介</h3>
|
||||
<p>{{ selectedHeritage.fullDescription }}</p>
|
||||
<h3>历史渊源</h3>
|
||||
<p>{{ selectedHeritage.history }}</p>
|
||||
<h3>技艺特点</h3>
|
||||
<ul>
|
||||
<li v-for="feature in selectedHeritage.features" :key="feature">{{ feature }}</li>
|
||||
</ul>
|
||||
<h3>传承价值</h3>
|
||||
<p>{{ selectedHeritage.value }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { VideoPlay, School, Camera, Trophy, Money } from '@element-plus/icons-vue'
|
||||
|
||||
// 响应式数据
|
||||
const activeLevel = ref('national')
|
||||
const detailDialogVisible = ref(false)
|
||||
const selectedHeritage = ref(null)
|
||||
|
||||
// 非遗级别
|
||||
const heritageLevels = ref([
|
||||
{ id: 'national', name: '国家级' },
|
||||
{ id: 'provincial', name: '省级' },
|
||||
{ id: 'municipal', name: '市级' }
|
||||
])
|
||||
|
||||
// 非遗项目数据
|
||||
const heritageItems = ref({
|
||||
national: [
|
||||
{
|
||||
id: 1,
|
||||
name: '鄂伦春族桦皮制作技艺',
|
||||
category: '传统技艺',
|
||||
description: '利用桦树皮制作各种生活用品的传统技艺',
|
||||
year: 2008,
|
||||
color: '#8b4513',
|
||||
fullDescription: '鄂伦春族桦皮制作技艺是一项古老的传统手工艺,主要利用白桦树皮制作各种生活用品。',
|
||||
history: '这项技艺有着数百年的历史,是鄂伦春族适应森林生活的智慧结晶。',
|
||||
features: ['材料天然环保', '制作工艺精湛', '实用性强', '装饰美观'],
|
||||
value: '体现了鄂伦春族与自然和谐共处的生活理念,具有重要的文化价值。'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '鄂伦春族民歌',
|
||||
category: '传统音乐',
|
||||
description: '反映鄂伦春族生活的传统民歌',
|
||||
year: 2008,
|
||||
color: '#2d5016',
|
||||
fullDescription: '鄂伦春族民歌是反映鄂伦春族生产生活、思想感情的重要载体。',
|
||||
history: '民歌世代口耳相传,内容丰富,形式多样。',
|
||||
features: ['旋律优美', '内容丰富', '形式多样', '情感真挚'],
|
||||
value: '是研究鄂伦春族历史文化的重要资料,具有很高的学术价值。'
|
||||
}
|
||||
],
|
||||
provincial: [
|
||||
{
|
||||
id: 3,
|
||||
name: '鄂伦春族传统刺绣',
|
||||
category: '传统美术',
|
||||
description: '具有民族特色的传统刺绣工艺',
|
||||
year: 2010,
|
||||
color: '#8b4513',
|
||||
fullDescription: '鄂伦春族传统刺绣以其精美的图案和独特的技法而闻名。',
|
||||
history: '刺绣技艺在鄂伦春族中有着悠久的历史传统。',
|
||||
features: ['图案精美', '色彩丰富', '技法独特', '寓意深刻'],
|
||||
value: '展现了鄂伦春族妇女的智慧和审美情趣。'
|
||||
}
|
||||
],
|
||||
municipal: [
|
||||
{
|
||||
id: 4,
|
||||
name: '鄂伦春族萨满舞',
|
||||
category: '传统舞蹈',
|
||||
description: '具有宗教色彩的传统舞蹈',
|
||||
year: 2012,
|
||||
color: '#2d5016',
|
||||
fullDescription: '萨满舞是鄂伦春族重要的宗教仪式舞蹈。',
|
||||
history: '萨满舞起源于古老的萨满教信仰。',
|
||||
features: ['动作独特', '节奏鲜明', '寓意深刻', '仪式感强'],
|
||||
value: '是研究鄂伦春族宗教信仰的重要资料。'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 技艺展示数据
|
||||
const crafts = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '桦皮制作技艺',
|
||||
description: '从采集桦皮到制作成品的完整工艺流程',
|
||||
color: '#8b4513',
|
||||
steps: [
|
||||
'选择合适的白桦树',
|
||||
'小心剥取桦皮',
|
||||
'清理和处理桦皮',
|
||||
'设计制作图案',
|
||||
'缝制和装饰',
|
||||
'最终成品整理'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '传统刺绣工艺',
|
||||
description: '精美的手工刺绣制作过程',
|
||||
color: '#2d5016',
|
||||
steps: [
|
||||
'设计刺绣图案',
|
||||
'选择合适面料',
|
||||
'准备彩色丝线',
|
||||
'绷布固定',
|
||||
'精细刺绣',
|
||||
'后期整理装裱'
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
// 传承人故事数据
|
||||
const inheritorStories = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '关小云',
|
||||
title: '国家级非遗传承人',
|
||||
age: 68,
|
||||
experience: 45,
|
||||
story: '从小跟随祖母学习桦皮制作技艺,经过几十年的钻研和实践,不仅掌握了传统技法,还在保持传统的基础上进行了创新发展。她制作的桦皮工艺品不仅实用,更是艺术品,深受人们喜爱。',
|
||||
works: ['桦皮首饰盒', '桦皮装饰画', '桦皮茶具', '桦皮摆件']
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '孟淑珍',
|
||||
title: '自治区级非遗传承人',
|
||||
age: 72,
|
||||
experience: 50,
|
||||
story: '自幼学习刺绣技艺,对传统图案有着深入的研究和理解。她的刺绣作品图案精美,色彩和谐,充分体现了鄂伦春族的文化特色。多年来培养了众多学生,为技艺传承做出了重要贡献。',
|
||||
works: ['传统服饰刺绣', '装饰挂画', '手工包袋', '民族头饰']
|
||||
}
|
||||
])
|
||||
|
||||
// 计算属性
|
||||
const currentHeritageList = computed(() => {
|
||||
return heritageItems.value[activeLevel.value] || []
|
||||
})
|
||||
|
||||
// 方法
|
||||
const viewDetails = (item: any) => {
|
||||
selectedHeritage.value = item
|
||||
detailDialogVisible.value = true
|
||||
}
|
||||
|
||||
const playCraftVideo = (craft: any) => {
|
||||
// 模拟播放视频
|
||||
console.log('播放视频:', craft.name)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面头部 */
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
padding: 4rem 0 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 非遗级别标签 */
|
||||
.level-tabs {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 2rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.level-tab {
|
||||
padding: 0.8rem 2rem;
|
||||
border: 2px solid var(--primary-color);
|
||||
background: transparent;
|
||||
color: var(--primary-color);
|
||||
border-radius: 25px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.level-tab:hover,
|
||||
.level-tab.active {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* 非遗列表 */
|
||||
.heritage-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.heritage-item {
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.heritage-item:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.heritage-image {
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.image-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--background-light);
|
||||
}
|
||||
|
||||
.heritage-info {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.heritage-name {
|
||||
font-size: 1.3rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.heritage-category {
|
||||
color: var(--secondary-color);
|
||||
font-weight: 500;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.heritage-description {
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.heritage-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.heritage-year {
|
||||
color: var(--text-light);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 技艺展示 */
|
||||
.crafts-showcase {
|
||||
display: grid;
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
.craft-item {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.craft-item:nth-child(even) {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.craft-item:nth-child(even) > * {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.craft-media {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.media-placeholder {
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.media-controls {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.craft-content {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.craft-title {
|
||||
font-size: 1.5rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.craft-description {
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.craft-steps h4 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.steps-list {
|
||||
color: var(--text-color);
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.steps-list li {
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 传承人故事 */
|
||||
.inheritors-stories {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.story-card {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.story-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.inheritor-name {
|
||||
font-size: 1.3rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.inheritor-title {
|
||||
color: var(--secondary-color);
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.inheritor-age {
|
||||
color: var(--text-light);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.story-content h4 {
|
||||
color: var(--primary-color);
|
||||
margin: 1.5rem 0 1rem;
|
||||
}
|
||||
|
||||
.story-content p {
|
||||
color: var(--text-color);
|
||||
line-height: 1.8;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.works-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.work-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.work-image {
|
||||
margin-bottom: 0.5rem;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.work-name {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
/* 保护现状 */
|
||||
.protection-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.protection-measures h3 {
|
||||
font-size: 1.5rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.measures-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.measure-item {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.measure-icon {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.measure-item h4 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.measure-item p {
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 详情对话框 */
|
||||
.heritage-detail {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.detail-content h3 {
|
||||
color: var(--primary-color);
|
||||
margin: 1.5rem 0 1rem;
|
||||
}
|
||||
|
||||
.detail-content h3:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.detail-content p {
|
||||
color: var(--text-color);
|
||||
line-height: 1.8;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.detail-content ul {
|
||||
color: var(--text-color);
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-content li {
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.level-tabs {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.heritage-list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.craft-item {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.craft-item:nth-child(even) {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.story-header {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.works-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.protection-stats,
|
||||
.measures-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.heritage-detail {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
554
src/views/Home.vue
Normal file
554
src/views/Home.vue
Normal file
@ -0,0 +1,554 @@
|
||||
<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="#" 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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useMainStore } from '../stores'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const store = useMainStore()
|
||||
|
||||
// 特色产品数据
|
||||
const featuredProducts = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '桦皮工艺盒',
|
||||
description: '传统桦皮制作工艺,精美实用',
|
||||
price: 168,
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '鄂伦春族刺绣',
|
||||
description: '手工刺绣,图案精美,寓意吉祥',
|
||||
price: 288,
|
||||
color: '#2d5016'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '传统服饰',
|
||||
description: '复刻传统服饰,展现民族风采',
|
||||
price: 588,
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '木雕摆件',
|
||||
description: '手工雕刻,展现森林文化',
|
||||
price: 128,
|
||||
color: '#2d5016'
|
||||
}
|
||||
])
|
||||
|
||||
// 文化故事数据
|
||||
const stories = ref([
|
||||
{
|
||||
id: 1,
|
||||
title: '森林中的狩猎文化',
|
||||
excerpt: '鄂伦春族世代生活在大兴安岭的森林中,形成了独特的狩猎文化和生活方式...',
|
||||
date: '2024-01-15',
|
||||
color: '#2d5016'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '桦皮工艺的传承',
|
||||
excerpt: '桦皮是鄂伦春族生活中不可缺少的材料,从日用品到艺术品,展现了民族智慧...',
|
||||
date: '2024-01-10',
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '篝火节的欢歌',
|
||||
excerpt: '每年的篝火节是鄂伦春族最重要的节日,族人们围着篝火载歌载舞...',
|
||||
date: '2024-01-05',
|
||||
color: '#2d5016'
|
||||
}
|
||||
])
|
||||
|
||||
// 添加到购物车
|
||||
const addToCart = (product: any) => {
|
||||
store.addToCart(product)
|
||||
ElMessage.success('已添加到购物车')
|
||||
}
|
||||
</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;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.hero-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
1104
src/views/ProductDetail.vue
Normal file
1104
src/views/ProductDetail.vue
Normal file
File diff suppressed because it is too large
Load Diff
582
src/views/Products.vue
Normal file
582
src/views/Products.vue
Normal file
@ -0,0 +1,582 @@
|
||||
<template>
|
||||
<div class="products">
|
||||
<!-- 页面头部 -->
|
||||
<section class="page-header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<h1 class="page-title">特色产品</h1>
|
||||
<p class="page-subtitle">精选鄂伦春族传统手工艺品与文创产品</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 产品筛选 -->
|
||||
<section class="filters-section">
|
||||
<div class="container">
|
||||
<div class="filters">
|
||||
<div class="filter-group">
|
||||
<label>产品分类:</label>
|
||||
<div class="filter-buttons">
|
||||
<button
|
||||
v-for="category in categories"
|
||||
:key="category.id"
|
||||
:class="['filter-btn', { active: selectedCategory === category.id }]"
|
||||
@click="selectedCategory = category.id"
|
||||
>
|
||||
{{ category.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<label>价格范围:</label>
|
||||
<el-select v-model="priceRange" placeholder="选择价格范围" style="width: 200px;">
|
||||
<el-option label="全部价格" value="all" />
|
||||
<el-option label="100元以下" value="0-100" />
|
||||
<el-option label="100-300元" value="100-300" />
|
||||
<el-option label="300-500元" value="300-500" />
|
||||
<el-option label="500元以上" value="500+" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div class="filter-group">
|
||||
<label>排序方式:</label>
|
||||
<el-select v-model="sortBy" placeholder="排序方式" style="width: 150px;">
|
||||
<el-option label="默认排序" value="default" />
|
||||
<el-option label="价格从低到高" value="price-asc" />
|
||||
<el-option label="价格从高到低" value="price-desc" />
|
||||
<el-option label="最新上架" value="newest" />
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 产品列表 -->
|
||||
<section class="products-section section">
|
||||
<div class="container">
|
||||
<div class="products-grid">
|
||||
<div
|
||||
v-for="product in filteredProducts"
|
||||
:key="product.id"
|
||||
class="product-card card"
|
||||
@click="$router.push(`/product/${product.id}`)"
|
||||
>
|
||||
<div class="product-image">
|
||||
<div class="image-placeholder">
|
||||
<svg viewBox="0 0 250 200" width="100%" height="200">
|
||||
<rect width="250" height="200" :fill="product.color" opacity="0.2"/>
|
||||
<circle cx="125" cy="100" r="40" :fill="product.color" opacity="0.5"/>
|
||||
<text x="125" y="110" text-anchor="middle" :fill="product.color"
|
||||
font-size="14" font-weight="bold">{{ product.name.substring(0, 2) }}</text>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="product-badges">
|
||||
<span v-if="product.isNew" class="badge new">新品</span>
|
||||
<span v-if="product.isHot" class="badge hot">热销</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="product-info">
|
||||
<h3 class="product-name">{{ product.name }}</h3>
|
||||
<p class="product-category">{{ getCategoryName(product.category) }}</p>
|
||||
<p class="product-description">{{ product.description }}</p>
|
||||
|
||||
<div class="product-meta">
|
||||
<div class="craftsman-info" v-if="product.craftsman">
|
||||
<el-icon><User /></el-icon>
|
||||
<span>{{ product.craftsman }}</span>
|
||||
</div>
|
||||
<div class="stock-info">
|
||||
<span :class="['stock-status', product.stock > 0 ? 'in-stock' : 'out-stock']">
|
||||
{{ product.stock > 0 ? `库存${product.stock}件` : '暂时缺货' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="product-footer">
|
||||
<div class="price-info">
|
||||
<span class="current-price">¥{{ product.price }}</span>
|
||||
<span v-if="product.originalPrice" class="original-price">¥{{ product.originalPrice }}</span>
|
||||
</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="product.stock === 0"
|
||||
@click.stop="addToCart(product)"
|
||||
>
|
||||
{{ product.stock > 0 ? '加入购物车' : '缺货' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<div class="load-more" v-if="hasMore">
|
||||
<el-button @click="loadMore" :loading="loading">加载更多</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useMainStore } from '../stores'
|
||||
import { User } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const route = useRoute()
|
||||
const store = useMainStore()
|
||||
|
||||
// 响应式数据
|
||||
const selectedCategory = ref('all')
|
||||
const priceRange = ref('all')
|
||||
const sortBy = ref('default')
|
||||
const loading = ref(false)
|
||||
const hasMore = ref(true)
|
||||
|
||||
// 产品分类
|
||||
const categories = ref([
|
||||
{ id: 'all', name: '全部产品' },
|
||||
{ id: 'handicrafts', name: '手工艺品' },
|
||||
{ id: 'food', name: '特色食品' },
|
||||
{ id: 'cultural', name: '文创产品' },
|
||||
{ id: 'clothing', name: '服饰配饰' }
|
||||
])
|
||||
|
||||
// 产品数据
|
||||
const products = ref([
|
||||
{
|
||||
id: 1,
|
||||
name: '桦皮工艺盒',
|
||||
category: 'handicrafts',
|
||||
description: '传统桦皮制作工艺,精美实用的收纳盒',
|
||||
price: 168,
|
||||
originalPrice: 198,
|
||||
stock: 15,
|
||||
craftsman: '关小云',
|
||||
isNew: true,
|
||||
isHot: false,
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '鄂伦春族刺绣挂画',
|
||||
category: 'handicrafts',
|
||||
description: '手工刺绣,图案精美,寓意吉祥',
|
||||
price: 288,
|
||||
stock: 8,
|
||||
craftsman: '孟淑珍',
|
||||
isNew: false,
|
||||
isHot: true,
|
||||
color: '#2d5016'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '传统民族服饰',
|
||||
category: 'clothing',
|
||||
description: '复刻传统服饰,展现民族风采',
|
||||
price: 588,
|
||||
stock: 5,
|
||||
craftsman: '白热布',
|
||||
isNew: false,
|
||||
isHot: true,
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '手工木雕摆件',
|
||||
category: 'handicrafts',
|
||||
description: '精美木雕工艺,展现森林文化',
|
||||
price: 128,
|
||||
stock: 20,
|
||||
craftsman: '关金山',
|
||||
isNew: false,
|
||||
isHot: false,
|
||||
color: '#2d5016'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: '野生蓝莓干',
|
||||
category: 'food',
|
||||
description: '大兴安岭野生蓝莓,天然无添加',
|
||||
price: 58,
|
||||
originalPrice: 68,
|
||||
stock: 50,
|
||||
isNew: true,
|
||||
isHot: false,
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: '鄂伦春文化笔记本',
|
||||
category: 'cultural',
|
||||
description: '融入民族元素的精美笔记本',
|
||||
price: 35,
|
||||
stock: 100,
|
||||
isNew: false,
|
||||
isHot: false,
|
||||
color: '#2d5016'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: '桦皮茶具套装',
|
||||
category: 'handicrafts',
|
||||
description: '传统桦皮工艺制作的茶具',
|
||||
price: 368,
|
||||
stock: 3,
|
||||
craftsman: '关小云',
|
||||
isNew: false,
|
||||
isHot: true,
|
||||
color: '#8b4513'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: '民族风头饰',
|
||||
category: 'clothing',
|
||||
description: '传统图案设计的精美头饰',
|
||||
price: 88,
|
||||
stock: 12,
|
||||
isNew: true,
|
||||
isHot: false,
|
||||
color: '#2d5016'
|
||||
}
|
||||
])
|
||||
|
||||
// 计算属性
|
||||
const filteredProducts = computed(() => {
|
||||
let result = products.value
|
||||
|
||||
// 分类筛选
|
||||
if (selectedCategory.value !== 'all') {
|
||||
result = result.filter(p => p.category === selectedCategory.value)
|
||||
}
|
||||
|
||||
// 价格筛选
|
||||
if (priceRange.value !== 'all') {
|
||||
const [min, max] = priceRange.value.split('-').map(v => v === '+' ? Infinity : parseInt(v))
|
||||
result = result.filter(p => {
|
||||
if (max === undefined) return p.price >= min
|
||||
return p.price >= min && p.price <= max
|
||||
})
|
||||
}
|
||||
|
||||
// 排序
|
||||
switch (sortBy.value) {
|
||||
case 'price-asc':
|
||||
result.sort((a, b) => a.price - b.price)
|
||||
break
|
||||
case 'price-desc':
|
||||
result.sort((a, b) => b.price - a.price)
|
||||
break
|
||||
case 'newest':
|
||||
result.sort((a, b) => (b.isNew ? 1 : 0) - (a.isNew ? 1 : 0))
|
||||
break
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
// 方法
|
||||
const getCategoryName = (categoryId: string) => {
|
||||
const category = categories.value.find(c => c.id === categoryId)
|
||||
return category ? category.name : ''
|
||||
}
|
||||
|
||||
const addToCart = (product: any) => {
|
||||
if (product.stock === 0) {
|
||||
ElMessage.warning('商品暂时缺货')
|
||||
return
|
||||
}
|
||||
|
||||
store.addToCart(product)
|
||||
ElMessage.success('已添加到购物车')
|
||||
}
|
||||
|
||||
const loadMore = () => {
|
||||
loading.value = true
|
||||
// 模拟加载更多数据
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
hasMore.value = false
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// 处理搜索参数
|
||||
const searchQuery = route.query.search as string
|
||||
if (searchQuery) {
|
||||
// 这里可以根据搜索关键词筛选产品
|
||||
console.log('搜索关键词:', searchQuery)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面头部 */
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
||||
color: white;
|
||||
padding: 4rem 0 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 筛选区域 */
|
||||
.filters-section {
|
||||
background: var(--background-light);
|
||||
padding: 2rem 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.filter-group label {
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
padding: 0.5rem 1rem;
|
||||
border: 1px solid var(--border-color);
|
||||
background: white;
|
||||
color: var(--text-color);
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.filter-btn:hover,
|
||||
.filter-btn.active {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* 产品网格 */
|
||||
.products-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.product-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.product-image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.image-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--background-light);
|
||||
}
|
||||
|
||||
.product-badges {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge.new {
|
||||
background: #ff4757;
|
||||
}
|
||||
|
||||
.badge.hot {
|
||||
background: #ff6b35;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.product-category {
|
||||
color: var(--secondary-color);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.product-description {
|
||||
color: var(--text-light);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.product-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.craftsman-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.stock-status {
|
||||
font-size: 0.8rem;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.stock-status.in-stock {
|
||||
background: rgba(46, 160, 67, 0.1);
|
||||
color: #2ea043;
|
||||
}
|
||||
|
||||
.stock-status.out-stock {
|
||||
background: rgba(218, 54, 51, 0.1);
|
||||
color: #da3633;
|
||||
}
|
||||
|
||||
.product-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.price-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.current-price {
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.original-price {
|
||||
font-size: 1rem;
|
||||
color: var(--text-light);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
/* 加载更多 */
|
||||
.load-more {
|
||||
text-align: center;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.filters {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.filter-buttons {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.products-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.product-meta {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.product-footer {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.products-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
1044
src/views/Profile.vue
Normal file
1044
src/views/Profile.vue
Normal file
File diff suppressed because it is too large
Load Diff
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
15
tsconfig.app.json
Normal file
15
tsconfig.app.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
25
tsconfig.node.json
Normal file
25
tsconfig.node.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2023",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
18
vite.config.ts
Normal file
18
vite.config.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
],
|
||||
})
|
Loading…
Reference in New Issue
Block a user