Xây dựng một ứng dụng thời tiết đơn giản sử dụng Vuejs - Phần 2
Bài đăng này đã không được cập nhật trong 6 năm
Xây dựng các component
Api Endpoint.
Trong phần trước, mình đã hướng dẫn các bạn mô hinh của một ứng dụng Vue cơ bản với vuex. Để có thể nhận thông tin về thời tiết, ta cần một nguồn cung cấp dữ liệu xịn từ các endpoint api. Hiện tại mình đang sử dụng các api có sẵn của openweathermap, nhưng chúng ta có thể sử dụng bất kì api khác mà bạn cảm thấy phù hợp với úng dựng của mình. Để có thể sử dụng Openweather Api, chúng ta truy cập theo đường link như sau và hãy tạo cho mình một tài khoản.
Lập xong tài khoản, ta có thể tạo một key tương ứng. Mình cần key để gửi theo url mới có thể nhận dữ liệu từ Openweather.
Ta có thể truy cập truy cập vào api với key tướng ứng để kiếm tra xem key của chúng ta đã được chưa
http://api.openweathermap.org/data/2.5/weather?q={tên thành phố cần kiểm tra}&appid={key của bạn}
Ta sẽ nhận được dữ liệu như sau:
Rất khó nhìn phải không, do đó mình khuyên các bạn nên cài thêm extension để có thể hiện thị json một cách clean nhất. Với Chrome bạn có thể tải JSON Formatter còn Firefox thì không cần do đã được tích hợp sẵn vào trình duyệt. Sau khi format ta được dữ liệu như sau:
Dễ nhìn hơn hẳn phải không. Nào chúng ta cùng bắt tay tạo các component để hiển thị dữ liệu này.
Main Component
Đây là nơi sẽ hiển thị các Weather Card của các thành phố mà người dùng chọn. Từ đây người dùng có thể bấm vào các Weather Card này để chi tiết thông tin thời tiết thành phố đó hoặc mở sang trang thêm thành phố mà mình sẽ nói ở phần sau.
Đầu tiên là chúng ta tạo component.
<template>
<div class="container_wrap">
<add-weather-card/>
</div>
</template>
<script>
import AddWeatherCard from "@/components/AddWeatherCard";
export default {
components: {
AddWeatherCard
}
};
</script>
<style>
.weather__card {
float: left;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr 1fr;
box-shadow: 0 0 2rem rgba(0, 0, 255, 0.1);
justify-items: center;
padding: 1rem;
margin: 2rem;
width: 18rem;
height: 30rem;
cursor: pointer;
background-color: white;
border-radius: 1.75rem;
animation: 1.25s ease-in-out 0ms 1 fadein;
}
</style>
WeatherCard Component
Trước tiên ta tạo một Weather Card có tác dụng thêm thành phố vào danh sách.
<template>
<router-link tag="section" to='/addcity' class="weather__card">
<div class="add_section__button">
<button class="button__add">
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11h-4v4h-2v-4H7v-2h4V7h2v4h4v2z" />
</svg>
</button>
</div>
<div class="add_section__text">
Add city
</div>
</router-link>
</template>
<style scope>
.add_section__button {
margin-top: 100px;
}
.button__add {
height: 100px;
width: 100px;
border: 3px solid transparent;
border-radius: 50%;
background-color: transparent;
color: var(--dark-color);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
line-height: 0;
transition: transform 300ms ease-out, border-color 300ms ease-out;
}
.button__add svg {
height: 100%;
width: 100%;
fill: var(--accent-color);
transition: fill 300ms ease-out;
}
</style>
Đến bước này, chúng ta cần đến Vue-route để định tuyến đến hiển thị các component. Để cài vue-route ta gõ câu lệnh sau:
npm install --save vue-router
Và cần khai báo với vue là ta sẽ sử dụng vue-route. Nhưng để giúp khai báo một cách minh bạch, mình khuyên các bạn nên tách việc khai báo vue-route ra một file khác rồi import vào main.js như mình đã làm với store. Điều này giúp ta có thể quản lý code tốt hơn.
Tạo một file theo đường dẫn như sau :
src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from '@/components/Main'
Vue.use(Router)
export default new Router({
routes: [{
path: '/',
name: 'Main',
component: Main
},
]
})
Và trong file main.js ta sửa lại như sau:
import Vue from "vue";
import App from "./App.vue";
import store from "./store/index.js";
import router from "./router";
Vue.config.productionTip = false;
new Vue({
store,
router,
render: h => h(App)
}).$mount("#app");
Cuối cùng để hiển thị Main Component, ta vào App Component và sửa template như sau:
<template>
<div>
<div :class="$style.container">
<router-view/>
</div>
<sidebar/>
<sidebar-toggle/>
</div>
</template>
Và úm ba la xì bùa, ta đã nhận được thành quả như sau
Bạn chắc đang thắc mắc, là tại sao Vue có thể hiểu để hiện thị được như vậy. Như cách thông thường thi khi ta muốn hiển thị 1 component thì ta khai báo components trong script và gọi tên component đó ở trong template. <router-view/> có tác dụng tương tự vây nhưng cơ chế hơi khác một chút. Vue sẽ đọc đường dẫn trên URL, tìm xem ứng với URL này được khai báo component nào, nếu có Vue sẽ gọi đến component đó và hiển thị ra cho người dùng.
Bây giờ chúng ta tạo một một component nữa tên là WeatherCard để hiển thị thông tin thời tiết.
<template>
<router-link tag="section" :to="`/detail/${city}`" class="weather__card">
<span class="city-name__text">{{city}}</span>
<div class="weather-icon__container">
<div v-if="condition === 'Clouds' || condition ==='Haze'">
<img src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4Ij4KPHBhdGggc3R5bGU9ImZpbGw6IzI2OUFGMjsiIGQ9Ik00MjkuMzgyLDQxMi41MTJjNDUuNjI5LDAsODIuNjE4LTM2Ljk5LDgyLjYxOC04Mi42MTljMC0zOS4xOTYtMjcuMzA1LTcxLjk5NC02My45MjUtODAuNDY4ICBjLTAuNzQ5LTgyLjkzNC02OC4yLTE0OS45MzctMTUxLjMxMS0xNDkuOTM3Yy01Ny4wNiwwLTEwNi43MzUsMzEuNTg1LTEzMi41MTgsNzguMjIxYy03Ljg0NS0zLjM5NS0xNi40OTItNS4yODUtMjUuNTgzLTUuMjg1ICBjLTMyLjc1LDAtNTkuNzc5LDI0LjQyMS02My45MTcsNTYuMDQyQzMyLjEzMSwyMzYuOTE2LDAsMjc0LjQ5OCwwLDMxOS41OTRjMCw1MS4zMTgsNDEuNjAxLDkyLjkxOCw5Mi45MTcsOTIuOTE4ICBDMTA3LjIxMSw0MTIuNTEyLDQxOS41ODksNDEyLjUxMiw0MjkuMzgyLDQxMi41MTJ6Ii8+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=" />
</div>
<div v-else-if="condition === 'Rain' || condition === 'Drizzle'">
<img src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIuMDAxIDUxMi4wMDEiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMi4wMDEgNTEyLjAwMTsiIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSI1MTJweCIgaGVpZ2h0PSI1MTJweCI+CjxwYXRoIHN0eWxlPSJmaWxsOiM2OENDRUE7IiBkPSJNMjg2LjQzNCwyMjEuNzZjLTAuMDM5LTAuMTA1LTAuMDg0LTAuMjA1LTAuMTM1LTAuMzA0TDE3Mi4xNzIsMS40ODYgIEMxNzEuNjk4LDAuNTc0LDE3MC43NTcsMCwxNjkuNzI4LDBzLTEuOTcsMC41NzQtMi40NDQsMS40ODZMNTMuMTU3LDIyMS40NTZjLTAuMDUyLDAuMDk5LTAuMDk3LDAuMi0wLjEzNSwwLjMwNCAgYy0xMi4wMjgsMzIuMjA1LTEwLjc5Nyw2Ny4xNjQsMy40NjksOTguNDM3YzE0LjI2NSwzMS4yNzUsMzkuODU3LDU1LjEyLDcyLjA2MSw2Ny4xNDZjMTMuMTksNC45MjYsMjcuMDE0LDcuNjQyLDQxLjA5Miw4LjA3ICBjMC4wMjgsMC4wMDEsMC4wNTYsMC4wMDEsMC4wODQsMC4wMDFzMC4wNTYsMCwwLjA4NC0wLjAwMWMxNC4wNzctMC40MywyNy45MDItMy4xNDUsNDEuMDktOC4wNyAgYzMyLjIwMy0xMi4wMjYsNTcuNzk2LTM1Ljg3Miw3Mi4wNjItNjcuMTQ2QzI5Ny4yMjksMjg4LjkyNCwyOTguNDYyLDI1My45NjUsMjg2LjQzNCwyMjEuNzZ6Ii8+CjxnPgoJPHBhdGggc3R5bGU9ImZpbGw6IzVBQkNFMjsiIGQ9Ik0xNzguNTI4LDM3My4zNDRjLTI3LjM1Ny0yNi45NTgtNDIuNTc4LTYyLjk1OS00Mi44NjEtMTAxLjM3MSAgIGMtMC4wMDEtMC4xMjMsMC4wMDYtMC4yNDgsMC4wMjEtMC4zNzFMMTY2LjM2MSwzLjI2Nkw1My4xNTcsMjIxLjQ1NmMtMC4wNTIsMC4wOTktMC4wOTcsMC4yLTAuMTM1LDAuMzA0ICAgYy0xMi4wMjgsMzIuMjA1LTEwLjc5Nyw2Ny4xNjQsMy40NjksOTguNDM3YzE0LjI2NSwzMS4yNzUsMzkuODU3LDU1LjEyLDcyLjA2MSw2Ny4xNDZjMTMuMTksNC45MjYsMjcuMDE0LDcuNjQyLDQxLjA5Miw4LjA3ICAgYzAuMDI4LDAuMDAxLDAuMDU2LDAuMDAxLDAuMDg0LDAuMDAxczAuMDU2LDAsMC4wODQtMC4wMDFjMTAuMjYzLTAuMzEzLDIwLjM4OS0xLjg1NiwzMC4yMzUtNC41NyAgIEMxOTIuNDEyLDM4NS43NjUsMTg1LjIwNiwzNzkuOTIyLDE3OC41MjgsMzczLjM0NHoiLz4KCTxwYXRoIHN0eWxlPSJmaWxsOiM1QUJDRTI7IiBkPSJNNDYzLjA4NCwyODAuNzhjLTAuMDM5LTAuMTA0LTAuMDg0LTAuMjA1LTAuMTM1LTAuMzA0bC01Ni4yNzMtMTA4LjQ2MSAgIGMtMC40NzQtMC45MTItMS40MTUtMS40ODYtMi40NDQtMS40ODZjLTEuMDI5LDAtMS45NywwLjU3My0yLjQ0NCwxLjQ4NmwtNTYuMjczLDEwOC40NjFjLTAuMDUyLDAuMDk5LTAuMDk3LDAuMi0wLjEzNSwwLjMwNCAgIGMtNi4wNjEsMTYuMjI4LTUuNDQsMzMuODQ0LDEuNzQ5LDQ5LjYwMmM3LjE4NywxNS43NTksMjAuMDgzLDI3Ljc3NSwzNi4zMTMsMzMuODM2YzYuNjQ2LDIuNDgxLDEzLjYxMywzLjg0OSwyMC43MDYsNC4wNjYgICBjMC4wMjgsMC4wMDEsMC4wNTYsMC4wMDEsMC4wODQsMC4wMDFzMC4wNTYsMCwwLjA4NC0wLjAwMWM3LjA5MS0wLjIxNywxNC4wNTgtMS41ODUsMjAuNzA1LTQuMDY2ICAgQzQ1OC41MTYsMzUxLjcwOSw0NzUuNTkyLDMxNC4yNzgsNDYzLjA4NCwyODAuNzh6Ii8+CjwvZz4KPHBhdGggc3R5bGU9ImZpbGw6IzY4Q0NFQTsiIGQ9Ik00NjMuMDg0LDI4MC43OGMtMC4wMzktMC4xMDQtMC4wODQtMC4yMDUtMC4xMzUtMC4zMDRsLTU2LjI3My0xMDguNDYxICBjLTAuNDc0LTAuOTEyLTEuNDE1LTEuNDg2LTIuNDQ0LTEuNDg2Yy0wLjAwNSwwLTAuMDExLDAuMDAyLTAuMDE3LDAuMDAybC0yMi4xNTIsMTI4LjgyMWMtMC4wMjEsMC4xMjEtMC4wMzQsMC4yNDMtMC4wMzksMC4zNjUgIGMtMC45MzgsMTkuMDY4LDUuNjA0LDM3LjM1NiwxOC40MjIsNTEuNDk5YzUuNDkyLDYuMDYsMTEuODYsMTEuMDM4LDE4Ljg0LDE0LjgzNmMxLjkzNC0wLjUyNCwzLjg0OC0xLjEyOCw1LjczNS0xLjgzMyAgQzQ1OC41MTYsMzUxLjcwOSw0NzUuNTkyLDMxNC4yNzgsNDYzLjA4NCwyODAuNzh6Ii8+CjxwYXRoIHN0eWxlPSJmaWxsOiM1QUJDRTI7IiBkPSJNMzUwLjkxLDQ1Mi4xNDVsLTM3LjQ5NS03Mi4yNzFjLTAuNDc0LTAuOTEyLTEuNDE1LTEuNDg2LTIuNDQ0LTEuNDg2ICBjLTEuMDI5LDAtMS45NywwLjU3NC0yLjQ0NCwxLjQ4NmwtMzcuNSw3Mi4yNzFjLTAuMDUyLDAuMDk5LTAuMDk3LDAuMjAyLTAuMTM1LDAuMzA1Yy04LjUxMSwyMi43OTYsMy4xMDgsNDguMjY4LDI1LjkwNCw1Ni43ODMgIGM0LjUyMywxLjY4Niw5LjI2NCwyLjYxOCwxNC4wOSwyLjc2N2MwLjAyOSwwLjAwMSwwLjA1NywwLjAwMSwwLjA4NSwwLjAwMXMwLjA1NiwwLDAuMDg1LTAuMDAxICBjNC44MjItMC4xNDksOS41NjItMS4wNzksMTQuMDg5LTIuNzY4YzIyLjc5NS04LjUxMywzNC40MTQtMzMuOTg2LDI1LjkwMS01Ni43ODNDMzUxLjAwNyw0NTIuMzQ0LDM1MC45NjEsNDUyLjI0MywzNTAuOTEsNDUyLjE0NXoiLz4KPHBhdGggc3R5bGU9ImZpbGw6IzY4Q0NFQTsiIGQ9Ik0zNTEuMDQ1LDQ1Mi40NDljLTAuMDM5LTAuMTA1LTAuMDg0LTAuMjA2LTAuMTM1LTAuMzA0bC0zNy40OTUtNzIuMjcxICBjLTAuNDc0LTAuOTEyLTEuNDE1LTEuNDg2LTIuNDQ0LTEuNDg2Yy0wLjI5MywwLTAuNTc0LDAuMDU3LTAuODQ0LDAuMTQ1bC0xNS4zOTQsOTAuNTY2Yy0wLjAyMiwwLjEyMi0wLjAzNCwwLjI0OS0wLjA0MSwwLjM3NCAgYy0wLjgwNiwxNy4xMzQsNy4xOCwzMi42ODIsMTkuOTcxLDQyLjI2OGMzLjU3Ni0wLjQwMyw3LjA5LTEuMjQzLDEwLjQ4Mi0yLjUwOEMzNDcuOTQsNTAwLjcxOCwzNTkuNTU5LDQ3NS4yNDUsMzUxLjA0NSw0NTIuNDQ5eiIvPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K" />
</div>
<div v-else-if="condition === 'Storm'">
<img src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDM4NS4zNDQgMzg1LjM0NCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzg1LjM0NCAzODUuMzQ0OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4Ij4KPGc+Cgk8cGF0aCBzdHlsZT0iZmlsbDojNTBEMUVGOyIgZD0iTTMwMy4yMDQsMTExLjA1NGM0NS4zNjUsMCw4Mi4xNCwzNi43NzUsODIuMTQsODIuMTRzLTM2Ljc3NSw4Mi4xNC04Mi4xNCw4Mi4xNGwwLDBINjQuMjQ0ICAgQzI4Ljc2NSwyNzUuMzM3LDAuMDAyLDI0Ni41NzgsMCwyMTEuMDk5czI4Ljc1Ny02NC4yNDIsNjQuMjM2LTY0LjI0NGMwLjAwMywwLDAuMDA2LDAsMC4wMDksMGMxLjQ2NywwLDIuOTIsMCw0LjM2LDAgICBjLTUuMzgtNTQuOTY2LDM0LjgxOC0xMDMuODg1LDg5Ljc4NC0xMDkuMjY1YzUxLjg5OS01LjA3OSw5OC45ODgsMzAuNTc0LDEwOC4xNzYsODEuOTA1ICAgQzI3Ny45NTcsMTEzLjg3NCwyOTAuNTAxLDExMC45ODUsMzAzLjIwNCwxMTEuMDU0TDMwMy4yMDQsMTExLjA1NHoiLz4KCTxwYXRoIHN0eWxlPSJmaWxsOiM1MEQxRUY7IiBkPSJNMjE1LjUyNCwyODkuMjU0bDIwLjU2LDI5LjM2YzIuMjk4LDMuMzE0LDEuNDc0LDcuODYyLTEuODQsMTAuMTZzLTcuODYyLDEuNDc0LTEwLjE2LTEuODRsMCwwICAgbC0yNi40LTM3LjY4TDIxNS41MjQsMjg5LjI1NHogTTE2Ni44ODQsMjg5LjI1NGwzMy4yOCw0Ny41MmMyLjI5OCwzLjMxNCwxLjQ3NCw3Ljg2Mi0xLjg0LDEwLjE2cy03Ljg2MywxLjQ3NC0xMC4xNi0xLjg0bDAsMCAgIGwtMzkuMTItNTZMMTY2Ljg4NCwyODkuMjU0eiBNMTE4LjI0NCwyODkuMjU0bDIwLjU2LDI5LjM2YzIuMjk4LDMuMzE0LDEuNDc0LDcuODYyLTEuODQsMTAuMTZzLTcuODYzLDEuNDc0LTEwLjE2LTEuODRsMCwwICAgbC0yNi4xMi0zNy42OEwxMTguMjQ0LDI4OS4yNTR6IE0yNjQuMTY0LDI4OS4yNTRsMzMuMjgsNDcuNTJjMi4yOTgsMy4zMTQsMS40NzQsNy44NjItMS44NCwxMC4xNnMtNy44NjIsMS40NzQtMTAuMTYtMS44NGwwLDAgICBsLTEyLjc2LTE4LjE2bC0yNi40LTM3LjY4SDI2NC4xNjR6Ii8+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==" />
</div>
<div v-else-if="condition === 'Sunny' || condition === 'Clear'">
<img src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQwMCA0MDAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQwMCA0MDA7IiB4bWw6c3BhY2U9InByZXNlcnZlIiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiPgo8cGF0aCBzdHlsZT0iZmlsbDojRkZFRDAwOyIgZD0iTTEzMC40Niw2NS4wMmMyLjEyNiwzLjQxMiwxLjA4NCw3LjkwMi0yLjMyOSwxMC4wMjljLTMuNDEyLDIuMTI2LTcuOTAyLDEuMDg0LTEwLjAyOS0yLjMyOSAgYy0wLjA4NS0wLjEzNy0wLjE2Ni0wLjI3Ny0wLjI0Mi0wLjQyTDk3LjM0LDM2Ljc4Yy0yLjEyNi0zLjQxMi0xLjA4NC03LjkwMiwyLjMyOS0xMC4wMjljMy40MTItMi4xMjYsNy45MDItMS4wODQsMTAuMDI5LDIuMzI5ICBjMC4wODUsMC4xMzcsMC4xNjYsMC4yNzcsMC4yNDMsMC40MkwxMzAuNDYsNjUuMDJ6IE03Mi4zLDExNy44NmMzLjQxMiwyLjEyNiw0LjQ1NSw2LjYxNiwyLjMyOSwxMC4wMjkgIGMtMi4wMjcsMy4yNTMtNi4yMjgsNC4zNzctOS42MDksMi41NzFsLTM1LjU2LTIwLjUyYy0zLjQxMi0yLjEyNi00LjQ1NS02LjYxNi0yLjMyOS0xMC4wMjljMi4wMjctMy4yNTMsNi4yMjgtNC4zNzcsOS42MDktMi41NzEgIEw3Mi4zLDExNy44NnogTTQ4LjMsMTkyLjY2YzQuMDMyLDAsNy4zLDMuMjY4LDcuMyw3LjNzLTMuMjY4LDcuMy03LjMsNy4zaC00MWMtNC4wMzIsMC03LjMtMy4yNjgtNy4zLTcuMyAgYzAtNC4wMzIsMy4yNjgtNy4zLDcuMy03LjNsMCwwSDQ4LjN6IE02NC45OCwyNjkuNDZjMy40MTItMi4xMjYsNy45MDItMS4wODQsMTAuMDI5LDIuMzI5YzIuMTI2LDMuNDEyLDEuMDg0LDcuOTAyLTIuMzI5LDEwLjAyOSAgYy0wLjEzNywwLjA4NS0wLjI3NywwLjE2Ni0wLjQyLDAuMjQybC0zNS40OCwyMC41NmMtMy40MTIsMi4xMjYtNy45MDIsMS4wODQtMTAuMDI5LTIuMzI5Yy0yLjEyNi0zLjQxMi0xLjA4NC03LjkwMiwyLjMyOS0xMC4wMjkgIGMwLjEzNy0wLjA4NSwwLjI3Ny0wLjE2NiwwLjQyLTAuMjQzTDY0Ljk4LDI2OS40NnogTTExNy44NiwzMjcuNjZjMi4xMjYtMy40MTIsNi42MTYtNC40NTUsMTAuMDI5LTIuMzI5ICBjMy4yNTMsMi4wMjcsNC4zNzcsNi4yMjgsMi41NzEsOS42MDlsLTIwLjUyLDM1LjU2Yy0xLjg5NCwzLjU0Ni02LjMwNSw0Ljg4Ni05Ljg1MSwyLjk5MXMtNC44ODYtNi4zMDUtMi45OTEtOS44NTEgIGMwLjA3Ni0wLjE0MywwLjE1Ny0wLjI4MywwLjI0My0wLjQyTDExNy44NiwzMjcuNjZ6IE0xOTIuNjYsMzUxLjY2YzAtNC4wMzIsMy4yNjgtNy4zLDcuMy03LjNjNC4wMzIsMCw3LjMsMy4yNjgsNy4zLDcuM3Y0MS4wNCAgYzAsNC4wMzItMy4yNjgsNy4zLTcuMyw3LjNjLTQuMDMyLDAtNy4zLTMuMjY4LTcuMy03LjNWMzUxLjY2eiBNMjY5LjQ2LDMzNC45OGMtMS44OTQtMy41NDYtMC41NTUtNy45NTcsMi45OTEtOS44NTEgIGMzLjM4LTEuODA2LDcuNTgyLTAuNjgxLDkuNjA5LDIuNTcxbDIwLjUyLDM1LjU2YzIuMTI2LDMuNDEyLDEuMDg0LDcuOTAyLTIuMzI5LDEwLjAyOXMtNy45MDIsMS4wODQtMTAuMDI5LTIuMzI5ICBjLTAuMDg1LTAuMTM3LTAuMTY2LTAuMjc3LTAuMjQzLTAuNDJMMjY5LjQ2LDMzNC45OHogTTMyNy42NiwyODIuMWMtMy41NDYtMS44OTQtNC44ODYtNi4zMDUtMi45OTEtOS44NTFzNi4zMDUtNC44ODYsOS44NTEtMi45OTEgIGMwLjE0MywwLjA3NiwwLjI4MywwLjE1NywwLjQyLDAuMjQzbDM1LjU2LDIwLjUyYzMuNTQ2LDEuODk0LDQuODg2LDYuMzA1LDIuOTkxLDkuODUxcy02LjMwNSw0Ljg4Ni05Ljg1MSwyLjk5MSAgYy0wLjE0My0wLjA3Ni0wLjI4My0wLjE1Ny0wLjQyLTAuMjQzTDMyNy42NiwyODIuMXogTTM1MS42NiwyMDcuM2MtNC4wMzIsMC03LjMtMy4yNjgtNy4zLTcuM2MwLTQuMDMyLDMuMjY4LTcuMyw3LjMtNy4zbDAsMGg0MS4wNCAgYzQuMDMyLDAsNy4zLDMuMjY4LDcuMyw3LjNjMCw0LjAzMi0zLjI2OCw3LjMtNy4zLDcuM2wwLDBIMzUxLjY2eiBNMzM0Ljk4LDEzMC41Yy0zLjQxMiwyLjEyNi03LjkwMiwxLjA4NC0xMC4wMjktMi4zMjkgIGMtMi4xMjYtMy40MTItMS4wODQtNy45MDIsMi4zMjktMTAuMDI5YzAuMTM3LTAuMDg1LDAuMjc3LTAuMTY2LDAuNDItMC4yNDNsMzUuNTYtMjAuNTJjMy41NDYtMS44OTQsNy45NTctMC41NTUsOS44NTEsMi45OTEgIGMxLjgwNiwzLjM4LDAuNjgxLDcuNTgyLTIuNTcxLDkuNjA5TDMzNC45OCwxMzAuNXogTTI4Mi4xLDcyLjNjLTEuODk0LDMuNTQ2LTYuMzA1LDQuODg2LTkuODUxLDIuOTkxICBjLTMuNTQ2LTEuODk0LTQuODg2LTYuMzA1LTIuOTkxLTkuODUxYzAuMDc2LTAuMTQzLDAuMTU3LTAuMjgzLDAuMjQzLTAuNDJsMjAuNTItMzUuNTZjMS44OTQtMy41NDYsNi4zMDUtNC44ODYsOS44NTEtMi45OTEgIGMzLjU0NiwxLjg5NCw0Ljg4Niw2LjMwNSwyLjk5MSw5Ljg1MWMtMC4wNzYsMC4xNDMtMC4xNTcsMC4yODMtMC4yNDMsMC40MkwyODIuMSw3Mi4zeiBNMjA3LjMsNDguM2MwLDQuMDMyLTMuMjY4LDcuMy03LjMsNy4zICBjLTQuMDMyLDAtNy4zLTMuMjY4LTcuMy03LjNsMCwwdi00MWMwLTQuMDMyLDMuMjY4LTcuMyw3LjMtNy4zYzQuMDMyLDAsNy4zLDMuMjY4LDcuMyw3LjNWNDguM3ogTTE5OS45OCw5OS45OCAgYzU1LjIyOCwwLDEwMCw0NC43NzIsMTAwLDEwMHMtNDQuNzcyLDEwMC0xMDAsMTAwcy0xMDAtNDQuNzcyLTEwMC0xMDBTMTQ0Ljc1Miw5OS45OCwxOTkuOTgsOTkuOTh6Ii8+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=" />
</div>
<div v-else-if="condition === 'Fog'">
<img src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQ4MCA0ODAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQ4MCA0ODA7IiB4bWw6c3BhY2U9InByZXNlcnZlIiB3aWR0aD0iMTI4cHgiIGhlaWdodD0iMTI4cHgiPgo8Zz4KCTxnPgoJCTxyZWN0IHg9IjAiIHk9IjQxNiIgd2lkdGg9IjQ4MCIgaGVpZ2h0PSIxNiIgZmlsbD0iIzAwMDAwMCIvPgoJPC9nPgo8L2c+CjxnPgoJPGc+CgkJPHJlY3QgeD0iMCIgeT0iNDY0IiB3aWR0aD0iNDgwIiBoZWlnaHQ9IjE2IiBmaWxsPSIjMDAwMDAwIi8+Cgk8L2c+CjwvZz4KPGc+Cgk8Zz4KCQk8cmVjdCB4PSIxMzYiIHdpZHRoPSIxNiIgaGVpZ2h0PSIzMiIgZmlsbD0iIzAwMDAwMCIvPgoJPC9nPgo8L2c+CjxnPgoJPGc+CgkJPHJlY3QgeD0iMCIgeT0iMTI4IiB3aWR0aD0iMzIiIGhlaWdodD0iMTYiIGZpbGw9IiMwMDAwMDAiLz4KCTwvZz4KPC9nPgo8Zz4KCTxnPgoJCTxwYXRoIGQ9Ik0zOTAuMjQsMTkyLjk1MkMzNzYuMDMyLDE1MC4zODQsMzI3LjMwNCwxMjAsMjcyLDEyMGMtMTIuMDMyLDAtMjMuMjk2LDEuNTY4LTMzLjY5Niw0LjUzNiAgICBDMjI5LjE4NCw4MC44NTYsMTg5LjczNiw0OCwxNDQsNDhjLTUyLjA0LDAtOTYsNDMuOTYtOTYsOTZjMCwzNS43NTIsMjAuNjQsNjcuODQsNTMuMjI0LDgzLjkzNiAgICBjLTAuODE2LDEuMzI4LTEuNzUyLDIuNi0yLjQ0LDMuOTg0Yy0yLjE3NiwwLTQuNzUyLDAuMDI0LTcuNTEyLDAuMDU2TDg4LDIzMmMtNDguNTIsMC04OCwzNS44ODgtODgsODBzMzkuNDgsODAsODgsODBoMjg4LjI3MiAgICBDNDM1LjQwOCwzODkuOTc2LDQ4MCwzNDUuMjcyLDQ4MCwyODhDNDgwLDI0MS41MDQsNDQzLjM0NCwyMDMuMDE2LDM5MC4yNCwxOTIuOTUyeiBNNjQsMTQ0YzAtNDMuMzYsMzYuNjQtODAsODAtODAgICAgYzM4LjMwNCwwLDcyLjQ0LDI5LjE1Miw3OS4xMTIsNjYuMTkyYy0yNC45MTIsMTEuNjA4LTQzLjM2LDMyLjgxNi01My4wOCw2Mi4wMjRjLTIzLjYsMS40NzItNDQuMzY4LDkuOTYtNTguMzM2LDIyLjkyOCAgICBDODIuNjI0LDIwMi4zMjgsNjQsMTc0Ljc5Miw2NCwxNDR6IE0zNzYsMzc2SDg4Yy0zOS42OTYsMC03Mi0yOC43MTItNzItNjRjMC0zNS4yODgsMzIuMzA0LTY0LDcyLTY0bDMuNDMyLTAuMDI0ICAgIGMxLjg5Ni0wLjAxNiw1LjMyOC0wLjA1Niw4LjA3Mi0wLjA1NmMxLjgwOCwwLDMuMzEyLDAuMDE2LDMuODk2LDAuMDU2YzMuNTY4LDAuMjI0LDYuODg4LTEuODcyLDguMTEyLTUuMjQgICAgYzcuMzA0LTIwLjA0LDMzLjg2NC0zNC4zMjgsNjQuNTkyLTM0LjczNmMzLjUzNi0wLjA0OCw2LjYxNi0yLjQwOCw3LjU4NC01LjgwOEMxOTUuNzA0LDE2MC4xMjgsMjI3Ljg4OCwxMzYsMjcyLDEzNiAgICBjNDkuNDk2LDAsOTQuMzIsMjguMzkyLDEwNC4yNjQsNjYuMDRjMC44MTYsMy4wOCwzLjM3Niw1LjM4NCw2LjUyOCw1Ljg2NEM0MzAuNjA4LDIxNS4yMDgsNDY0LDI0OC4xNDQsNDY0LDI4OCAgICBDNDY0LDMzNi40NTYsNDI2LjA0OCwzNzQuMjg4LDM3NiwzNzZ6IiBmaWxsPSIjMDAwMDAwIi8+Cgk8L2c+CjwvZz4KPGc+Cgk8Zz4KCQk8cmVjdCB4PSI1MS4wNjkiIHk9IjM1LjEyMSIgdHJhbnNmb3JtPSJtYXRyaXgoMC43MDg4IC0wLjcwNTQgMC43MDU0IDAuNzA4OCAtMTkuMzg4IDU2Ljc3MTcpIiB3aWR0aD0iMTYiIGhlaWdodD0iMzMuNDk2IiBmaWxsPSIjMDAwMDAwIi8+Cgk8L2c+CjwvZz4KPGc+Cgk8Zz4KCQk8cmVjdCB4PSIyNy4wMjkiIHk9IjIxMS45OTkiIHRyYW5zZm9ybT0ibWF0cml4KDAuNzA3MSAtMC43MDcxIDAuNzA3MSAwLjcwNzEgLTE0Mi42NzUyIDk1LjU0OTIpIiB3aWR0aD0iMzMuOTQ0IiBoZWlnaHQ9IjE2IiBmaWxsPSIjMDAwMDAwIi8+Cgk8L2c+CjwvZz4KPGc+Cgk8Zz4KCQk8cmVjdCB4PSIyMTkuMDMxIiB5PSI0NC4wMDMiIHRyYW5zZm9ybT0ibWF0cml4KDAuNzA3MSAtMC43MDcxIDAuNzA3MSAwLjcwNzEgMzIuMzUyIDE4Mi4xMTAyKSIgd2lkdGg9IjMzLjk0NCIgaGVpZ2h0PSIxNiIgZmlsbD0iIzAwMDAwMCIvPgoJPC9nPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=" />
</div>
<div v-else-if="condition === 'Mist'">
<img src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjEyOHB4IiBoZWlnaHQ9IjEyOHB4Ij4KPGc+Cgk8Zz4KCQk8Zz4KCQkJPHBhdGggZD0iTTQyLjY2NywxODcuNzMzaDI3My4wNjdjNC43MTksMCw4LjUzMy0zLjgyMyw4LjUzMy04LjUzM3MtMy44MTQtOC41MzMtOC41MzMtOC41MzNINDIuNjY3ICAgICBjLTQuNzE5LDAtOC41MzMsMy44MjMtOC41MzMsOC41MzNTMzcuOTQ4LDE4Ny43MzMsNDIuNjY3LDE4Ny43MzN6IiBmaWxsPSIjMDAwMDAwIi8+CgkJCTxwYXRoIGQ9Ik0yNDcuNDY3LDI3My4wNjdINTkuNzMzYy00LjcxOSwwLTguNTMzLDMuODIzLTguNTMzLDguNTMzYzAsNC43MSwzLjgxNCw4LjUzMyw4LjUzMyw4LjUzM2gxODcuNzMzICAgICBjNC43MTksMCw4LjUzMy0zLjgyMyw4LjUzMy04LjUzM0MyNTYsMjc2Ljg5LDI1Mi4xODYsMjczLjA2NywyNDcuNDY3LDI3My4wNjd6IiBmaWxsPSIjMDAwMDAwIi8+CgkJCTxwYXRoIGQ9Ik0xMTkuNDY3LDIzMC40YzAtNC43MS0zLjgxNC04LjUzMy04LjUzMy04LjUzM0g4LjUzM0MzLjgxNCwyMjEuODY3LDAsMjI1LjY5LDAsMjMwLjRjMCw0LjcxLDMuODE0LDguNTMzLDguNTMzLDguNTMzICAgICBoMTAyLjRDMTE1LjY1MiwyMzguOTMzLDExOS40NjcsMjM1LjExLDExOS40NjcsMjMwLjR6IiBmaWxsPSIjMDAwMDAwIi8+CgkJCTxwYXRoIGQ9Ik05My44NjcsMTM2LjUzM2gxMTkuNDY3YzQuNzE5LDAsOC41MzMtMy44MjMsOC41MzMtOC41MzNjMC00LjcxLTMuODE0LTguNTMzLTguNTMzLTguNTMzSDkzLjg2NyAgICAgYy00LjcxOSwwLTguNTMzLDMuODIzLTguNTMzLDguNTMzQzg1LjMzMywxMzIuNzEsODkuMTQ4LDEzNi41MzMsOTMuODY3LDEzNi41MzN6IiBmaWxsPSIjMDAwMDAwIi8+CgkJCTxwYXRoIGQ9Ik0yMzguOTMzLDEyOGMwLDQuNzEsMy44MTQsOC41MzMsOC41MzMsOC41MzNINDM1LjJjNC43MTksMCw4LjUzMy0zLjgyMyw4LjUzMy04LjUzM2MwLTQuNzEtMy44MTQtOC41MzMtOC41MzMtOC41MzMgICAgIEgyNDcuNDY3QzI0Mi43NDgsMTE5LjQ2NywyMzguOTMzLDEyMy4yOSwyMzguOTMzLDEyOHoiIGZpbGw9IiMwMDAwMDAiLz4KCQkJPHBhdGggZD0iTTM0OS44NjcsMTcwLjY2N2MtNC43MTksMC04LjUzMywzLjgyMy04LjUzMyw4LjUzM3MzLjgxNCw4LjUzMyw4LjUzMyw4LjUzM2g2OC4yNjdjNC43MTksMCw4LjUzMy0zLjgyMyw4LjUzMy04LjUzMyAgICAgcy0zLjgxNC04LjUzMy04LjUzMy04LjUzM0gzNDkuODY3eiIgZmlsbD0iIzAwMDAwMCIvPgoJCQk8cGF0aCBkPSJNMTc5LjIsODUuMzMzaDIyMS44NjdjNC43MTksMCw4LjUzMy0zLjgyMyw4LjUzMy04LjUzM3MtMy44MTQtOC41MzMtOC41MzMtOC41MzNIMTc5LjIgICAgIGMtNC43MTksMC04LjUzMywzLjgyMy04LjUzMyw4LjUzM1MxNzQuNDgxLDg1LjMzMywxNzkuMiw4NS4zMzN6IiBmaWxsPSIjMDAwMDAwIi8+CgkJCTxwYXRoIGQ9Ik00NjkuMzMzLDI3My4wNjdIMjgxLjZjLTQuNzE5LDAtOC41MzMsMy44MjMtOC41MzMsOC41MzNjMCw0LjcxLDMuODE0LDguNTMzLDguNTMzLDguNTMzaDE4Ny43MzMgICAgIGM0LjcxOSwwLDguNTMzLTMuODIzLDguNTMzLTguNTMzQzQ3Ny44NjcsMjc2Ljg5LDQ3NC4wNTIsMjczLjA2Nyw0NjkuMzMzLDI3My4wNjd6IiBmaWxsPSIjMDAwMDAwIi8+CgkJCTxwYXRoIGQ9Ik00MTguMTMzLDM3NS40NjdIMjQ3LjQ2N2MtNC43MTksMC04LjUzMywzLjgyMy04LjUzMyw4LjUzM3MzLjgxNCw4LjUzMyw4LjUzMyw4LjUzM2gxNzAuNjY3ICAgICBjNC43MTksMCw4LjUzMy0zLjgyMyw4LjUzMy04LjUzM1M0MjIuODUyLDM3NS40NjcsNDE4LjEzMywzNzUuNDY3eiIgZmlsbD0iIzAwMDAwMCIvPgoJCQk8cGF0aCBkPSJNMzY2LjkzMyw0MjYuNjY3SDE0NS4wNjdjLTQuNzE5LDAtOC41MzMsMy44MjMtOC41MzMsOC41MzNzMy44MTQsOC41MzMsOC41MzMsOC41MzNoMjIxLjg2NyAgICAgYzQuNzE5LDAsOC41MzMtMy44MjMsOC41MzMtOC41MzNTMzcxLjY1Miw0MjYuNjY3LDM2Ni45MzMsNDI2LjY2N3oiIGZpbGw9IiMwMDAwMDAiLz4KCQkJPHBhdGggZD0iTTUwMy40NjcsMjIxLjg2N2gtMzU4LjRjLTQuNzE5LDAtOC41MzMsMy44MjMtOC41MzMsOC41MzNjMCw0LjcxLDMuODE0LDguNTMzLDguNTMzLDguNTMzaDM1OC40ICAgICBjNC43MTksMCw4LjUzMy0zLjgyMyw4LjUzMy04LjUzM0M1MTIsMjI1LjY5LDUwOC4xODYsMjIxLjg2Nyw1MDMuNDY3LDIyMS44Njd6IiBmaWxsPSIjMDAwMDAwIi8+CgkJCTxwYXRoIGQ9Ik05My44NjcsMzI0LjI2N2MtNC43MTksMC04LjUzMywzLjgyMy04LjUzMyw4LjUzM3MzLjgxNCw4LjUzMyw4LjUzMyw4LjUzM2g2OC4yNjdjNC43MTksMCw4LjUzMy0zLjgyMyw4LjUzMy04LjUzMyAgICAgcy0zLjgxNC04LjUzMy04LjUzMy04LjUzM0g5My44Njd6IiBmaWxsPSIjMDAwMDAwIi8+CgkJCTxwYXRoIGQ9Ik0yMjEuODY3LDM4NGMwLTQuNzEtMy44MTQtOC41MzMtOC41MzMtOC41MzNoLTE1My42Yy00LjcxOSwwLTguNTMzLDMuODIzLTguNTMzLDguNTMzczMuODE0LDguNTMzLDguNTMzLDguNTMzaDE1My42ICAgICBDMjE4LjA1MiwzOTIuNTMzLDIyMS44NjcsMzg4LjcxLDIyMS44NjcsMzg0eiIgZmlsbD0iIzAwMDAwMCIvPgoJCQk8cGF0aCBkPSJNMTg3LjczMywzMzIuOGMwLDQuNzEsMy44MTQsOC41MzMsOC41MzMsOC41MzNIMzg0YzQuNzE5LDAsOC41MzMtMy44MjMsOC41MzMtOC41MzNzLTMuODE0LTguNTMzLTguNTMzLTguNTMzSDE5Ni4yNjcgICAgIEMxOTEuNTQ4LDMyNC4yNjcsMTg3LjczMywzMjguMDksMTg3LjczMywzMzIuOHoiIGZpbGw9IiMwMDAwMDAiLz4KCQk8L2c+Cgk8L2c+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPGc+CjwvZz4KPC9zdmc+Cg==" />
</div>
</div>
<div class="temperature-text__container">
<span class="temperature__text">{{ currentTemp }}</span>
<span class="temperature-metric__text">°</span>
<span class="weather-condition__text">{{ condition }}</span>
<span></span>
</div>
<section class="min-max__container">
<div class="min__container">
<svg class="min-arrow__icon" viewBox="188.5 807 21 21">
<path fill="#00ff9b" d="M209.5 817.5h-21L199 828z" data-name="Min Arrow" />
</svg>
<span class="min-temperature__text">{{ minTemp }}</span>
<span class="min__text">Min</span>
</div>
<div class="max__container">
<svg class="max-arrow__icon" viewBox="449.5 820 21 21">
<path fill="red" d="M449.5 830.5h21L460 820z" data-name="Max Arrow" />
</svg>
<span class="max-temperature__text">{{ maxTemp }}</span>
<span class="max__text">Max</span>
</div>
</section>
</router-link>
</template>
<script>
import axios from "axios";
export default {
props: ["city"],
data() {
return {
currentTemp: "",
condition: "",
minTemp: "",
maxTemp: "",
openWeatherApiKey: ""
};
},
methods: {
getCurrentConditionByName: function() {
let vm = this;
let url = `http://api.openweathermap.org/data/2.5/weather?q=${
this.city
}&appid=${this.openWeatherApiKey}`;
axios
.get(url)
.then(response => {
vm.setDataWeather(response.data);
})
.catch(response => {
console.log(response);
});
},
setDataWeather: function(data) {
this.currentTemp = parseInt(data.main.temp);
this.condition = data.weather[0].main;
this.minTemp = parseInt(data.main.temp_min);
this.maxTemp = parseInt(data.main.temp_max);
}
},
created() {
this.getCurrentConditionByName();
}
};
</script>
<style scoped>
.weather__card-dark {
background: linear-gradient(to bottom, #711b86, #00057a);
color: white;
}
.city-name__text {
text-transform: uppercase;
font-size: 1.4rem;
letter-spacing: 0.1rem;
margin-bottom: 1rem;
}
.temperature__text {
align-self: end;
width: 100%;
font-size: 4rem;
font-weight: 100;
letter-spacing: 0.1rem;
}
.temperature-metric__text {
text-align: start;
font-size: 3rem;
}
.min-max__container {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: 1fr 1fr;
align-items: center;
}
.min__container,
.max__container {
margin: 1rem 3rem;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
}
.min-arrow__icon,
.max-arrow__icon {
height: 1.25rem;
margin: auto;
}
.max-arrow__icon {
margin-bottom: -0.05rem;
}
.weather-condition__text {
display: block;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 0.1rem;
text-align: center;
}
.max__text {
color: #ff0070;
}
.min__text {
color: #00ff9b;
}
.max__text,
.min__text {
font-size: 1rem;
text-align: center;
}
.max-temperature__text,
.min-temperature__text {
text-align: center;
font-size: 2rem;
}
.weather-icon__container {
width: 10rem;
margin-bottom: 2rem;
display: flex;
justify-content: center;
}
.weather-icon__container > div > img {
max-width: 100%;
}
</style>
Tại đây bạn điển key mà bạn lấy từ open weather để vào openWeatherApiKey và cài thêm axios bằng câu lệnh sau:
npm install --save axios
Trong Main Component bạn sửa lại như sau :
<template>
<div class="container_wrap">
<weather-card v-for="city in listCity" :city='city' :key="city.id" />
<add-weather-card/>
</div>
</template>
<script>
import AddWeatherCard from "@/components/AddWeatherCard";
import WeatherCard from "@/components/WeatherCard";
export default {
components: {
AddWeatherCard,
WeatherCard
},
data() {
return {
listCity: ["hanoi"]
};
}
};
</script>
Chúng ta có thể hiểu luông hoạt động một cách đơn gian như sau. Main sẽ chứa danh sách các thành phố, và một thành phố ta gọi 1 component WeatherCard mà được truyền dữ liệu tên thành phố qua props. Props là cơ chế giúp ta truyền data từ component cha tới component con. Và trong component con, WeatherCard lấy dữ liệu đó và sử dụng axios (tương tự ajax) gọi api để hiển thị.
Và ta đa, ta đã có sản phẩm như này:
Tổng kết
Chúng ta đã có những bước chân để xây dựng một ứng dụng VueJs của mình. Bạn có thể tham khảo repo của mình https://github.com/quanKM/weather-app để có thể tự mở rộng ứng dụng của riêng mình.
Tham Khảo
All rights reserved