diff --git a/auto-imports.d.ts b/auto-imports.d.ts index 1d89ee8..d7d6df6 100644 --- a/auto-imports.d.ts +++ b/auto-imports.d.ts @@ -5,5 +5,6 @@ // Generated by unplugin-auto-import export {} declare global { - + const ElMessage: typeof import('element-plus/es')['ElMessage'] + const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] } diff --git a/components.d.ts b/components.d.ts index b2134a9..cda9eef 100644 --- a/components.d.ts +++ b/components.d.ts @@ -29,5 +29,6 @@ declare module 'vue' { HelloWorld: typeof import('./src/components/HelloWorld.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + VideoPlayer: typeof import('./src/components/VideoPlayer.vue')['default'] } } diff --git a/src/api/cart.ts b/src/api/cart.ts new file mode 100644 index 0000000..d02e992 --- /dev/null +++ b/src/api/cart.ts @@ -0,0 +1,35 @@ +import api from './index' + +// 购物车接口 +export const cartApi = { + // 获取用户购物车 + getCartByUserId: (userId: string) => { + return api.get(`/oroqen/cart/user/${userId}`) + }, + + // 添加到购物车 + addToCart: (userId: string, productId: string, quantity: number) => { + return api.post('/oroqen/cart/add', null, { + params: { userId, productId, quantity } + }) + }, + + // 更新购物车商品数量 + updateCartQuantity: (userId: string, productId: string, quantity: number) => { + return api.post('/oroqen/cart/update', null, { + params: { userId, productId, quantity } + }) + }, + + // 从购物车移除商品 + removeFromCart: (userId: string, productId: string) => { + return api.post('/oroqen/cart/remove', null, { + params: { userId, productId } + }) + }, + + // 清空购物车 + clearCart: (userId: string) => { + return api.post(`/oroqen/cart/clear/${userId}`) + } +} \ No newline at end of file diff --git a/src/api/culture.ts b/src/api/culture.ts new file mode 100644 index 0000000..a090238 --- /dev/null +++ b/src/api/culture.ts @@ -0,0 +1,52 @@ +import api from './index' + +// 文化内容接口 +export const cultureApi = { + // 获取文化内容列表 + getCultureList: (params: any) => { + return api.get('/oroqen/cultureContent/list', { params }) + }, + + // 获取文化内容详情 + getCultureById: (id: string) => { + return api.get('/oroqen/cultureContent/queryById', { params: { id } }) + }, + + // 获取推荐文化内容 + getRecommendedCulture: () => { + return api.get('/oroqen/cultureContent/recommended') + }, + + // 根据分类获取文化内容 + getCultureByCategory: (categoryId: string, params: any) => { + return api.get(`/oroqen/cultureContent/category/${categoryId}`, { params }) + }, + + // 点赞文化内容 + likeCulture: (id: string) => { + return api.post(`/oroqen/cultureContent/like/${id}`) + }, + + // 取消点赞 + unlikeCulture: (id: string) => { + return api.post(`/oroqen/cultureContent/unlike/${id}`) + } +} + +// 文化内容分类接口 +export const cultureCategoryApi = { + // 获取分类树 + getCategoryTree: () => { + return api.get('/oroqen/cultureCategory/tree') + }, + + // 获取分类列表 + getCategoryList: (params: any) => { + return api.get('/oroqen/cultureCategory/list', { params }) + }, + + // 获取子分类 + getChildCategories: (parentId: string) => { + return api.get(`/oroqen/cultureCategory/children/${parentId}`) + } +} \ No newline at end of file diff --git a/src/api/favorite.ts b/src/api/favorite.ts new file mode 100644 index 0000000..3eead47 --- /dev/null +++ b/src/api/favorite.ts @@ -0,0 +1,32 @@ +import api from './index' + +// 用户收藏接口 +export const favoriteApi = { + // 获取用户收藏列表 + getFavoritesByUserId: (userId: string, favoriteType?: string) => { + const params: any = { userId } + if (favoriteType) params.favoriteType = favoriteType + return api.get('/oroqen/userFavorite/user', { params }) + }, + + // 添加收藏 + addFavorite: (userId: string, itemId: string, favoriteType: string) => { + return api.post('/oroqen/userFavorite/add', null, { + params: { userId, itemId, favoriteType } + }) + }, + + // 取消收藏 + removeFavorite: (userId: string, itemId: string, favoriteType: string) => { + return api.post('/oroqen/userFavorite/remove', null, { + params: { userId, itemId, favoriteType } + }) + }, + + // 检查是否已收藏 + checkFavoriteStatus: (userId: string, itemId: string, favoriteType: string) => { + return api.get('/oroqen/userFavorite/status', { + params: { userId, itemId, favoriteType } + }) + } +} \ No newline at end of file diff --git a/src/api/heritage.ts b/src/api/heritage.ts new file mode 100644 index 0000000..334b4c1 --- /dev/null +++ b/src/api/heritage.ts @@ -0,0 +1,52 @@ +import api from './index' + +// 非遗传承人接口 +export const heritageApi = { + // 获取传承人列表 + getInheritorList: (params: any) => { + return api.get('/oroqen/heritageInheritor/list', { params }) + }, + + // 获取传承人详情 + getInheritorById: (id: string) => { + return api.get('/oroqen/heritageInheritor/queryById', { params: { id } }) + }, + + // 获取所有传承人(不分页) + getAllInheritors: () => { + return api.get('/oroqen/heritageInheritor/list', { + params: { pageNo: 1, pageSize: 1000 } + }) + }, + + // 非遗项目接口 + // 获取非遗项目列表 + getHeritageProjectList: (params: any) => { + return api.get('/oroqen/heritageProject/list', { params }) + }, + + // 获取推荐项目列表 + getRecommendedProjects: () => { + return api.get('/oroqen/heritageProject/recommended') + }, + + // 按级别获取项目列表 + getProjectsByLevel: (level: string) => { + return api.get('/oroqen/heritageProject/byLevel', { params: { level } }) + }, + + // 按类别获取项目列表 + getProjectsByCategory: (category: string) => { + return api.get('/oroqen/heritageProject/byCategory', { params: { category } }) + }, + + // 获取项目详情 + getProjectById: (id: string) => { + return api.get('/oroqen/heritageProject/queryById', { params: { id } }) + }, + + // 获取级别统计 + getLevelStats: () => { + return api.get('/oroqen/heritageProject/levelStats') + } +} \ No newline at end of file diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..5503436 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,47 @@ +import axios from 'axios' + +// 创建axios实例 +const api = axios.create({ + baseURL: 'http://localhost:8080/jeecg-boot', // 后端API地址 + timeout: 10000, + headers: { + 'Content-Type': 'application/json' + } +}) + +// 请求拦截器 +api.interceptors.request.use( + config => { + // 可以在这里添加token等认证信息 + const token = localStorage.getItem('token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + error => { + return Promise.reject(error) + } +) + +// 响应拦截器 +api.interceptors.response.use( + response => { + const { data } = response + if (data.success) { + return data.result || data + } else { + // 创建一个包含完整错误信息的错误对象 + const error = new Error(data.message || '请求失败') + error.code = data.code + error.data = data + return Promise.reject(error) + } + }, + error => { + console.error('API Error:', error) + return Promise.reject(error) + } +) + +export default api \ No newline at end of file diff --git a/src/api/order.ts b/src/api/order.ts new file mode 100644 index 0000000..1480787 --- /dev/null +++ b/src/api/order.ts @@ -0,0 +1,39 @@ +import api from './index' + +// 订单接口 +export const orderApi = { + // 获取用户订单列表 + getOrdersByUserId: (userId: string, status?: string) => { + const params: any = { userId } + if (status) params.status = status + return api.get('/oroqen/order/user', { params }) + }, + + // 创建订单 + createOrder: (orderData: any) => { + return api.post('/oroqen/order/create', orderData) + }, + + // 获取订单详情 + getOrderById: (id: string) => { + return api.get('/oroqen/order/queryById', { params: { id } }) + }, + + // 取消订单 + cancelOrder: (id: string) => { + return api.post(`/oroqen/order/cancel/${id}`) + }, + + // 确认收货 + confirmOrder: (id: string) => { + return api.post(`/oroqen/order/confirm/${id}`) + } +} + +// 订单项接口 +export const orderItemApi = { + // 获取订单项列表 + getOrderItemsByOrderId: (orderId: string) => { + return api.get(`/oroqen/orderItem/order/${orderId}`) + } +} \ No newline at end of file diff --git a/src/api/product.ts b/src/api/product.ts new file mode 100644 index 0000000..fd5c69a --- /dev/null +++ b/src/api/product.ts @@ -0,0 +1,47 @@ +import api from './index' + +// 产品接口 +export const productApi = { + // 获取产品列表 + getProductList: (params: any) => { + return api.get('/oroqen/product/list', { params }) + }, + + // 获取产品详情 + getProductById: (id: string | number) => { + return api.get('/oroqen/product/queryById', { params: { id: id.toString() } }) + }, + + // 获取推荐产品 + getFeaturedProducts: () => { + return api.get('/oroqen/product/featured') + }, + + // 获取热销产品 + getHotProducts: () => { + return api.get('/oroqen/product/hot') + }, + + // 根据分类获取产品 + getProductsByCategory: (categoryId: string, params: any) => { + return api.get(`/oroqen/product/category/${categoryId}`, { params }) + } +} + +// 产品分类接口 +export const productCategoryApi = { + // 获取分类树 + getCategoryTree: () => { + return api.get('/oroqen/productCategory/tree') + }, + + // 获取分类列表 + getCategoryList: (params: any) => { + return api.get('/oroqen/productCategory/list', { params }) + }, + + // 获取子分类 + getChildCategories: (parentId: string) => { + return api.get(`/oroqen/productCategory/children/${parentId}`) + } +} \ No newline at end of file diff --git a/src/router/index.ts b/src/router/index.ts index 29bc1f9..27f7d80 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,6 +1,7 @@ import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' import Culture from '../views/Culture.vue' +import CultureDetail from '../views/CultureDetail.vue' import Heritage from '../views/Heritage.vue' import Products from '../views/Products.vue' import ProductDetail from '../views/ProductDetail.vue' @@ -19,6 +20,11 @@ const routes = [ name: 'Culture', component: Culture }, + { + path: '/culture/:id', + name: 'CultureDetail', + component: CultureDetail + }, { path: '/heritage', name: 'Heritage', diff --git a/src/views/Cart.vue b/src/views/Cart.vue index b7018c3..b6b3e86 100644 --- a/src/views/Cart.vue +++ b/src/views/Cart.vue @@ -196,50 +196,145 @@ import { useRouter } from 'vue-router' import { useMainStore } from '../stores' import { User, ShoppingCart } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' +import { cartApi } from '../api/cart' +import { productApi } from '../api/product' const router = useRouter() const store = useMainStore() // 响应式数据 const selectAll = ref(false) +const loading = ref(false) +const cartItems = ref([]) // 推荐商品 -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 recommendedProducts = ref([]) + +// 获取购物车数据 +const getCartData = async () => { + try { + loading.value = true + // 这里应该从用户信息中获取userId,暂时使用固定值 + const userId = 1 + const response = await cartApi.getUserCart(userId) + + if (response && response.length > 0) { + cartItems.value = response.map((item: any) => ({ + id: item.id, + productId: item.productId, + name: item.productName || '商品名称', + price: item.price || 0, + quantity: item.quantity || 1, + stock: item.stock || 99, + specs: item.specs || '规格:标准版', + craftsman: item.craftsman, + originalPrice: item.originalPrice, + selected: true, + color: getProductColor(item.productId) + })) + } + + // 如果没有数据,使用默认数据 + if (cartItems.value.length === 0) { + cartItems.value = getDefaultCartItems() + } + + } catch (error) { + console.error('获取购物车数据失败:', error) + // 使用默认数据 + cartItems.value = getDefaultCartItems() + } finally { + loading.value = false } -]) +} + +// 获取推荐商品 +const getRecommendedProducts = async () => { + try { + const response = await productApi.getHotProducts() + + if (response?.records && response.records.length > 0) { + recommendedProducts.value = response.records.slice(0, 4).map((product: any) => ({ + id: product.id, + name: product.productName, + price: product.price, + color: getProductColor(product.id) + })) + } + + // 如果没有数据,使用默认数据 + if (recommendedProducts.value.length === 0) { + recommendedProducts.value = getDefaultRecommendedProducts() + } + + } catch (error) { + console.error('获取推荐商品失败:', error) + recommendedProducts.value = getDefaultRecommendedProducts() + } +} + +// 获取默认购物车数据 +const getDefaultCartItems = () => { + return [ + { + id: 1, + productId: 1, + name: '精美桦皮首饰盒', + price: 168, + quantity: 1, + stock: 15, + specs: '规格:标准版', + craftsman: '关小云', + originalPrice: 198, + selected: true, + color: '#8b4513' + }, + { + id: 2, + productId: 3, + name: '手工木雕摆件', + price: 128, + quantity: 2, + stock: 8, + specs: '规格:中号', + craftsman: '关金山', + selected: true, + color: '#2d5016' + } + ] +} + +// 获取默认推荐商品 +const getDefaultRecommendedProducts = () => { + return [ + { + 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) }) @@ -265,36 +360,38 @@ 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) => { +const increaseQuantity = async (item: any) => { if (item.quantity < item.stock) { - store.updateCartItemQuantity(item.id, item.quantity + 1) + try { + await cartApi.updateQuantity(item.id, item.quantity + 1) + item.quantity += 1 + ElMessage.success('数量已更新') + } catch (error) { + console.error('更新数量失败:', error) + ElMessage.error('更新失败') + } } else { ElMessage.warning('库存不足') } } -const decreaseQuantity = (item: any) => { +const decreaseQuantity = async (item: any) => { if (item.quantity > 1) { - store.updateCartItemQuantity(item.id, item.quantity - 1) + try { + await cartApi.updateQuantity(item.id, item.quantity - 1) + item.quantity -= 1 + ElMessage.success('数量已更新') + } catch (error) { + console.error('更新数量失败:', error) + ElMessage.error('更新失败') + } } } @@ -310,19 +407,33 @@ const removeFromCart = async (itemId: number) => { } ) - store.removeFromCart(itemId) + await cartApi.removeItem(itemId) + cartItems.value = cartItems.value.filter(item => item.id !== itemId) ElMessage.success('商品已删除') // 重新计算全选状态 updateSelection() - } catch { - // 用户取消删除 + } catch (error) { + if (error !== 'cancel') { + console.error('删除商品失败:', error) + ElMessage.error('删除失败') + } } } -const addRecommendedToCart = (product: any) => { - store.addToCart(product) - ElMessage.success('已添加到购物车') +const addRecommendedToCart = async (product: any) => { + try { + const userId = 1 // 这里应该从用户信息中获取 + await cartApi.addToCart({ + userId, + productId: product.id, + quantity: 1 + }) + ElMessage.success('已添加到购物车') + } catch (error) { + console.error('添加到购物车失败:', error) + ElMessage.error('添加失败') + } } const checkout = () => { @@ -338,8 +449,8 @@ const checkout = () => { // 生命周期 onMounted(() => { - // 初始化选中状态 - updateSelection() + getCartData() + getRecommendedProducts() }) diff --git a/src/views/Culture.vue b/src/views/Culture.vue index fd896d0..8324755 100644 --- a/src/views/Culture.vue +++ b/src/views/Culture.vue @@ -100,6 +100,45 @@ + +
+
+

