deotalandAi/.trae/documents/修改手机号登录逻辑.md

8.0 KiB
Raw Blame History

实现竖屏移动端卡片展示组件

组件设计目标

  • 适配竖屏移动端的卡片展示组件

  • 包含图片展示、加载蒙层、预览功能

  • 底部功能按钮(修改、编辑、场景图、下载、删除)

  • 向父组件抛出事件

实现步骤

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缓入缓出、加载蒙层

测试与验证

  • 确保组件在竖屏移动端正常显示

  • 测试图片加载、预览功能

  • 验证按钮事件正确抛出

  • 检查响应式布局适配情况