天气卡片

majiayan_branch
Your Name 2 months ago
parent 5060dfbf2d
commit d931e8ffdb

@ -8,9 +8,42 @@
<!-- 社区背景和OC活动区域 -->
<div class="community-scene">
<div class="scene-background">
<img src="/photo/map1.png" alt="社区背景" class="scene-image background-image" />
<div class="background-image-container"></div>
<!-- 添加天气信息显示 -->
<div v-if="weatherInfo" class="weather-widget">
<div class="weather-content">
<div class="weather-details">
<!-- 添加地址输入框 -->
<div class="location-input">
<el-input
v-model="inputLocation"
placeholder="输入任意城市名称"
size="small"
@keyup.enter="updateWeather"
:disabled="weatherLoading"
>
<template #append>
<el-button
@click="updateWeather"
:icon="Search"
:loading="weatherLoading"
/>
</template>
</el-input>
</div>
<div class="temperature">{{ weatherInfo.temperature }}°C</div>
<div class="condition">{{ weatherInfo.condition }}</div>
<div class="location">{{ weatherInfo.province }}{{ weatherInfo.city !== weatherInfo.province ? weatherInfo.city : '' }}</div>
<div class="weather-extras" v-if="weatherInfo?.humidity">
<span>湿度: {{ weatherInfo.humidity }}%</span>
<span>风速: {{ weatherInfo.windSpeed }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- 地点标记区域 - 修复移到 oc-characters 外部 -->
<div class="location-pins">
<div
@ -103,6 +136,103 @@ import OCListDialog from './OCListDialog.vue'
import OCInfoDialog from './OCInfoDialog.vue'
import { ocList } from '../stores/ocStore.js'
const inputLocation = ref('天津')
const currentLocation = ref('天津')
//
const getRealMap = async (location) => {
return {
imageUrl: '/photo/map1.png',
coordinates:
locationCoords
}
}
//
const updateWeather = async () => {
if (inputLocation.value.trim()) {
weatherLoading.value = true
currentLocation.value = inputLocation.value.trim()
try {
const weatherData = await getWeatherData(currentLocation.value)
weatherInfo.value = weatherData
} catch (error) {
console.error('更新天气失败:', error)
} finally {
weatherLoading.value = false
}
}
}
const getWeatherData = async (location) => {
try {
const API_KEY = '7d1e2dfdd86a45c64e4178c0ad321fcd'
// API
const geoResponse = await fetch(`https://restapi.amap.com/v3/geocode/geo?address=${encodeURIComponent(location)}&key=${API_KEY}`)
const geoData = await geoResponse.json()
if (geoData.status !== '1' || !geoData.geocodes || geoData.geocodes.length === 0) {
throw new Error(`找不到地点: ${location}`)
}
//
const adcode = geoData.geocodes[0].adcode
// 使
const weatherResponse = await fetch(`https://restapi.amap.com/v3/weather/weatherInfo?city=${adcode}&key=${API_KEY}&extensions=base`)
const weatherData = await weatherResponse.json()
if (weatherData.status === '1' && weatherData.lives && weatherData.lives.length > 0) {
const live = weatherData.lives[0]
return {
temperature: live.temperature,
condition: live.weather,
humidity: live.humidity,
windSpeed: live.windpower,
windDir: live.winddirection,
reportTime: live.reporttime,
province: live.province,
city: live.city
}
} else {
throw new Error('获取天气数据失败')
}
} catch (error) {
console.error('获取真实天气失败:', error)
return getMockWeatherData(location)
}
}
//
const getMockWeatherData = (location) => {
//
const conditions = ['晴', '多云', '阴', '小雨', '中雨', '大雨', '阵雨', '雷阵雨', '雪', '雾', '霾']
const randomCondition = conditions[Math.floor(Math.random() * conditions.length)]
//
const now = new Date()
const month = now.getMonth() + 1
let baseTemp = 20
if (month >= 3 && month <= 5) baseTemp = 18 //
else if (month >= 6 && month <= 8) baseTemp = 28 //
else if (month >= 9 && month <= 11) baseTemp = 15 //
else baseTemp = 5 //
const temperature = baseTemp + Math.floor(Math.random() * 15) - 5
return {
temperature: temperature,
condition: randomCondition,
humidity: 30 + Math.floor(Math.random() * 50),
windSpeed: (1 + Math.random() * 6).toFixed(1),
province: '未知省份',
city: location
}
}
//
const defaultAvatar = 'https://i.pravatar.cc/100?img=1'
@ -121,6 +251,10 @@ const locationCoords = {
'宠物店': { x: 35, y: 75 }
}
const currentMap = ref('')
const weatherInfo = ref(null)
const weatherLoading = ref(false)
// OC schedule
const ocPositions = ref([])
// oc
@ -166,6 +300,7 @@ onMounted(() => {
computePositions()
posTimer = setInterval(computePositions, 60 * 1000)
document.addEventListener('click', closePopoverOnClickOutside)
initMapAndWeather()
})
onUnmounted(() => {
if (posTimer) clearInterval(posTimer)
@ -224,6 +359,27 @@ const closePopoverOnClickOutside = (event) => {
closeLocationPopover()
}
}
const initMapAndWeather = async () => {
try {
// 使
const mapData = await getRealMap(currentLocation.value)
currentMap
.value = mapData.
imageUrl
//
const weatherData = await getWeatherData(currentLocation.value)
weatherInfo
.value =
weatherData
} catch (error) {
console
.error('初始化天气失败:', error)
currentMap
.value = '/photo/map1.png'
}
}
</script>
<style scoped>
@ -261,18 +417,34 @@ const closePopoverOnClickOutside = (event) => {
border-radius: 12px;
overflow: hidden;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
background: #f0f0f0;
/* 强制基于视口计算高度不受父元素padding影响 */
}
.scene-background {
width: 100%;
height: 100%;
position: relative;
background: #f0f0f0; /* 加载前的背景色 */
overflow: visible; /* 改为visible避免裁剪 */
}
.scene-image {
.background-image-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
/* 使用背景图替代img利用background-size控制显示方式 */
background-image: url("/photo/map1.png");
background-repeat: no-repeat;
background-position: center; /* 图片居中显示 */
background-size: contain; /* 完整显示图片,可能有留白 */
/*
background-size: cover;
*/
z-index: 1; /* 确保在更底层,不遮挡其他内容 */
pointer-events: none; /* 禁止背景图片拦截鼠标事件,保障天气卡等可交互 */
}
.right-panel {
@ -320,7 +492,8 @@ const closePopoverOnClickOutside = (event) => {
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /* children override */
pointer-events: none;
z-index: 10; /* 高于背景图 */
}
.location-pin {
@ -436,4 +609,118 @@ const closePopoverOnClickOutside = (event) => {
font-size: 12px;
}
}
/* 响应式调整 */
@media (max-width: 768px) {
.weather-widget {
top: 10px;
right: 10px;
padding: 8px;
min-width: 110px;
max-width: 130px;
}
.temperature {
font-size: 14px;
}
.condition, .location {
font-size: 10px;
}
.weather-extras {
font-size: 8px;
}
}
@media (max-width: 768px) {
.community-scene {
height: calc(100vh - 150px); /* 小屏幕下根据布局调整高度,适配更多空间 */
}
.scene-image {
object-fit: contain; /* 小屏幕下优先保证图片完整显示 */
}
}
/* 天气组件样式 */
.weather-widget {
position: absolute;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 10px; /* 减少内边距 */
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
z-index: 30;
min-width: 120px; /* 减小最小宽度 */
max-width: 140px; /* 添加最大宽度限制 */
}
.weather-content {
display: flex;
flex-direction: column;
gap: 6px; /* 减小间距 */
}
.weather-details {
color: #333;
text-align: center;
font-size: 0.9em; /* 整体缩小字体 */
}
.location-input {
margin-bottom: 6px;
}
.location-input :deep(.el-input-group) {
background: rgba(255,255,255,0.9);
border-radius: 4px;
border: 1px solid #e4e7ed;
height: 28px; /* 减小高度 */
}
.location-input :deep(.el-input__inner) {
border: none;
background: transparent;
padding: 0 8px;
font-size: 11px; /* 减小字体 */
height: 26px;
line-height: 26px;
}
.location-input :deep(.el-input-group__append) {
padding: 0 6px;
background: transparent;
}
.location-input :deep(.el-button) {
padding: 6px;
height: auto;
}
.temperature {
font-size: 16px; /* 减小温度字体 */
font-weight: bold;
margin-bottom: 2px;
}
.condition {
font-size: 11px; /* 减小天气状况字体 */
opacity: 0.8;
margin-bottom: 2px;
}
.location {
font-size: 10px; /* 减小地点字体 */
opacity: 0.6;
margin-bottom: 4px;
}
.weather-extras {
font-size: 9px; /* 减小额外信息字体 */
opacity: 0.7;
margin-top: 4px;
}
.weather-extras span {
display: block;
line-height: 1.2;
}
</style>
Loading…
Cancel
Save