deotalandAi/.trae/documents/实现竖屏移动端卡片展示组件.md

354 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# 实现竖屏移动端卡片展示组件
## 组件设计目标
- 适配竖屏移动端的卡片展示组件
- 包含图片展示、加载蒙层、预览功能
- 底部功能按钮(修改、编辑、场景图、下载、删除)
- 向父组件抛出事件
## 实现步骤
### 1. 实现 `shu.vue` 组件
- 设计组件结构:主容器、图片区域、功能按钮区域
- 实现响应式布局,适配竖屏移动端
- 使用 Scoped CSS + CSS 变量实现样式隔离与主题定制
### 2. 图片展示与加载效果
- 使用 `el-image` 组件实现图片展示
- 添加加载状态管理,显示加载蒙层
- 实现图片加载完成后的过渡效果
### 3. 图片预览功能
- 集成图片预览功能,适配 H5 移动端
- 点击图片触发预览,支持手势操作
### 4. 底部功能按钮
- 实现修改、编辑、场景图、下载、删除按钮
- 每个按钮向父组件抛出对应的事件
- 按钮样式设计符合 UI/UX 要求
### 5. 与父组件交互
-`CreateProjectShu.vue` 中引入并使用 `shu.vue` 组件
- 实现事件处理逻辑,接收并处理子组件抛出的事件
## 代码实现
### `shu.vue` 组件
```vue
<template>
<div class="shu-card-container">
<!-- 图片展示区域 -->
<div class="image-wrapper" @click="handlePreview">
<el-image
:src="props.imageUrl"
:fit="'cover'"
:preview-src-list="[props.imageUrl]"
@load="handleImageLoad"
@error="handleImageError"
>
<!-- 加载蒙层 -->
<template #loading>
<div class="loading-mask">
<div class="loading-spinner"></div>
<div class="loading-text">图片加载中...</div>
</div>
</template>
</el-image>
</div>
<!-- 功能按钮区域 -->
<div class="action-buttons">
<button class="action-btn" @click="handleModify" title="修改">
<el-icon class="btn-icon"><ChatDotRound /></el-icon>
<span class="btn-text">修改</span>
</button>
<button class="action-btn" @click="handleEdit" title="编辑">
<el-icon class="btn-icon"><EditPen /></el-icon>
<span class="btn-text">编辑</span>
</button>
<button class="action-btn" @click="handleScene" title="场景图">
<el-icon class="btn-icon"><Grid /></el-icon>
<span class="btn-text">场景图</span>
</button>
<button class="action-btn" @click="handleDownload" title="下载">
<el-icon class="btn-icon"><Download /></el-icon>
<span class="btn-text">下载</span>
</button>
<button class="action-btn delete-btn" @click="handleDelete" title="删除">
<el-icon class="btn-icon"><Delete /></el-icon>
<span class="btn-text">删除</span>
</button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { ElImage, ElIcon } from 'element-plus';
import { ChatDotRound, EditPen, Grid, Download, Delete } from '@element-plus/icons-vue';
// 定义组件属性
const props = defineProps({
// 图片URL
imageUrl: {
type: String,
default: ''
},
// 卡片数据
cardData: {
type: Object,
default: () => ({})
}
});
// 定义事件
const emit = defineEmits(['preview', 'modify', 'edit', 'scene', 'download', 'delete']);
// 图片加载状态
const isLoading = ref(true);
// 处理图片加载完成
const handleImageLoad = () => {
isLoading.value = false;
};
// 处理图片加载错误
const handleImageError = () => {
isLoading.value = false;
};
// 处理图片预览
const handlePreview = () => {
emit('preview', props.imageUrl);
};
// 处理修改按钮点击
const handleModify = (e) => {
e.stopPropagation();
emit('modify', props.cardData);
};
// 处理编辑按钮点击
const handleEdit = (e) => {
e.stopPropagation();
emit('edit', props.cardData);
};
// 处理场景图按钮点击
const handleScene = (e) => {
e.stopPropagation();
emit('scene', props.cardData);
};
// 处理下载按钮点击
const handleDownload = (e) => {
e.stopPropagation();
emit('download', props.imageUrl);
};
// 处理删除按钮点击
const handleDelete = (e) => {
e.stopPropagation();
emit('delete', props.cardData);
};
</script>
<style scoped>
/* 组件样式 */
.shu-card-container {
display: flex;
flex-direction: column;
width: 100%;
max-width: 400px;
margin: 0 auto;
background: #ffffff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}
/* 图片区域样式 */
.image-wrapper {
position: relative;
width: 100%;
padding-bottom: 150%; /* 2:3 竖屏比例 */
overflow: hidden;
cursor: pointer;
}
.image-wrapper :deep(.el-image) {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: transform 0.3s ease;
}
.image-wrapper :deep(.el-image__inner) {
object-fit: cover;
transition: transform 0.3s ease;
}
.image-wrapper:hover :deep(.el-image__inner) {
transform: scale(1.02);
}
/* 加载蒙层样式 */
.loading-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.9) 0%, rgba(248, 249, 250, 0.95) 100%);
transition: opacity 0.3s ease;
z-index: 10;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(107, 70, 193, 0.2);
border-top: 4px solid #6B46C1;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 12px;
}
.loading-text {
font-size: 14px;
color: #6B46C1;
font-weight: 500;
}
/* 功能按钮区域样式 */
.action-buttons {
display: flex;
flex-wrap: wrap;
padding: 12px;
background: #f8f9fa;
border-top: 1px solid #e9ecef;
}
.action-btn {
flex: 1;
min-width: calc(20% - 8px);
margin: 4px;
padding: 12px 8px;
border: none;
border-radius: 8px;
background: linear-gradient(135deg, #6B46C1 0%, #A78BFA 100%);
color: #ffffff;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
}
.action-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(107, 70, 193, 0.3);
}
.action-btn:active {
transform: translateY(0);
}
.action-btn.delete-btn {
background: linear-gradient(135deg, #EF4444 0%, #F87171 100%);
}
.action-btn.delete-btn:hover {
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
}
.btn-icon {
font-size: 20px;
}
.btn-text {
font-size: 12px;
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.shu-card-container {
margin: 8px;
border-radius: 8px;
}
.action-buttons {
padding: 8px;
}
.action-btn {
padding: 10px 6px;
min-width: calc(20% - 6px);
margin: 3px;
}
.btn-icon {
font-size: 18px;
}
.btn-text {
font-size: 11px;
}
}
@media (max-width: 480px) {
.shu-card-container {
margin: 4px;
}
.action-btn {
padding: 8px 4px;
}
.btn-icon {
font-size: 16px;
}
.btn-text {
font-size: 10px;
}
}
/* 动画效果 */
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
```
### 2. 集成到父组件 `CreateProjectShu.vue`
- 在父组件中引入 `shu.vue` 组件
- 实现事件处理逻辑,接收子组件抛出的事件
- 在模板中使用新组件展示卡片
## 设计规范
- 主色调:深紫色(#6B46C1、浅紫色#A78BFA
- 辅助色:深灰色(#1F2937、浅灰色#F3F4F6
- 按钮样式圆角设计8px半径、微妙阴影、悬停效果
- 字体排版Inter字体系列、16px基础大小、响应式缩放
- 布局风格基于卡片的设计、统一间距8px网格系统
- 动画效果平滑过渡200ms缓入缓出、加载蒙层
## 测试与验证
- 确保组件在竖屏移动端正常显示
- 测试图片加载、预览功能
- 验证按钮事件正确抛出
- 检查响应式布局适配情况