文化故事

+
+
+
+ + + + {{ story.title ? story.title.charAt(0) : '文' }} + + +
+
+

{{ story.title }}

+

{{ story.excerpt }}

+ +
+
+
+
+

暂无文化故事内容

+
+
+

加载中...

+
+
+
+
@@ -139,26 +178,43 @@

文化传承人

-
+
-
- - - - - -
-
+
+ + + + + + + + + + + + + +
+

{{ inheritor.name }}

-

{{ inheritor.title }}

-

{{ inheritor.skill }}

-

{{ inheritor.description }}

+

{{ inheritor.title }}

+

{{ inheritor.skill }}

+

{{ inheritor.description }}

+
+

籍贯:{{ inheritor.hometown }}

+

主要成就:{{ inheritor.achievements }}

+
+
+

暂无传承人数据

+
+
+

加载中...

+
@@ -186,115 +242,138 @@ \ No newline at end of file diff --git a/src/views/Heritage.vue b/src/views/Heritage.vue index 3856f21..af34077 100644 --- a/src/views/Heritage.vue +++ b/src/views/Heritage.vue @@ -4,7 +4,7 @@ -
+

传承人故事

-
+
- - - - + + + + + + + + + + + +
@@ -119,20 +185,44 @@

{{ story.story }}

代表作品

