7.9 KiB
7.9 KiB
实现竖屏移动端卡片展示组件
组件设计目标
- 适配竖屏移动端的卡片展示组件
- 包含图片展示、加载蒙层、预览功能
- 底部功能按钮(修改、编辑、场景图、下载、删除)
- 向父组件抛出事件
实现步骤
1. 实现 shu.vue 组件
- 设计组件结构:主容器、图片区域、功能按钮区域
- 实现响应式布局,适配竖屏移动端
- 使用 Scoped CSS + CSS 变量实现样式隔离与主题定制
2. 图片展示与加载效果
- 使用
el-image组件实现图片展示 - 添加加载状态管理,显示加载蒙层
- 实现图片加载完成后的过渡效果
3. 图片预览功能
- 集成图片预览功能,适配 H5 移动端
- 点击图片触发预览,支持手势操作
4. 底部功能按钮
- 实现修改、编辑、场景图、下载、删除按钮
- 每个按钮向父组件抛出对应的事件
- 按钮样式设计符合 UI/UX 要求
5. 与父组件交互
- 在
CreateProjectShu.vue中引入并使用shu.vue组件 - 实现事件处理逻辑,接收并处理子组件抛出的事件
代码实现
shu.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缓入缓出)、加载蒙层
测试与验证
- 确保组件在竖屏移动端正常显示
- 测试图片加载、预览功能
- 验证按钮事件正确抛出
- 检查响应式布局适配情况