727 lines
16 KiB
Vue
727 lines
16 KiB
Vue
<template>
|
||
<scroll-view scroll-y="true" class="page-scroll">
|
||
<view class="container">
|
||
<view class="header">
|
||
<text class="title">黄金实时价格</text>
|
||
<text class="subtitle">黄金9999 (1053)</text>
|
||
</view>
|
||
|
||
<!-- 当前价格卡片 -->
|
||
<view class="price-card" v-if="currentPrice">
|
||
<view class="price-main">
|
||
<view class="price-main-left">
|
||
<text class="price-label">当前价格</text>
|
||
<text class="price-value">¥{{ formatPrice(currentPrice.price) }}</text>
|
||
</view>
|
||
<view class="price-main-right">
|
||
<view class="refresh-btn" @click="handleRefreshPrice" :class="{ loading: refreshing }">
|
||
<text>{{ refreshing ? '查询中...' : '立马查询' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="price-change" :class="priceChangeClass">
|
||
<text class="change-value">{{ priceChangeText }}</text>
|
||
<text class="change-percent">{{ priceChangePercentText }}</text>
|
||
</view>
|
||
<view class="price-details">
|
||
<view class="detail-item">
|
||
<text class="detail-label">最高</text>
|
||
<text class="detail-value">¥{{ formatPrice(currentPrice.highPrice) }}</text>
|
||
</view>
|
||
<view class="detail-item">
|
||
<text class="detail-label">最低</text>
|
||
<text class="detail-value">¥{{ formatPrice(currentPrice.lowPrice) }}</text>
|
||
</view>
|
||
<view class="detail-item">
|
||
<text class="detail-label">开盘</text>
|
||
<text class="detail-value">¥{{ formatPrice(currentPrice.openPrice) }}</text>
|
||
</view>
|
||
<view class="detail-item">
|
||
<text class="detail-label">昨收</text>
|
||
<text class="detail-value">¥{{ formatPrice(currentPrice.yesterdayClose) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="update-time">
|
||
<text>更新时间: {{ currentPrice.updateTime }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载状态 -->
|
||
<view class="loading-card" v-if="loading">
|
||
<text class="loading-text">加载中...</text>
|
||
</view>
|
||
|
||
<!-- 无数据状态 -->
|
||
<view class="error-card" v-if="!loading && !error && !currentPrice">
|
||
<text class="error-text">暂无黄金价格数据,请稍后再试</text>
|
||
</view>
|
||
|
||
<!-- 错误状态 -->
|
||
<view class="error-card" v-if="error && !loading">
|
||
<text class="error-text">{{ error }}</text>
|
||
<view class="retry-btn" @click="loadCurrentPrice">
|
||
<text>重试</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 日期切换区域 -->
|
||
<view class="date-switch-section" v-if="currentDate">
|
||
<view class="date-switch-container">
|
||
<view class="date-btn" @click="goToPreviousDay" :class="{ disabled: isFirstDay }">
|
||
<text>前一天</text>
|
||
</view>
|
||
<view class="date-display">
|
||
<text class="date-text">{{ currentDate }}</text>
|
||
</view>
|
||
<view class="date-btn" @click="goToNextDay" :class="{ disabled: isToday }">
|
||
<text>后一天</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 历史价格图表 -->
|
||
<view class="chart-section" v-if="dailyPrices.length > 0">
|
||
<text class="section-title">{{ currentDate }} 价格走势</text>
|
||
<view class="chart-container">
|
||
<canvas canvas-id="priceChart" id="priceChart" type="2d" class="price-chart"></canvas>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 无数据提示 -->
|
||
<view class="error-card" v-if="!loading && dailyPrices.length === 0 && currentDate">
|
||
<text class="error-text">该日期暂无数据</text>
|
||
</view>
|
||
|
||
<!-- 历史价格列表 -->
|
||
<view class="history-section" v-if="dailyPrices.length > 0">
|
||
<text class="section-title">{{ currentDate }} 价格记录</text>
|
||
<view class="history-list">
|
||
<view class="history-item" v-for="(item, index) in dailyPrices" :key="index">
|
||
<text class="history-date">{{ item.updateTime }}</text>
|
||
<text class="history-price">¥{{ formatPrice(item.price) }}</text>
|
||
<text class="history-change" :class="getChangeClass(item.priceChange)">
|
||
{{ formatChange(item.priceChange) }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部预留空白区域,避免历史记录被底部导航遮挡 -->
|
||
<view class="bottom-spacer"></view>
|
||
</view>
|
||
</scroll-view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue';
|
||
import { getCurrentGoldPrice, getGoldPricesByDate, getLatestGoldPriceDate, refreshGoldPrice } from '../../api/gold-price';
|
||
|
||
const currentPrice = ref(null);
|
||
const dailyPrices = ref([]);
|
||
const currentDate = ref('');
|
||
const loading = ref(false);
|
||
const error = ref('');
|
||
const refreshing = ref(false);
|
||
|
||
const priceChangeClass = computed(() => {
|
||
if (!currentPrice.value || !currentPrice.value.priceChange) return '';
|
||
return currentPrice.value.priceChange >= 0 ? 'up' : 'down';
|
||
});
|
||
|
||
const priceChangeText = computed(() => {
|
||
if (!currentPrice.value || currentPrice.value.priceChange === null) return '--';
|
||
const change = currentPrice.value.priceChange;
|
||
return change >= 0 ? `+${formatPrice(change)}` : formatPrice(change);
|
||
});
|
||
|
||
const priceChangePercentText = computed(() => {
|
||
if (!currentPrice.value || currentPrice.value.priceChangePercent === null) return '--';
|
||
const percent = currentPrice.value.priceChangePercent;
|
||
return percent >= 0 ? `+${percent.toFixed(2)}%` : `${percent.toFixed(2)}%`;
|
||
});
|
||
|
||
const isToday = computed(() => {
|
||
if (!currentDate.value) return false;
|
||
const today = formatDate(new Date());
|
||
return currentDate.value === today;
|
||
});
|
||
|
||
const isFirstDay = computed(() => {
|
||
// 简单判断:如果当前日期是今天,则不能往前
|
||
// 实际应该查询数据库是否有更早的数据,这里简化处理
|
||
return false;
|
||
});
|
||
|
||
const formatPrice = (price) => {
|
||
if (price === null || price === undefined) return '--';
|
||
return Number(price).toFixed(2);
|
||
};
|
||
|
||
const formatChange = (change) => {
|
||
if (change === null || change === undefined) return '--';
|
||
return change >= 0 ? `+${Number(change).toFixed(2)}` : Number(change).toFixed(2);
|
||
};
|
||
|
||
const getChangeClass = (change) => {
|
||
if (change === null || change === undefined) return '';
|
||
return change >= 0 ? 'up' : 'down';
|
||
};
|
||
|
||
const formatDate = (date) => {
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
return `${year}-${month}-${day}`;
|
||
};
|
||
|
||
const formatTime = (dateTimeStr) => {
|
||
if (!dateTimeStr) return '';
|
||
// dateTimeStr 格式: "2024-01-15 10:15:00"
|
||
return dateTimeStr;
|
||
};
|
||
|
||
const loadCurrentPrice = async () => {
|
||
loading.value = true;
|
||
error.value = '';
|
||
try {
|
||
const res = await getCurrentGoldPrice();
|
||
if (res && res.code === 200) {
|
||
if (res.data) {
|
||
currentPrice.value = res.data;
|
||
} else {
|
||
currentPrice.value = null;
|
||
}
|
||
} else {
|
||
error.value = res?.message || '获取价格失败';
|
||
}
|
||
} catch (e) {
|
||
console.error('获取当前价格失败', e);
|
||
error.value = '网络请求失败,请稍后重试';
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
const handleRefreshPrice = async () => {
|
||
if (refreshing.value) return;
|
||
|
||
refreshing.value = true;
|
||
try {
|
||
const res = await refreshGoldPrice('1053');
|
||
if (res && res.code === 200) {
|
||
if (res.data) {
|
||
currentPrice.value = res.data;
|
||
uni.showToast({
|
||
title: '查询成功',
|
||
icon: 'success'
|
||
});
|
||
// 如果当前查看的是今天,刷新当天的价格列表
|
||
const today = formatDate(new Date());
|
||
if (currentDate.value === today) {
|
||
await loadDailyPrices(today);
|
||
}
|
||
} else {
|
||
uni.showToast({
|
||
title: '查询失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
} else {
|
||
uni.showToast({
|
||
title: res?.message || '查询失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
} catch (e) {
|
||
console.error('立即查询失败', e);
|
||
uni.showToast({
|
||
title: '查询失败,请稍后重试',
|
||
icon: 'none'
|
||
});
|
||
} finally {
|
||
refreshing.value = false;
|
||
}
|
||
};
|
||
|
||
const loadDailyPrices = async (date) => {
|
||
try {
|
||
const res = await getGoldPricesByDate('1053', date);
|
||
if (res && res.code === 200) {
|
||
dailyPrices.value = res.data || [];
|
||
if (dailyPrices.value.length > 0) {
|
||
// 确保数据按时间排序
|
||
dailyPrices.value.sort((a, b) => {
|
||
return new Date(a.updateTime) - new Date(b.updateTime);
|
||
});
|
||
// 延迟绘制图表,确保 canvas 已渲染
|
||
setTimeout(() => {
|
||
drawChart();
|
||
}, 100);
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('获取当天价格失败', e);
|
||
}
|
||
};
|
||
|
||
const goToPreviousDay = () => {
|
||
if (isFirstDay.value) return;
|
||
const date = new Date(currentDate.value);
|
||
date.setDate(date.getDate() - 1);
|
||
currentDate.value = formatDate(date);
|
||
loadDailyPrices(currentDate.value);
|
||
};
|
||
|
||
const goToNextDay = () => {
|
||
if (isToday.value) return;
|
||
const date = new Date(currentDate.value);
|
||
date.setDate(date.getDate() + 1);
|
||
const newDate = formatDate(date);
|
||
const today = formatDate(new Date());
|
||
if (newDate > today) return;
|
||
currentDate.value = newDate;
|
||
loadDailyPrices(currentDate.value);
|
||
};
|
||
|
||
const drawChart = () => {
|
||
const ctx = uni.createCanvasContext('priceChart');
|
||
const data = [...dailyPrices.value];
|
||
|
||
if (data.length === 0) return;
|
||
|
||
const width = 320;
|
||
const height = 180;
|
||
const padding = 40;
|
||
const chartWidth = width - padding * 2;
|
||
const chartHeight = height - padding * 2;
|
||
|
||
const prices = data.map((item) => Number(item.price));
|
||
const minPrice = Math.min(...prices) * 0.998;
|
||
const maxPrice = Math.max(...prices) * 1.002;
|
||
const priceRange = maxPrice - minPrice;
|
||
|
||
// 绘制背景
|
||
ctx.setFillStyle('#FFF8E1');
|
||
ctx.fillRect(0, 0, width, height);
|
||
|
||
// 绘制网格线
|
||
ctx.setStrokeStyle('#E0E0E0');
|
||
ctx.setLineWidth(0.5);
|
||
for (let i = 0; i <= 4; i++) {
|
||
const y = padding + (chartHeight / 4) * i;
|
||
ctx.beginPath();
|
||
ctx.moveTo(padding, y);
|
||
ctx.lineTo(width - padding, y);
|
||
ctx.stroke();
|
||
}
|
||
|
||
// 绘制价格线
|
||
ctx.setStrokeStyle('#FFB300');
|
||
ctx.setLineWidth(2);
|
||
ctx.beginPath();
|
||
|
||
data.forEach((item, index) => {
|
||
const x = padding + (chartWidth / (data.length - 1 || 1)) * index;
|
||
const y = padding + chartHeight - ((Number(item.price) - minPrice) / priceRange) * chartHeight;
|
||
|
||
if (index === 0) {
|
||
ctx.moveTo(x, y);
|
||
} else {
|
||
ctx.lineTo(x, y);
|
||
}
|
||
});
|
||
ctx.stroke();
|
||
|
||
// 绘制数据点
|
||
ctx.setFillStyle('#FF8F00');
|
||
data.forEach((item, index) => {
|
||
const x = padding + (chartWidth / (data.length - 1 || 1)) * index;
|
||
const y = padding + chartHeight - ((Number(item.price) - minPrice) / priceRange) * chartHeight;
|
||
ctx.beginPath();
|
||
ctx.arc(x, y, 3, 0, 2 * Math.PI);
|
||
ctx.fill();
|
||
});
|
||
|
||
// 绘制Y轴标签
|
||
ctx.setFillStyle('#5D4037');
|
||
ctx.setFontSize(10);
|
||
for (let i = 0; i <= 4; i++) {
|
||
const price = maxPrice - (priceRange / 4) * i;
|
||
const y = padding + (chartHeight / 4) * i;
|
||
ctx.fillText(price.toFixed(1), 5, y + 3);
|
||
}
|
||
|
||
// 绘制X轴标签(时间点)
|
||
ctx.setFillStyle('#5D4037');
|
||
ctx.setFontSize(9);
|
||
const labelInterval = Math.max(1, Math.floor(data.length / 6)); // 最多显示6个标签
|
||
data.forEach((item, index) => {
|
||
if (index % labelInterval === 0 || index === data.length - 1) {
|
||
const x = padding + (chartWidth / (data.length - 1 || 1)) * index;
|
||
// 从 updateTime 提取时间部分(HH:mm)
|
||
const timeStr = item.updateTime ? item.updateTime.split(' ')[1]?.substring(0, 5) : '';
|
||
if (timeStr) {
|
||
ctx.fillText(timeStr, x - 15, height - 10);
|
||
}
|
||
}
|
||
});
|
||
|
||
ctx.draw();
|
||
};
|
||
|
||
const initPage = async () => {
|
||
// 先加载当前价格
|
||
await loadCurrentPrice();
|
||
|
||
// 获取最近有数据的日期
|
||
try {
|
||
const res = await getLatestGoldPriceDate('1053');
|
||
if (res && res.code === 200 && res.data) {
|
||
const latestDate = res.data;
|
||
const today = formatDate(new Date());
|
||
// 如果最近有数据的日期是今天,则显示今天;否则显示最近有数据的日期
|
||
currentDate.value = latestDate === today ? today : latestDate;
|
||
} else {
|
||
// 如果没有数据,默认显示今天
|
||
currentDate.value = formatDate(new Date());
|
||
}
|
||
} catch (e) {
|
||
console.error('获取最近日期失败', e);
|
||
// 出错时默认显示今天
|
||
currentDate.value = formatDate(new Date());
|
||
}
|
||
|
||
// 加载当前日期的数据
|
||
if (currentDate.value) {
|
||
await loadDailyPrices(currentDate.value);
|
||
}
|
||
};
|
||
|
||
onMounted(() => {
|
||
initPage();
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page-scroll {
|
||
height: 100vh;
|
||
}
|
||
|
||
.container {
|
||
min-height: 100vh;
|
||
background: linear-gradient(135deg, #ffe5cc 0%, #ffd4a8 50%, #ffc08a 100%);
|
||
padding: 20rpx;
|
||
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
|
||
}
|
||
|
||
.header {
|
||
text-align: center;
|
||
padding: 30rpx 0;
|
||
}
|
||
|
||
.title {
|
||
display: block;
|
||
font-size: 44rpx;
|
||
font-weight: bold;
|
||
color: #5d4037;
|
||
text-shadow: 2rpx 2rpx 0 #ffd700;
|
||
}
|
||
|
||
.subtitle {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #8d6e63;
|
||
margin-top: 10rpx;
|
||
}
|
||
|
||
.price-card {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
border: 3rpx solid #c8956e;
|
||
box-shadow: 0 8rpx 20rpx rgba(200, 149, 110, 0.15);
|
||
}
|
||
|
||
.price-main {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.price-main-left {
|
||
flex: 1;
|
||
text-align: center;
|
||
}
|
||
|
||
.price-main-right {
|
||
flex: 0 0 auto;
|
||
margin-left: 20rpx;
|
||
}
|
||
|
||
.price-label {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #8d6e63;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.price-value {
|
||
display: block;
|
||
font-size: 60rpx;
|
||
font-weight: bold;
|
||
color: #5d4037;
|
||
}
|
||
|
||
.refresh-btn {
|
||
padding: 12rpx 24rpx;
|
||
background: linear-gradient(135deg, #c8956e, #ffd700);
|
||
border-radius: 20rpx;
|
||
border: 2rpx solid #5d4037;
|
||
text-align: center;
|
||
min-width: 120rpx;
|
||
}
|
||
|
||
.refresh-btn text {
|
||
font-size: 24rpx;
|
||
color: #5d4037;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.refresh-btn.loading {
|
||
opacity: 0.7;
|
||
background: linear-gradient(135deg, #bdbdbd, #e0e0e0);
|
||
}
|
||
|
||
.refresh-btn.loading text {
|
||
color: #757575;
|
||
}
|
||
|
||
.price-change {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 20rpx;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.price-change.up .change-value,
|
||
.price-change.up .change-percent {
|
||
color: #fa3534;
|
||
}
|
||
|
||
.price-change.down .change-value,
|
||
.price-change.down .change-percent {
|
||
color: #19be6b;
|
||
}
|
||
|
||
.change-value,
|
||
.change-percent {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.price-details {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 15rpx;
|
||
}
|
||
|
||
.detail-item {
|
||
flex: 1;
|
||
min-width: 45%;
|
||
background: linear-gradient(135deg, rgba(200, 149, 110, 0.1), rgba(255, 215, 0, 0.1));
|
||
padding: 15rpx;
|
||
border-radius: 10rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.detail-label {
|
||
display: block;
|
||
font-size: 22rpx;
|
||
color: #8d6e63;
|
||
margin-bottom: 5rpx;
|
||
}
|
||
|
||
.detail-value {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #5d4037;
|
||
}
|
||
|
||
.update-time {
|
||
text-align: center;
|
||
margin-top: 20rpx;
|
||
font-size: 22rpx;
|
||
color: #bcaaa4;
|
||
}
|
||
|
||
.loading-card,
|
||
.error-card {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 20rpx;
|
||
padding: 60rpx 30rpx;
|
||
margin-bottom: 20rpx;
|
||
border: 3rpx solid #c8956e;
|
||
text-align: center;
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 28rpx;
|
||
color: #8d6e63;
|
||
}
|
||
|
||
.error-text {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
color: #fa3534;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.retry-btn {
|
||
display: inline-block;
|
||
padding: 15rpx 40rpx;
|
||
background: linear-gradient(135deg, #c8956e, #ffd700);
|
||
border-radius: 30rpx;
|
||
border: 2rpx solid #5d4037;
|
||
}
|
||
|
||
.retry-btn text {
|
||
font-size: 26rpx;
|
||
color: #5d4037;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.date-switch-section {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 20rpx;
|
||
padding: 20rpx 30rpx;
|
||
margin-bottom: 20rpx;
|
||
border: 3rpx solid #c8956e;
|
||
}
|
||
|
||
.date-switch-container {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.date-btn {
|
||
flex: 1;
|
||
padding: 15rpx 20rpx;
|
||
background: linear-gradient(135deg, #c8956e, #ffd700);
|
||
border-radius: 15rpx;
|
||
border: 2rpx solid #5d4037;
|
||
text-align: center;
|
||
}
|
||
|
||
.date-btn text {
|
||
font-size: 26rpx;
|
||
color: #5d4037;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.date-btn.disabled {
|
||
background: #e0e0e0;
|
||
border-color: #bdbdbd;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.date-btn.disabled text {
|
||
color: #9e9e9e;
|
||
}
|
||
|
||
.date-display {
|
||
flex: 2;
|
||
text-align: center;
|
||
padding: 0 20rpx;
|
||
}
|
||
|
||
.date-text {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #5d4037;
|
||
}
|
||
|
||
.chart-section,
|
||
.history-section {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 20rpx;
|
||
border: 3rpx solid #c8956e;
|
||
}
|
||
|
||
.section-title {
|
||
display: block;
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #5d4037;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.chart-container {
|
||
width: 100%;
|
||
height: 360rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.price-chart {
|
||
width: 640rpx;
|
||
height: 360rpx;
|
||
}
|
||
|
||
.history-list {
|
||
max-height: 500rpx;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.history-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20rpx 0;
|
||
border-bottom: 1rpx solid rgba(200, 149, 110, 0.2);
|
||
}
|
||
|
||
.history-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.history-date {
|
||
font-size: 24rpx;
|
||
color: #8d6e63;
|
||
flex: 1.5;
|
||
}
|
||
|
||
.history-price {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #5d4037;
|
||
flex: 1;
|
||
text-align: center;
|
||
}
|
||
|
||
.history-change {
|
||
font-size: 26rpx;
|
||
font-weight: 600;
|
||
flex: 1;
|
||
text-align: right;
|
||
}
|
||
|
||
.history-change.up {
|
||
color: #fa3534;
|
||
}
|
||
|
||
.history-change.down {
|
||
color: #19be6b;
|
||
}
|
||
|
||
.bottom-spacer {
|
||
height: 200px;
|
||
}
|
||
</style>
|