-
+
- + + {{ work.substring(0, 2) }} + font-size="10">{{ work.name.substring(0, 2) }}
-

{{ work }}

+

{{ work.name }}

+ + +
+ + 点击查看详情 +
+ + +
+ + 加载更多传承人故事 + +
+ + +
+

已显示全部 {{ inheritorStories.length }} 位传承人的故事

+
@@ -143,20 +233,20 @@
-
15
+
{{ protectionStats.national }}
国家级非遗项目
-
28
+
{{ protectionStats.provincial }}
省级非遗项目
-
45
+
{{ protectionStats.inheritors }}
代表性传承人
-
8
-
传承基地
+
{{ protectionStats.municipal }}
+
市级非遗项目
@@ -198,6 +288,151 @@
+ + +
+
+
+
+ + + + + + + + + + + + + +
+
+
+

{{ selectedInheritor.name }}

+

{{ selectedInheritor.title }}

+
+
+ 年龄: + {{ selectedInheritor.age }}岁 +
+
+ 从艺经验: + {{ selectedInheritor.experience }}年 +
+
+ 专业技艺: + {{ selectedInheritor.skill || '传统技艺' }} +
+
+ 传承级别: + {{ selectedInheritor.level || '市级' }} +
+
+ 籍贯: + {{ selectedInheritor.hometown }} +
+
+ 性别: + {{ selectedInheritor.gender === 1 ? '男' : '女' }} +
+
+ 出生年份: + {{ selectedInheritor.birthYear }}年 +
+
+
+
+ +
+
+

