WARNING

在开始使用实践之前,你需要注意,自建网站需要一定的相关知识,你可能会遇到一系列的问题,请确保你有独立解决问题的能力。
假如你想修改主题的某些组件或样式,你还需要掌握 Vue.js 的相关知识
虽然如今你可以借助 AI 来辅助完成一些需求,但知其然,知其所以然
是每一个优秀的人都应该具备的基本素养。

天气及地区获取需要 高德开放平台 相关 API

TIP

本文章基于imsyy的home里的weather组件魔改而来

在项目根目录创建一个 .env 文件

写入以下内容:

VITE_WEATHER_KEY = "你获取到的天气API KEY"

新建 Weather.vue 文件

新建在:.vitepress\theme\components\Aside\Widgets\Weather.vue
写入以下内容:

vue
<!-- 侧边栏 - 天气数据 -->
<template>
  <div class="weather-data s-card">
    <div class="title">
      <i class="iconfont icon-chart"></i>
      <span class="title-name">天气数据</span>
    </div>

    <div class="all-data">
      <!-- 城市 -->
      <div class="data-item">
        <span class="name"><i class="iconfont icon-home"></i> 城市</span>
        <span class="num">{{ weatherData?.city || '--' }}</span>
      </div>
      <!-- 温度 -->
      <div class="data-item">
        <span class="name"><i class="iconfont icon-fire"></i> 温度</span>
        <span class="num">
          {{ weatherData?.temperature != null ? weatherData.temperature + '℃' : '--' }}
        </span>
      </div>
      <!-- 湿度 -->
      <div class="data-item">
        <span class="name"><i class="iconfont icon-visibility"></i> 湿度</span>
        <span class="num">
          {{ weatherData?.humidity != null ? weatherData.humidity + '%' : '--' }}
        </span>
      </div>
      <!-- 风向 -->
      <div class="data-item">
        <span class="name"><i class="iconfont icon-arrow-right"></i> 风向</span>
        <span class="num">{{ weatherData?.winddirection || '--' }}</span>
      </div>
      <!-- 风力 -->
      <div class="data-item">
        <span class="name"><i class="iconfont icon-refresh"></i> 风力</span>
        <span class="num">{{ weatherData?.windpower || '--' }}</span>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { getAdcode, getWeather } from '@/api'

const weatherData = ref(null)

// 移动端检测:若是移动端,则不请求,直接显示“--”
const isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent)

onMounted(async () => {
  if (isMobile) return
  try {
    const { adcode } = await getAdcode(import.meta.env.VITE_WEATHER_KEY)
    const { lives } = await getWeather(import.meta.env.VITE_WEATHER_KEY, adcode)
    weatherData.value = lives[0]
  } catch (e) {
    console.error('获取天气失败:', e)
    $message.error("获取天气失败,可能是天气API超出使用上限");
  }
})
</script>

<style lang="scss" scoped>
.weather-data {
  .all-data {
    display: flex;
    flex-direction: column;
    margin-top: 12px;

    .data-item {
      width: 100%;
      padding: 8px 0;
      display: flex;
      justify-content: space-between;

      .name {
        font-weight: 500;
        display: flex;
        align-items: center;
        i {
          margin-right: 4px;
        }
      }

      .num {
        font-size: 1.1em;
      }
    }
  }
}
</style>

修改 index.vue

修改index.vue使Weather.vue挂载在侧边
index.vue路径:
.vitepress\theme\components\Aside\index.vue

vue
<template>
  <aside class="main-aside">
    <Hello v-if="theme.aside.hello.enable" class="weidgets" />
    <div class="sticky">
      <Toc v-if="theme.aside.toc.enable && showToc" class="weidgets" />
      <Weather v-if="theme.aside.weather.enable" class="weidgets" />
      <Countdown class="weidgets" />
      <Tags v-if="theme.aside.tags.enable" class="weidgets" />
      <SiteData v-if="theme.aside.siteData.enable" class="weidgets" />
    </div>
  </aside>
</template>

修改 index.js

.vitepress\theme\api\index.js

js
// 获取高德地理位置信息
export const getAdcode = async (key) => {
  const res = await fetch(`https://restapi.amap.com/v3/ip?key=${key}`);
  return await res.json();
};

// 获取高德地理天气信息
export const getWeather = async (key, city) => {
  const res = await fetch(
    `https://restapi.amap.com/v3/weather/weatherInfo?key=${key}&city=${city}`,
  );
  return await res.json();
};

修改 themeConfig.mjs

.vitepress\theme\assets\themeConfig.mjs

js
    // 天气数据
    weather: {
      enable: true,
    },

完成

按照步骤做完后,你现在dev之后应该就可以看见一个天气侧边栏出现在右边了。祝愉快~

优化:天气组件获取失败自动隐藏

.vitepress\theme\components\Aside\Widgets\Weather.vue

vue
<script setup>
import { ref, onMounted } from 'vue'
import { getAdcode, getWeather } from '@/api'

// 声明会在请求出错时抛出的事件
const emit = defineEmits(['fetch-error'])

const weatherData = ref(null)
const loading     = ref(true)
const error       = ref(false)

// 移动端检测:若是移动端,则不请求,直接不渲染
const isMobile = /Mobi|Android|iPhone|iPad|Pad|iPod/i.test(navigator.userAgent)

onMounted(async () => {
  if (isMobile) {
    loading.value = false
    return
  }
  try {
    const { adcode }        = await getAdcode(import.meta.env.VITE_WEATHER_KEY)
    const { lives }         = await getWeather(import.meta.env.VITE_WEATHER_KEY, adcode)
    weatherData.value       = lives[0]
  } catch (e) {
    console.error('获取天气失败:', e)
    error.value = true
    // 向父组件抛出“fetch-error”事件
    emit('fetch-error', e)
  } finally {
    loading.value = false
  }
})
</script>

.vitepress\theme\components\Aside\index.vue

vue
<template>
  <aside class="main-aside">
    <Hello v-if="theme.aside.hello.enable" class="weidgets" />
    <div class="sticky">
      <Toc v-if="theme.aside.toc.enable && showToc" class="weidgets" />
  <Weather
    v-if="theme.aside.weather.enable && showWeather"
    class="weidgets"
    @fetch-error="onWeatherError"
  />
      <Countdown class="weidgets" />
      <Tags v-if="theme.aside.tags.enable" class="weidgets" />
      <SiteData v-if="theme.aside.siteData.enable" class="weidgets" />
    </div>
  </aside>
</template>

<script setup>
const { theme } = useData();
const props = defineProps({
  // 显示目录
  showToc: {
    type: Boolean,
    default: false,
  },
});

const showWeather = ref(true)
// 一旦收到子组件的 fetch-error 事件,就把 showWeather 置为 false
function onWeatherError(err) {
  console.error('天气组件获取失败:', err)
  showWeather.value = false
}
</script>
赞赏博主
评论 隐私政策