Files
test-repo/docs/.vitepress/theme/components/appendix/frontend-evolution/ResponsiveGridDemo.vue
T
sanbuphy d35211071a style: update border-radius and padding values across components
- standardize border-radius from 8px to 6px for consistent styling
- adjust padding values from 1rem to 0.75rem for better visual hierarchy
- remove redundant overflow-y properties for cleaner code
2026-02-14 20:23:34 +08:00

542 lines
12 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
<!--
ResponsiveGridDemo.vue - 魔法衣柜
"衣服自动叠放"的比喻来解释响应式布局
-->
<template>
<div class="magic-closet">
<!-- 故事开头 -->
<div class="story-box">
<div class="story-emoji">👗🚪</div>
<h4 class="story-title">小美的魔法衣柜</h4>
<p class="story-text">
小美有一件神奇的魔法衣柜不管你把它放在大房间还是小房间<br>
<strong>里面的衣服都会自动叠好排好完美适应空间大小</strong>
</p>
</div>
<!-- 衣柜宽度调节 -->
<div class="closet-control">
<div class="control-label">
<span>🚪 拖动把手把衣柜放进不同房间</span>
<span class="room-label">{{ currentRoom.name }}</span>
</div>
<div class="slider-box">
<span class="slider-emoji">🏠</span>
<input
type="range"
v-model="closetWidth"
:min="280"
:max="900"
step="10"
class="magic-slider"
/>
<span class="slider-emoji">🏰</span>
</div>
<div class="width-hint">
当前衣柜宽度<strong>{{ closetWidth }}px</strong> | 可以放下 <strong>{{ clothesPerRow }}</strong> 件衣服
</div>
</div>
<!-- 魔法衣柜展示 -->
<div class="closet-display" :style="{ width: closetWidth + 'px' }">
<div class="closet-header">
<span class="closet-icon">🚪</span>
<span class="closet-title">小美的魔法衣柜</span>
<span class="closet-icon">🪄</span>
</div>
<div class="closet-interior">
<div class="clothes-rack" :style="rackStyle">
<div
v-for="(item, index) in clothes"
:key="index"
class="clothing-item"
:class="{ 'folded': isSmallSpace }"
:style="{ animationDelay: (index * 0.1) + 's' }"
>
<div class="item-hanger">🪝</div>
<div class="item-emoji">{{ item.emoji }}</div>
<div class="item-name">{{ item.name }}</div>
<div class="fold-hint" v-if="isSmallSpace">叠好了!</div>
</div>
</div>
</div>
<div class="closet-footer">
<span> 衣服数量{{ clothes.length }}</span>
<span>📐 排列方式{{ arrangementMode }}</span>
</div>
</div>
<!-- 魔法原理说明 -->
<div class="magic-explain">
<div class="explain-title">🔮 魔法原理揭秘</div>
<div class="explain-cards">
<div class="explain-card">
<div class="card-icon">📱</div>
<div class="card-title">小房间手机</div>
<div class="card-desc">衣柜只有 320px 衣服会自动叠起来<strong>1</strong>排开</div>
</div>
<div class="explain-arrow"></div>
<div class="explain-card">
<div class="card-icon">📲</div>
<div class="card-title">中房间平板</div>
<div class="card-desc">衣柜有 768px 衣服舒展开<strong>2</strong>排开</div>
</div>
<div class="explain-arrow"></div>
<div class="explain-card">
<div class="card-icon">💻</div>
<div class="card-title">大房间电脑</div>
<div class="card-desc">衣柜有 1200px 衣服完全展开<strong>3</strong>排开</div>
</div>
</div>
</div>
<!-- 代码展示 -->
<div class="code-section">
<div class="code-header">
<span>💻 魔法咒语CSS代码</span>
<span class="code-tag">CSS</span>
</div>
<pre class="code-content"><code>/* 默认:小房间,衣服叠成1列 */
.closet {
display: grid;
gap: 10px;
grid-template-columns: 1fr; /* 1列 */
}
/* 中房间:衣服排成2列 */
@media (min-width: 640px) {
.closet {
grid-template-columns: repeat(2, 1fr); /* 2列 */
}
}
/* 大房间:衣服排成3列 */
@media (min-width: 1024px) {
.closet {
grid-template-columns: repeat(3, 1fr); /* 3列 */
}
}</code></pre>
</div>
<!-- 总结 -->
<div class="summary-box">
<div class="summary-icon">🎯</div>
<div class="summary-content">
<strong>关键 takeaway</strong>
响应式布局就像小美的魔法衣柜<strong>同一套衣服内容</strong>
会根据<strong>房间大小屏幕宽度</strong>自动调整排列方式
这就是 CSS 媒体查询Media Query的魔法
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
// 衣柜宽度(模拟屏幕宽度)
const closetWidth = ref(375)
// 房间类型
const rooms = [
{ name: '小房间(手机)', min: 280, max: 639, cols: 1, icon: '📱' },
{ name: '中房间(平板)', min: 640, max: 1023, cols: 2, icon: '📲' },
{ name: '大房间(电脑)', min: 1024, max: 900, cols: 3, icon: '💻' }
]
// 当前房间
const currentRoom = computed(() => {
const room = rooms.find(r => closetWidth.value >= r.min && closetWidth.value <= r.max)
return room || rooms[0]
})
// 每行衣服数量
const clothesPerRow = computed(() => currentRoom.value.cols)
// 是否小空间(需要叠衣服)
const isSmallSpace = computed(() => closetWidth.value < 500)
// 排列模式文字
const arrangementMode = computed(() => {
if (closetWidth.value < 640) return '小空间模式(叠放)'
if (closetWidth.value < 1024) return '中等空间(舒展)'
return '大空间(完全展开)'
})
// 衣柜网格样式
const rackStyle = computed(() => {
const cols = currentRoom.value.cols
return {
display: 'grid',
gridTemplateColumns: `repeat(${cols}, 1fr)`,
gap: '10px'
}
})
// 衣服列表
const clothes = [
{ emoji: '👗', name: '连衣裙' },
{ emoji: '👔', name: '衬衫' },
{ emoji: '👖', name: '牛仔裤' },
{ emoji: '🧥', name: '大衣' },
{ emoji: '👘', name: '和服' },
{ emoji: '🥻', name: '纱丽' }
]
</script>
<style scoped>
.magic-closet {
border: 2px solid #e0d5c8;
border-radius: 16px;
background: linear-gradient(135deg, #faf6f0 0%, #f5ebe0 100%);
padding: 24px;
margin: 20px 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
}
/* 故事框 */
.story-box {
text-align: center;
margin-bottom: 24px;
padding: 20px;
background: linear-gradient(135deg, #fff5e6, #ffecd2);
border-radius: 16px;
border: 2px dashed #ffb347;
}
.story-emoji {
font-size: 48px;
margin-bottom: 8px;
animation: bounce 2s infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.story-title {
font-size: 20px;
font-weight: bold;
color: #8b4513;
margin: 0 0 8px 0;
}
.story-text {
font-size: 14px;
color: #666;
margin: 0;
line-height: 1.6;
}
/* 衣柜控制 */
.closet-control {
background: white;
border-radius: 12px;
padding: 16px;
margin-bottom: 20px;
border: 2px solid #e0e0e0;
}
.control-label {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
color: #333;
}
.room-label {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.slider-box {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}
.slider-emoji {
font-size: 20px;
}
.magic-slider {
flex: 1;
height: 10px;
-webkit-appearance: none;
appearance: none;
background: linear-gradient(90deg, #ffb347, #ff6b6b, #4ecdc4);
border-radius: 5px;
outline: none;
}
.magic-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 28px;
height: 28px;
background: white;
border: 4px solid #ff6b6b;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.width-hint {
text-align: center;
font-size: 13px;
color: #666;
background: #f8f9fa;
padding: 8px;
border-radius: 6px;
}
/* 衣柜展示 */
.closet-display {
margin: 0 auto 20px;
background: linear-gradient(135deg, #8b4513, #a0522d);
border-radius: 16px;
padding: 4px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.closet-header {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
padding: 12px;
background: linear-gradient(135deg, #d2691e, #cd853f);
border-radius: 12px 12px 0 0;
}
.closet-icon {
font-size: 24px;
}
.closet-title {
font-size: 16px;
font-weight: bold;
color: #fff;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.closet-interior {
background: linear-gradient(135deg, #f5f5dc, #faf0e6);
padding: 16px;
min-height: 200px;
}
.clothes-rack {
display: grid;
gap: 12px;
}
.clothing-item {
background: white;
border-radius: 12px;
padding: 12px;
text-align: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
animation: popIn 0.5s ease both;
}
@keyframes popIn {
0% { opacity: 0; transform: scale(0.8); }
100% { opacity: 1; transform: scale(1); }
}
.clothing-item:hover {
transform: translateY(-4px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}
.clothing-item.folded {
padding: 8px;
}
.item-hanger {
font-size: 20px;
margin-bottom: 4px;
}
.item-emoji {
font-size: 40px;
margin-bottom: 4px;
display: block;
}
.clothing-item.folded .item-emoji {
font-size: 28px;
}
.item-name {
font-size: 13px;
color: #333;
font-weight: 500;
}
.clothing-item.folded .item-name {
font-size: 11px;
}
.fold-hint {
font-size: 10px;
color: #ff6b6b;
margin-top: 4px;
font-weight: bold;
}
.closet-footer {
display: flex;
justify-content: space-between;
padding: 12px 16px;
background: linear-gradient(135deg, #d2691e, #cd853f);
border-radius: 0 0 12px 12px;
font-size: 12px;
color: white;
}
/* 魔法原理说明 */
.magic-explain {
background: linear-gradient(135deg, #e3f2fd, #f3e5f5);
border-radius: 16px;
padding: 20px;
margin-bottom: 20px;
border: 2px dashed #7e57c2;
}
.explain-title {
font-size: 18px;
font-weight: bold;
color: #5e35b1;
text-align: center;
margin-bottom: 16px;
}
.explain-cards {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
}
.explain-card {
background: white;
border-radius: 12px;
padding: 16px;
text-align: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
min-width: 140px;
}
.card-icon {
font-size: 32px;
margin-bottom: 8px;
}
.card-title {
font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 6px;
}
.card-desc {
font-size: 12px;
color: #666;
line-height: 1.4;
}
.explain-arrow {
font-size: 24px;
color: #7e57c2;
}
/* 代码区域 */
.code-section {
border: 2px solid #e0e0e0;
border-radius: 12px;
overflow: hidden;
margin-bottom: 16px;
}
.code-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
font-weight: bold;
}
.code-tag {
background: rgba(255, 255, 255, 0.2);
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
}
.code-content {
margin: 0;
padding: 16px;
background: #2d2d2d;
color: #f8f8f2;
font-size: 13px;
line-height: 1.6;
overflow-x: auto;
}
/* 总结框 */
.summary-box {
display: flex;
gap: 16px;
padding: 20px;
background: linear-gradient(135deg, #d4edda, #c3e6cb);
border-radius: 12px;
border-left: 4px solid #28a745;
}
.summary-icon {
font-size: 32px;
flex-shrink: 0;
}
.summary-content {
flex: 1;
font-size: 14px;
color: #155724;
line-height: 1.6;
}
/* 响应式调整 */
@media (max-width: 768px) {
.explain-cards {
flex-direction: column;
}
.explain-arrow {
transform: rotate(90deg);
}
.closet-display {
transform: scale(0.9);
transform-origin: top center;
}
}
</style>