个人简介

+

{{ selectedInheritor.biography || selectedInheritor.story }}

+
+ +
+

传承故事

+

{{ selectedInheritor.story }}

+
+ +
+

主要成就

+

{{ selectedInheritor.achievements || '在传统技艺传承方面做出了重要贡献,培训了多名优秀学徒,参与了多项文化保护项目。' }}

+
+ +
+

代表作品

+
+
+ +
+ +
+ + 暂无图片 +
+
+ + +
+
+

{{ work.name }}

+

{{ work.description }}

+ +
+ {{ work.category || '传统技艺' }} + {{ work.level || '国家级' }} +
+ +
+ {{ tag }} +
+
+ + + +
+
+
+
+ +
+

传承理念

+

{{ selectedInheritor.philosophy || '传承不仅是技艺的延续,更是文化精神的传递。我希望通过自己的努力,让更多年轻人了解和热爱我们的传统文化。' }}

+
+
+
+
+ +
@@ -224,17 +459,86 @@
+ + +
+
+ +
+ + +
+ + + + + + {{ currentImageIndex + 1 }} / {{ previewImages.length }} + + + + + +
+ + +
+
+ +
+
+
+
@@ -510,50 +1210,276 @@ const playCraftVideo = (craft: any) => { width: 100%; border-radius: 12px; overflow: hidden; + position: relative; } -.media-controls { +/* 视频容器样式 - 优化版本 */ +.video-container { + width: 100%; + height: 280px; + border-radius: 16px; + overflow: hidden; + background: #e8ddd4; + padding: 20px; + box-sizing: border-box; + position: relative; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; +} + +.video-container:hover { + transform: translateY(-4px); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15); +} + +.craft-video { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 12px; + background: #d4b896; +} + +/* 视频覆盖层 */ +.video-overlay { position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); + bottom: 20px; + left: 20px; + right: 20px; + background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent); + border-radius: 0 0 12px 12px; + padding: 20px; + color: white; + opacity: 0; + transition: opacity 0.3s ease; } +.video-container:hover .video-overlay { + opacity: 1; +} + +.video-title { + margin: 0 0 5px 0; + font-size: 1.1rem; + font-weight: 600; +} + +.video-category { + margin: 0; + font-size: 0.9rem; + opacity: 0.8; +} + +/* 技艺内容区域优化 */ .craft-content { background: white; - padding: 2rem; - border-radius: 12px; - box-shadow: var(--shadow); + padding: 2.5rem; + border-radius: 16px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); + transition: all 0.3s ease; +} + +.craft-content:hover { + transform: translateY(-2px); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12); +} + +.craft-header { + margin-bottom: 1.5rem; } .craft-title { - font-size: 1.5rem; + font-size: 1.6rem; color: var(--primary-color); margin-bottom: 1rem; + font-weight: 600; +} + +.craft-tags { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; +} + +.craft-tag { + background: linear-gradient(135deg, var(--primary-color), #3a5a1f); + color: white; + padding: 0.3rem 0.8rem; + border-radius: 20px; + font-size: 0.8rem; + font-weight: 500; } .craft-description { color: var(--text-color); - line-height: 1.6; - margin-bottom: 1.5rem; + line-height: 1.7; + margin-bottom: 2rem; + font-size: 1rem; } +/* 传承人信息优化 */ +.craft-inheritor { + margin-bottom: 2rem; + padding: 1.5rem; + background: linear-gradient(135deg, #f8f9fa, #e9ecef); + border-radius: 12px; + border-left: 4px solid var(--primary-color); +} + +.inheritor-info { + display: flex; + align-items: center; + gap: 1rem; +} + +.inheritor-avatar-small { + flex-shrink: 0; + width: 40px; + height: 40px; + border-radius: 50%; + overflow: hidden; + background: white; + display: flex; + align-items: center; + justify-content: center; +} + +.inheritor-details { + flex: 1; +} + +.inheritor-name { + margin: 0 0 0.3rem 0; + color: var(--text-color); + font-size: 0.95rem; +} + +.inheritor-level { + margin: 0; + color: var(--text-secondary); + font-size: 0.85rem; +} + +/* 制作工艺优化 */ .craft-steps h4 { color: var(--primary-color); - margin-bottom: 1rem; + margin-bottom: 1.5rem; + font-size: 1.2rem; + font-weight: 600; } -.steps-list { +.steps-container { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.step-item { + display: flex; + align-items: flex-start; + gap: 1rem; + padding: 1rem; + background: #f8f9fa; + border-radius: 10px; + transition: all 0.3s ease; +} + +.step-item:hover { + background: #e9ecef; + transform: translateX(5px); +} + +.step-number { + width: 28px; + height: 28px; + background: var(--primary-color); + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: 0.9rem; + flex-shrink: 0; +} + +.step-content { + flex: 1; color: var(--text-color); - padding-left: 1.5rem; -} - -.steps-list li { - margin-bottom: 0.5rem; line-height: 1.5; + font-size: 0.95rem; } -/* 传承人故事 */ +.more-steps { + text-align: center; + padding: 1rem; + color: var(--text-secondary); + font-style: italic; + background: #f1f3f4; + border-radius: 8px; + border: 2px dashed #dee2e6; +} + +.more-steps span { + font-size: 0.9rem; +} + +/* 传承人头像样式 */ +.avatar-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; +} + +.large-avatar-image { + width: 120px; + height: 120px; + object-fit: cover; + border-radius: 50%; +} + +.avatar-placeholder { + width: 80px; + height: 80px; + border-radius: 50%; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +} + +.default-avatar { + border-radius: 50%; + transition: transform 0.3s ease, filter 0.3s ease; + cursor: pointer; +} + +.default-avatar:hover { + transform: scale(1.05); + filter: brightness(1.1); +} + +.large-avatar-placeholder { + width: 120px; + height: 120px; + border-radius: 50%; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +} + +.default-large-avatar { + border-radius: 50%; + transition: transform 0.3s ease, filter 0.3s ease; + cursor: pointer; +} + +.default-large-avatar:hover { + transform: scale(1.02); + filter: brightness(1.1); +} .inheritors-stories { display: grid; gap: 2rem; @@ -567,45 +1493,89 @@ const playCraftVideo = (craft: any) => { display: flex; align-items: center; gap: 1.5rem; - margin-bottom: 2rem; + margin-bottom: 1.5rem; } -.avatar-placeholder { +.inheritor-avatar { + flex-shrink: 0; + width: 80px; + height: 80px; border-radius: 50%; overflow: hidden; } -.inheritor-name { - font-size: 1.3rem; - color: var(--primary-color); - margin-bottom: 0.5rem; +.inheritor-large-avatar { + flex-shrink: 0; + width: 120px; + height: 120px; + border-radius: 50%; + overflow: hidden; } -.inheritor-title { - color: var(--secondary-color); - font-weight: 500; - margin-bottom: 0.5rem; +.avatar-placeholder { + width: 80px; + height: 80px; + border-radius: 50%; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; } -.inheritor-age { - color: var(--text-light); +.avatar-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; +} + +.large-avatar-placeholder { + width: 120px; + height: 120px; + border-radius: 50%; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; +} + +.large-avatar-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 50%; +} + +.inheritor-basic h3 { + margin: 0 0 0.5rem 0; + color: var(--text-primary); +} + +.inheritor-basic p { + margin: 0.25rem 0; + color: var(--text-secondary); font-size: 0.9rem; } .story-content h4 { color: var(--primary-color); - margin: 1.5rem 0 1rem; + margin: 1.5rem 0 1rem 0; + font-size: 1.1rem; } .story-content p { - color: var(--text-color); - line-height: 1.8; - margin-bottom: 1rem; + line-height: 1.6; + color: var(--text-primary); + margin-bottom: 1.5rem; } .works-grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 1rem; } @@ -615,84 +1585,77 @@ const playCraftVideo = (craft: any) => { .work-image { margin-bottom: 0.5rem; - border-radius: 4px; + border-radius: 8px; overflow: hidden; + width: 100%; + height: 80px; + display: flex; + align-items: center; + justify-content: center; + background: var(--background-light); +} + +.work-image .work-cover-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 8px; } .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); + color: var(--text-secondary); + margin: 0 0 0.3rem 0; font-weight: 500; } -.protection-measures h3 { - font-size: 1.5rem; +.work-category { + font-size: 0.8rem; color: var(--primary-color); - margin-bottom: 2rem; + margin: 0 0 0.2rem 0; +} + +.work-level { + font-size: 0.75rem; + color: var(--text-light); + margin: 0; +} + +/* 加载更多按钮样式 */ +.load-more-container { text-align: center; + margin-top: 3rem; + padding: 2rem 0; } -.measures-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 2rem; +.load-more-container .el-button { + padding: 12px 32px; + font-size: 16px; + border-radius: 25px; + box-shadow: 0 4px 12px rgba(45, 80, 22, 0.2); + transition: all 0.3s ease; } -.measure-item { - background: white; - padding: 2rem; +.load-more-container .el-button:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(45, 80, 22, 0.3); +} + +/* 全部加载完成提示样式 */ +.all-loaded-tip { + text-align: center; + margin-top: 2rem; + padding: 1.5rem; + background: var(--background-light); border-radius: 12px; - text-align: center; - box-shadow: var(--shadow); + border: 1px solid var(--border-color); } -.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; +.all-loaded-tip p { + margin: 0; + color: var(--text-secondary); + font-size: 14px; + font-style: italic; } .detail-content h3 { @@ -720,6 +1683,189 @@ const playCraftVideo = (craft: any) => { line-height: 1.5; } +/* 保护现状与成果样式 */ +.protection-overview { + display: grid; + gap: 3rem; +} + +.protection-stats { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 2rem; + margin-bottom: 3rem; +} + +.stat-card { + background: linear-gradient(135deg, #ffffff, #f8f9fa); + padding: 2rem; + border-radius: 16px; + text-align: center; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(45, 80, 22, 0.1); + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.stat-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); +} + +.stat-card:hover { + transform: translateY(-8px); + box-shadow: 0 16px 48px rgba(0, 0, 0, 0.15); +} + +.stat-number { + font-size: 3rem; + font-weight: bold; + color: var(--primary-color); + margin-bottom: 0.5rem; + text-shadow: 0 2px 4px rgba(45, 80, 22, 0.1); +} + +.stat-label { + font-size: 1.1rem; + color: var(--text-secondary); + font-weight: 500; + letter-spacing: 0.5px; +} + +.protection-measures { + background: linear-gradient(135deg, #f8f9fa, #ffffff); + padding: 3rem; + border-radius: 20px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08); + border: 1px solid rgba(45, 80, 22, 0.1); +} + +.protection-measures h3 { + font-size: 2rem; + color: var(--primary-color); + text-align: center; + margin-bottom: 2.5rem; + font-weight: bold; + position: relative; +} + +.protection-measures h3::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)); + border-radius: 2px; +} + +.measures-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; +} + +.measure-item { + background: white; + padding: 2rem; + border-radius: 16px; + text-align: center; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + border: 1px solid rgba(45, 80, 22, 0.05); + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.measure-item::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + transform: scaleX(0); + transition: transform 0.3s ease; +} + +.measure-item:hover::before { + transform: scaleX(1); +} + +.measure-item:hover { + transform: translateY(-6px); + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15); +} + +.measure-icon { + width: 80px; + height: 80px; + margin: 0 auto 1.5rem; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + box-shadow: 0 8px 24px rgba(45, 80, 22, 0.3); + transition: all 0.3s ease; +} + +.measure-item:hover .measure-icon { + transform: scale(1.1); + box-shadow: 0 12px 32px rgba(45, 80, 22, 0.4); +} + +.measure-item h4 { + font-size: 1.3rem; + color: var(--primary-color); + margin-bottom: 1rem; + font-weight: bold; +} + +.measure-item p { + color: var(--text-secondary); + line-height: 1.6; + font-size: 1rem; + margin: 0; +} + +/* 保护现状部分的动画效果 */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.stat-card, +.measure-item { + animation: fadeInUp 0.6s ease forwards; +} + +.stat-card:nth-child(1) { animation-delay: 0.1s; } +.stat-card:nth-child(2) { animation-delay: 0.2s; } +.stat-card:nth-child(3) { animation-delay: 0.3s; } +.stat-card:nth-child(4) { animation-delay: 0.4s; } + +.measure-item:nth-child(1) { animation-delay: 0.5s; } +.measure-item:nth-child(2) { animation-delay: 0.6s; } +.measure-item:nth-child(3) { animation-delay: 0.7s; } +.measure-item:nth-child(4) { animation-delay: 0.8s; } + /* 响应式设计 */ @media (max-width: 768px) { .page-title { @@ -760,4 +1906,409 @@ const playCraftVideo = (craft: any) => { grid-template-columns: 1fr; } } + +/* 传承人故事卡片点击样式 */ +.clickable-card { + cursor: pointer; + position: relative; + transition: all 0.3s ease; +} + +.clickable-card:hover { + transform: translateY(-4px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); +} + +.click-hint { + position: absolute; + top: 1rem; + right: 1rem; + display: flex; + align-items: center; + gap: 0.5rem; + color: var(--primary-color); + font-size: 0.9rem; + opacity: 0; + transition: opacity 0.3s ease; +} + +.clickable-card:hover .click-hint { + opacity: 1; +} + +/* 传承人详情对话框样式 */ +.inheritor-detail { + max-height: 70vh; + overflow-y: auto; +} + +.inheritor-detail-header { + display: flex; + align-items: flex-start; + gap: 2rem; + margin-bottom: 2rem; + padding-bottom: 2rem; + border-bottom: 2px solid var(--border-color); +} + +.inheritor-large-avatar { + flex-shrink: 0; +} + +.large-avatar-placeholder { + width: 120px; + height: 120px; + border-radius: 50%; + overflow: hidden; + background: var(--background-light); + border: 3px solid var(--primary-color); +} + +.inheritor-detail-info { + flex: 1; +} + +.inheritor-detail-name { + font-size: 2rem; + color: var(--primary-color); + margin: 0 0 0.5rem 0; + font-weight: bold; +} + +.inheritor-detail-title { + font-size: 1.2rem; + color: var(--secondary-color); + margin: 0 0 1.5rem 0; + font-weight: 500; +} + +.inheritor-meta { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; +} + +.meta-item { + display: flex; + align-items: center; +} + +.meta-label { + font-weight: 500; + color: var(--text-secondary); + min-width: 80px; +} + +.meta-value { + color: var(--text-primary); + font-weight: 600; +} + +.inheritor-detail-content { + display: grid; + gap: 2rem; +} + +.detail-section { + background: var(--background-light); + padding: 1.5rem; + border-radius: 12px; + border-left: 4px solid var(--primary-color); +} + +.detail-section .section-title { + font-size: 1.3rem; + color: var(--primary-color); + margin: 0 0 1rem 0; + font-weight: bold; +} + +.detail-section .section-content { + color: var(--text-primary); + line-height: 1.8; + margin: 0; + font-size: 1rem; +} + +.works-showcase { + display: flex; + flex-direction: column; + gap: 16px; + margin-top: 1rem; +} + +.work-showcase-item { + display: flex; + align-items: flex-start; + gap: 16px; + padding: 16px; + background: white; + border-radius: 8px; + border: 1px solid #e9ecef; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.3s ease; +} + +.work-showcase-item:hover { + transform: translateY(-2px); +} + +.work-showcase-image { + flex-shrink: 0; + width: 150px; + height: 120px; + border-radius: 8px; + overflow: hidden; + position: relative; + background: #f0f0f0; + display: flex; + align-items: center; + justify-content: center; +} + +.work-showcase-image.no-image { + background: #f8f9fa; + border: 2px dashed #ddd; +} + +.no-image-placeholder { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + color: #999; + font-size: 12px; +} + +.work-cover-image { + width: 100%; + height: 100%; + object-fit: cover; + cursor: pointer; + transition: transform 0.3s ease; +} + +.work-cover-image:hover { + transform: scale(1.05); +} + +.image-count-badge { + position: absolute; + top: 8px; + right: 8px; + background: rgba(0, 0, 0, 0.7); + color: white; + padding: 4px 8px; + border-radius: 12px; + font-size: 12px; + display: flex; + align-items: center; + gap: 4px; +} + +.work-content { + flex: 1; + display: flex; + gap: 16px; +} + +.work-info { + flex: 1; +} + +.work-gallery { + flex-shrink: 0; + width: 120px; +} + +.gallery-title { + font-size: 12px; + color: #666; + margin-bottom: 8px; + font-weight: 500; +} + +.gallery-thumbnails { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 4px; +} + +.gallery-thumb { + width: 56px; + height: 44px; + border-radius: 4px; + overflow: hidden; + cursor: pointer; + position: relative; + border: 2px solid transparent; + transition: all 0.3s ease; +} + +.gallery-thumb:hover { + border-color: var(--primary-color); + transform: scale(1.05); +} + +.gallery-thumb img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.thumb-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 0.3s ease; + color: white; +} + +.gallery-thumb:hover .thumb-overlay { + opacity: 1; +} + +.work-showcase-name { + margin: 0 0 8px 0; + font-size: 16px; + font-weight: 600; + color: var(--primary-color); +} + +.work-showcase-desc { + margin: 0 0 12px 0; + color: var(--text-secondary); + font-size: 14px; + line-height: 1.5; +} + +.work-meta-info { + display: flex; + gap: 8px; + margin-bottom: 8px; +} + +.work-meta-item { + display: flex; + align-items: center; +} + +.work-category { + background: linear-gradient(135deg, #4CAF50, #45a049); + color: white; + padding: 4px 8px; + border-radius: 12px; + font-size: 12px; + font-weight: 500; + box-shadow: 0 2px 4px rgba(76, 175, 80, 0.3); +} + +.work-level { + background: linear-gradient(135deg, #8B4513, #A0522D); + color: white; + padding: 4px 8px; + border-radius: 12px; + font-size: 12px; + font-weight: 500; + box-shadow: 0 2px 4px rgba(139, 69, 19, 0.3); +} + +.work-tags { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.work-tag { + background: var(--background-light); + color: var(--text-secondary); + padding: 2px 6px; + border-radius: 8px; + font-size: 11px; + border: 1px solid var(--border-color); +} + +/* 图片预览对话框样式 */ +.image-preview-dialog .el-dialog__body { + padding: 0; +} + +.image-preview-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + padding: 20px; +} + +.preview-main { + width: 100%; + max-height: 60vh; + display: flex; + justify-content: center; + align-items: center; + background: #f5f5f5; + border-radius: 8px; + overflow: hidden; +} + +.preview-image { + max-width: 100%; + max-height: 60vh; + object-fit: contain; + border-radius: 8px; +} + +.preview-navigation { + display: flex; + align-items: center; + gap: 20px; + margin: 16px 0; +} + +.image-counter { + font-size: 16px; + font-weight: 500; + color: var(--text-primary); + min-width: 80px; + text-align: center; +} + +.preview-thumbnails { + display: flex; + gap: 8px; + max-width: 100%; + overflow-x: auto; + padding: 8px 0; +} + +.preview-thumb { + flex-shrink: 0; + width: 60px; + height: 48px; + border-radius: 4px; + overflow: hidden; + cursor: pointer; + border: 2px solid transparent; + transition: all 0.3s ease; +} + +.preview-thumb:hover, +.preview-thumb.active { + border-color: var(--primary-color); + transform: scale(1.05); +} + +.preview-thumb img { + width: 100%; + height: 100%; + object-fit: cover; +} \ No newline at end of file diff --git a/src/views/Home.vue b/src/views/Home.vue index a85c6bf..d84f01a 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -146,7 +146,7 @@

{{ story.excerpt }}

@@ -178,77 +178,195 @@
+ + + +
+ +
+
+ + + + {{ selectedStory.title.substring(0, 2) }} + +
+ +
+ + +
+

内容简介

+

{{ selectedStory.excerpt }}

+ +
+

详细内容

+
+
+ + +
+ + +
+ 查看完整详情 + 关闭 +
+
+ +
+ +

加载中...

+
+