fix: 新增天气和时间展示。

This commit is contained in:
tianyongbao
2026-01-01 16:10:55 +08:00
parent c20ad6f9a5
commit 3b51567f0c
2 changed files with 505 additions and 2 deletions

View File

@@ -2,6 +2,19 @@
<page class="home-page workbench-page">
<!-- 固定标题栏 -->
<div class="workbench-header">
<div class="header-info">
<div class="datetime-weather">
<span class="date">{{ currentDate }}</span>
<span class="time">{{ currentTime }}</span>
<div class="weather" v-if="weather.city">
<span class="weather-icon">{{ weather.icon }}</span>
<span class="weather-info">{{ weather.city }} {{ weather.temp }}°C {{ weather.desc }}</span>
<select class="city-selector" v-model="selectedCity" @change="changeCity">
<option v-for="city in cityList" :key="city" :value="city">{{ city }}</option>
</select>
</div>
</div>
</div>
<h1>统一工作平台</h1>
<div class="header-actions">
<button class="view-toggle-btn" @click="toggleView">
@@ -92,12 +105,55 @@ export default {
superCategories: [],
categories: [],
activeSuperCategoryId: null, // 当前Tab的大分类ID
viewMode: this.isMobile() ? 'colorful' : 'simple' // 手机端默认炫彩版PC端默认简洁版
viewMode: this.isMobile() ? 'colorful' : 'simple', // 手机端默认炫彩版PC端默认简洁版
currentDate: '',
currentTime: '',
weather: {
city: '',
temp: '',
desc: '',
icon: ''
},
timer: null,
selectedCity: '青岛', // 默认城市
cityList: [
// 直辖市
'北京', '上海', '天津', '重庆',
// 省会城市
'广州', '深圳', '成都', '杭州', '武汉', '西安', '郑州', '南京', '济南', '沈阳', '长春', '哈尔滨',
'石家庄', '太原', '呼和浩特', '南昌', '长沙', '福州', '合肥', '南宁', '昆明', '贵阳', '拉萨',
'兰州', '西宁', '银川', '乌鲁木齐', '海口', '台北', '香港', '澳门',
// 山东省主要城市
'青岛', '烟台', '威海', '潍坊', '淄博', '东营', '济宁', '泰安', '临沂', '德州', '聊城', '滨州', '菏泽', '枣庄', '日照', '莱芜',
// 其他重要城市
'苏州', '无锡', '常州', '南通', '徐州', '扬州', '镇江', '泰州', '盐城', '连云港', '宿迁', '淮安',
'宁波', '温州', '嘉兴', '湖州', '绍兴', '金华', '衢州', '舟山', '台州', '丽水',
'厦门', '泉州', '漳州', '龙岩', '三明', '南平', '莆田', '宁德',
'珠海', '佛山', '东莞', '中山', '惠州', '江门', '湛江', '茂名', '肇庆', '汕头', '韶关', '梅州', '汕尾', '河源', '阳江', '清远', '潮州', '揭阳', '云浮',
'大连', '鞍山', '抚顺', '本溪', '丹东', '锦州', '营口', '阜新', '辽阳', '盘锦', '铁岭', '朝阳', '葫芦岛',
'保定', '唐山', '秦皇岛', '邯郸', '邢台', '张家口', '承德', '沧州', '廊坊', '衡水'
]
}
},
created() {
// 从localStorage恢复上次选择的城市
const savedCity = localStorage.getItem('selectedCity')
if (savedCity && this.cityList.includes(savedCity)) {
this.selectedCity = savedCity
}
// 先显示加载中
this.weather = {
city: this.selectedCity,
temp: '--',
desc: '加载中',
icon: '⏳'
}
this.loadData()
this.updateDateTime()
this.getWeather()
},
mounted() {
@@ -107,10 +163,17 @@ export default {
})
// 监听窗口大小变化
window.addEventListener('resize', this.adjustContainerPadding)
// 启动时间更新定时器
this.timer = setInterval(() => {
this.updateDateTime()
}, 1000)
},
beforeDestroy() {
window.removeEventListener('resize', this.adjustContainerPadding)
if (this.timer) {
clearInterval(this.timer)
}
},
computed: {
@@ -122,6 +185,113 @@ export default {
},
methods: {
// 更新日期时间
updateDateTime() {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
const weekDay = weekDays[now.getDay()]
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
this.currentDate = `${year}-${month}-${day} ${weekDay}`
this.currentTime = `${hours}:${minutes}:${seconds}`
},
// 获取天气信息
async getWeather(city = this.selectedCity) {
try {
// 使用高德天气API国内速度快
// 免费额度每天5000次个人使用完全够用
const key = '7bd11d7a12a97df85efdf08214cb264c'
// 城市编码映射高德需要城市编码或adcode
const response = await fetch(
`https://restapi.amap.com/v3/weather/weatherInfo?city=${encodeURIComponent(city)}&key=${key}&extensions=base`,
{ timeout: 3000 }
)
const data = await response.json()
if (data.status === '1' && data.lives && data.lives.length > 0) {
const live = data.lives[0]
this.weather = {
city: live.city,
temp: live.temperature,
desc: live.weather,
icon: this.getWeatherIcon(live.weather)
}
} else {
// API失败显示默认
this.weather = {
city: city,
temp: '--',
desc: '暂无',
icon: '🌤️'
}
}
} catch (error) {
console.error('获取天气失败:', error)
this.weather = {
city: city,
temp: '--',
desc: '暂无',
icon: '🌤️'
}
}
},
// 切换城市
changeCity() {
this.getWeather(this.selectedCity)
// 保存到localStorage
localStorage.setItem('selectedCity', this.selectedCity)
},
// 根据天气描述返回对应的emoji图标
getWeatherIcon(weather) {
const desc = weather.toLowerCase()
// 晴天
if (desc.includes('晴') || desc.includes('clear') || desc.includes('sunny')) {
return '☀️'
}
// 多云
if (desc.includes('多云') || desc.includes('partly') || desc.includes('cloudy')) {
return '⛅'
}
// 阴天
if (desc.includes('阴') || desc.includes('overcast')) {
return '☁️'
}
// 雨
if (desc.includes('雨') || desc.includes('rain') || desc.includes('drizzle')) {
return '🌧️'
}
// 雷雨
if (desc.includes('雷') || desc.includes('thunder')) {
return '⛈️'
}
// 雪
if (desc.includes('雪') || desc.includes('snow')) {
return '❄️'
}
// 雾霾
if (desc.includes('雾') || desc.includes('霾') || desc.includes('fog') || desc.includes('haze') || desc.includes('mist')) {
return '🌫️'
}
// 风
if (desc.includes('风') || desc.includes('wind')) {
return '💨'
}
return '🌤️'
},
// 动态调整容器padding-top
adjustContainerPadding() {
setTimeout(() => {
@@ -500,7 +670,7 @@ export default {
height: @header-height; // 固定高度
display: flex;
align-items: center;
justify-content: center;
justify-content: center; // 让h1居中
padding: 0 40px; // 只保留左右padding去掉上下padding
background: @primary-gradient;
z-index: 99;
@@ -526,6 +696,93 @@ export default {
}
}
.header-info {
position: absolute;
left: 40px; // 放在左边
display: flex;
justify-content: flex-start;
align-items: center;
.datetime-weather {
display: flex;
align-items: center;
gap: 16px;
padding: 8px 20px;
background: rgba(255, 255, 255, 0.15);
border-radius: 25px;
backdrop-filter: blur(10px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
.date {
font-size: 14px;
font-weight: 600;
color: rgba(255, 255, 255, 0.95);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
white-space: nowrap;
}
.time {
font-size: 18px;
font-weight: 700;
color: white;
font-family: 'Consolas', 'Monaco', monospace;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
letter-spacing: 1px;
white-space: nowrap;
}
.weather {
display: flex;
align-items: center;
gap: 6px;
padding-left: 16px;
border-left: 2px solid rgba(255, 255, 255, 0.3);
.weather-icon {
font-size: 18px;
}
.weather-info {
font-size: 14px;
font-weight: 600;
color: rgba(255, 255, 255, 0.95);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
white-space: nowrap;
}
.city-selector {
margin-left: 8px;
padding: 4px 8px;
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 6px;
color: white;
font-size: 13px;
font-weight: 600;
cursor: pointer;
outline: none;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
option {
background: #667eea;
color: white;
}
&:hover {
background: rgba(255, 255, 255, 0.3);
border-color: rgba(255, 255, 255, 0.5);
}
&:focus {
background: rgba(255, 255, 255, 0.3);
border-color: white;
}
}
}
}
}
.header-actions {
position: absolute;
right: 40px;

View File

@@ -1,6 +1,19 @@
<template>
<page class="manage-page">
<div class="manage-header">
<div class="header-info">
<div class="datetime-weather">
<span class="date">{{ currentDate }}</span>
<span class="time">{{ currentTime }}</span>
<div class="weather" v-if="weather.city">
<span class="weather-icon">{{ weather.icon }}</span>
<span class="weather-info">{{ weather.city }} {{ weather.temp }}°C {{ weather.desc }}</span>
<select class="city-selector" v-model="selectedCity" @change="changeCity">
<option v-for="city in cityList" :key="city" :value="city">{{ city }}</option>
</select>
</div>
</div>
</div>
<h1>书签管理</h1>
<button class="back-btn" @click="goBack">返回工作台</button>
</div>
@@ -277,6 +290,34 @@ export default {
text: '',
type: 'success' // success, error, warning
},
// 时间天气相关
currentDate: '',
currentTime: '',
weather: {
city: '',
temp: '',
desc: '',
icon: ''
},
timer: null,
selectedCity: '青岛',
cityList: [
// 直辖市
'北京', '上海', '天津', '重庆',
// 省会城市
'广州', '深圳', '成都', '杭州', '武汉', '西安', '郑州', '南京', '济南', '沈阳', '长春', '哈尔滨',
'石家庄', '太原', '呼和浩特', '南昌', '长沙', '福州', '合肥', '南宁', '昆明', '贵阳', '拉萨',
'兰州', '西宁', '银川', '乌鲁木齐', '海口', '台北', '香港', '澳门',
// 山东省主要城市
'青岛', '烟台', '威海', '潍坊', '淄博', '东营', '济宁', '泰安', '临沂', '德州', '聊城', '滨州', '菏泽', '枣庄', '日照', '莱芜',
// 其他重要城市
'苏州', '无锡', '常州', '南通', '徐州', '扬州', '镇江', '泰州', '盐城', '连云港', '宿迁', '淮安',
'宁波', '温州', '嘉兴', '湖州', '绍兴', '金华', '衢州', '舟山', '台州', '丽水',
'厦门', '泉州', '漳州', '龙岩', '三明', '南平', '莆田', '宁德',
'珠海', '佛山', '东莞', '中山', '惠州', '江门', '湛江', '茂名', '肇庆', '汕头', '韶关', '梅州', '汕尾', '河源', '阳江', '清远', '潮州', '揭阳', '云浮',
'大连', '鞍山', '抚顺', '本溪', '丹东', '锦州', '营口', '阜新', '辽阳', '盘锦', '铁岭', '朝阳', '葫芦岛',
'保定', '唐山', '秦皇岛', '邯郸', '邢台', '张家口', '承德', '沧州', '廊坊', '衡水'
],
colorOptions: [
{ name: '粉色', value: 'linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%)' },
{ name: '蓝色', value: 'linear-gradient(135deg, #a1c4fd 0%, #c2e9fb 100%)' },
@@ -365,7 +406,23 @@ export default {
created() {
// 从localStorage恢复上次选择的城市
const savedCity = localStorage.getItem('selectedCity')
if (savedCity && this.cityList.includes(savedCity)) {
this.selectedCity = savedCity
}
// 先显示加载中
this.weather = {
city: this.selectedCity,
temp: '--',
desc: '加载中',
icon: '⏳'
}
this.loadData()
this.updateDateTime()
this.getWeather()
},
mounted() {
@@ -379,11 +436,18 @@ export default {
window.addEventListener('resize', this.adjustContainerPadding)
// 监听ESC键关闭弹框
window.addEventListener('keydown', this.handleEscKey)
// 启动时间更新定时器
this.timer = setInterval(() => {
this.updateDateTime()
}, 1000)
},
beforeDestroy() {
window.removeEventListener('resize', this.adjustContainerPadding)
window.removeEventListener('keydown', this.handleEscKey)
if (this.timer) {
clearInterval(this.timer)
}
},
updated() {
@@ -394,6 +458,101 @@ export default {
},
methods: {
// 更新日期时间
updateDateTime() {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
const weekDay = weekDays[now.getDay()]
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
this.currentDate = `${year}-${month}-${day} ${weekDay}`
this.currentTime = `${hours}:${minutes}:${seconds}`
},
// 获取天气信息
async getWeather(city = this.selectedCity) {
try {
// 使用高德天气API国内速度快
const key = '7bd11d7a12a97df85efdf08214cb264c'
const response = await fetch(
`https://restapi.amap.com/v3/weather/weatherInfo?city=${encodeURIComponent(city)}&key=${key}&extensions=base`,
{ timeout: 3000 }
)
const data = await response.json()
if (data.status === '1' && data.lives && data.lives.length > 0) {
const live = data.lives[0]
this.weather = {
city: live.city,
temp: live.temperature,
desc: live.weather,
icon: this.getWeatherIcon(live.weather)
}
} else {
this.weather = {
city: city,
temp: '--',
desc: '暂无',
icon: '🌤️'
}
}
} catch (error) {
console.error('获取天气失败:', error)
this.weather = {
city: city,
temp: '--',
desc: '暂无',
icon: '🌤️'
}
}
},
// 切换城市
changeCity() {
this.getWeather(this.selectedCity)
localStorage.setItem('selectedCity', this.selectedCity)
},
// 根据天气描述返回对应的emoji图标
getWeatherIcon(weather) {
const desc = weather.toLowerCase()
if (desc.includes('晴') || desc.includes('clear') || desc.includes('sunny')) {
return '☀️'
}
if (desc.includes('多云') || desc.includes('partly') || desc.includes('cloudy')) {
return '⛅'
}
if (desc.includes('阴') || desc.includes('overcast')) {
return '☁️'
}
if (desc.includes('雨') || desc.includes('rain') || desc.includes('drizzle')) {
return '🌧️'
}
if (desc.includes('雷') || desc.includes('thunder')) {
return '⛈️'
}
if (desc.includes('雪') || desc.includes('snow')) {
return '❄️'
}
if (desc.includes('雾') || desc.includes('霾') || desc.includes('fog') || desc.includes('haze') || desc.includes('mist')) {
return '🌫️'
}
if (desc.includes('风') || desc.includes('wind')) {
return '💨'
}
return '🌤️'
},
// ESC键关闭弹框
handleEscKey(event) {
if (event.key === 'Escape' || event.keyCode === 27) {
@@ -1223,6 +1382,93 @@ export default {
letter-spacing: 2px; // 添加字母间距
}
.header-info {
position: absolute;
left: 40px; // 放在左边
display: flex;
justify-content: flex-start;
align-items: center;
.datetime-weather {
display: flex;
align-items: center;
gap: 16px;
padding: 8px 20px;
background: rgba(255, 255, 255, 0.15);
border-radius: 25px;
backdrop-filter: blur(10px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
.date {
font-size: 14px;
font-weight: 600;
color: rgba(255, 255, 255, 0.95);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
white-space: nowrap;
}
.time {
font-size: 18px;
font-weight: 700;
color: white;
font-family: 'Consolas', 'Monaco', monospace;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
letter-spacing: 1px;
white-space: nowrap;
}
.weather {
display: flex;
align-items: center;
gap: 6px;
padding-left: 16px;
border-left: 2px solid rgba(255, 255, 255, 0.3);
.weather-icon {
font-size: 18px;
}
.weather-info {
font-size: 14px;
font-weight: 600;
color: rgba(255, 255, 255, 0.95);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
white-space: nowrap;
}
.city-selector {
margin-left: 8px;
padding: 4px 8px;
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 6px;
color: white;
font-size: 13px;
font-weight: 600;
cursor: pointer;
outline: none;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
option {
background: #667eea;
color: white;
}
&:hover {
background: rgba(255, 255, 255, 0.3);
border-color: rgba(255, 255, 255, 0.5);
}
&:focus {
background: rgba(255, 255, 255, 0.3);
border-color: white;
}
}
}
}
}
.back-btn {
position: absolute; // 绝对定位避免遮挡标题
right: 40px;