基于Vue.js实现卡片右侧/底部动态定位与可配置尺寸卡片添加功能
Vue 动态可扩展网格卡片实现方案
功能特性
我们要实现的卡片扩展功能支持以下能力:
- 页面初始化时自动渲染首张基础卡片
- 每张卡片自带两个操作按钮:点击右侧按钮将在当前卡片右侧新增卡片,点击底部按钮将在当前卡片下方新增卡片
- 所有尺寸参数支持动态配置:卡片宽度、卡片高度、卡片横向间距、卡片纵向间距
实现思路
用二维数组存储卡片网格结构,数组第一层对应行,第二层对应每行内的卡片,每个卡片存储唯一标识用于渲染区分。操作按钮触发时对应修改二维数组结构即可实现新增效果:
- 右侧新增:找到当前卡片所在行的下标位置,在该位置后插入新卡片元素
- 底部新增:在二维数组末尾新增一行,行内放入新卡片元素
核心实现代码
模板代码
<template> <div class="card-container" :style="containerStyle"> <div v-for="(row, rowIndex) in cardGrid" :key="`row-${rowIndex}`" class="card-row" :style="rowStyle"> <div v-for="(card, colIndex) in row" :key="card.id" class="card-item" :style="cardStyle"> <!-- 卡片自定义内容区域 --> <div class="card-content">{{ card.content }}</div> <!-- 右侧新增按钮 --> <button class="add-btn right-btn" @click="addRightCard(rowIndex, colIndex)">+</button> <!-- 底部新增按钮,可去掉v-if实现每张卡片都显示底部新增按钮 --> <button v-if="colIndex === row.length - 1" class="add-btn bottom-btn" @click="addBottomCard()">+</button> </div> </div> </div> </template>
逻辑代码
<script> export default { name: 'DynamicCardGrid', props: { // 卡片宽度,单位px cardWidth: { type: Number, default: 200 }, // 卡片高度,单位px cardHeight: { type: Number, default: 150 }, // 卡片之间的横向间距,单位px horizontalGap: { type: Number, default: 16 }, // 卡片之间的纵向间距,单位px verticalGap: { type: Number, default: 16 } }, data() { return { // 卡片网格二维数据,初始化自带第一张卡片 cardGrid: [ [{ id: Date.now(), content: '卡片1' }] ], idCounter: 1 } }, computed: { containerStyle() { return { padding: `${this.verticalGap}px ${this.horizontalGap}px` } }, rowStyle() { return { marginBottom: `${this.verticalGap}px`, gap: `${this.horizontalGap}px` } }, cardStyle() { return { width: `${this.cardWidth}px`, height: `${this.cardHeight}px` } } }, methods: { // 右侧新增卡片 addRightCard(rowIndex, colIndex) { this.idCounter++ const newCard = { id: Date.now() + this.idCounter, content: `卡片${this.idCounter}` } this.cardGrid[rowIndex].splice(colIndex + 1, 0, newCard) }, // 底部新增卡片 addBottomCard() { this.idCounter++ const newCard = { id: Date.now() + this.idCounter, content: `卡片${this.idCounter}` } this.cardGrid.push([newCard]) } } } </script>
样式代码
<style scoped> .card-container { box-sizing: border-box; width: 100%; } .card-row { display: flex; align-items: flex-start; } .card-item { position: relative; border: 1px solid #e5e7eb; border-radius: 8px; background: #fff; box-shadow: 0 2px 8px rgba(0,0,0,0.08); } .card-content { padding: 12px; width: 100%; height: 100%; box-sizing: border-box; } .add-btn { position: absolute; width: 24px; height: 24px; border-radius: 50%; border: none; background: #165dff; color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 16px; line-height: 1; } .right-btn { right: -12px; top: 50%; transform: translateY(-50%); } .bottom-btn { bottom: -12px; left: 50%; transform: translateX(-50%); } .add-btn:hover { background: #0f4bd1; } </style>
组件使用方式
在父组件引入后可直接通过props自定义配置参数:
<DynamicCardGrid :cardWidth="220" :cardHeight="160" :horizontalGap="20" :verticalGap="20" />
内容的提问来源于stack exchange,提问作者Chrollo Desu
相关产品推荐
相关产品推荐

