xyyd-fatfox/src/widgets/weather/Modal.tsx

334 lines
13 KiB
TypeScript
Raw Normal View History

2024-10-15 16:25:01 +08:00
import { Cascader, message } from 'ant-design-vue'
import { computed, defineComponent, onMounted, ref, type HTMLAttributes, type VNodeRef } from 'vue'
import useWeatherStore from './useWeatherStore'
import type { DefaultOptionType } from 'ant-design-vue/es/select'
import { addIcons, OhVueIcon } from 'oh-vue-icons'
import clsx from 'clsx'
import { FaMinus, HiSolidLocationMarker } from 'oh-vue-icons/icons'
import WeatherIcon from './weatherIcon'
import dayjs from 'dayjs'
addIcons(FaMinus, HiSolidLocationMarker)
2024-09-29 15:17:52 +08:00
export default defineComponent(() => {
2024-10-15 16:25:01 +08:00
const store = useWeatherStore()
const hourRef = ref<VNodeRef | null>(null)
const cascaderTriiger = ref(true)
const searchValue = ref('')
const filter = (inputValue: string, path: DefaultOptionType[]) =>
path.some(
(option) => (option.label as string).toLowerCase().indexOf(inputValue.toLowerCase()) > -1
)
onMounted(() => {
store.queryAll()
})
const nowWeather = computed(() => {
if (store.state.selectedCityCode === -1) return store.weatherData
const idx = store.state.weatherList.findIndex((val) => val.id === store.state.selectedCityCode)
if (idx !== -1) {
return store.state.weatherList[idx].data
} else {
return store.weatherData
}
})
function getPracticeIndex(val: string) {
const numVal = parseFloat(val) || 0
if (numVal < 115) {
return '适宜晨练'
} else {
return '不宜晨练'
}
}
function pollution(val: string) {
const numVal = parseFloat(val) || 0
if (numVal < 35) {
return '优'
} else if (numVal < 75) {
return '良'
} else if (numVal < 115) {
return '轻度污染'
} else if (numVal < 150) {
return '中度污染'
} else if (numVal < 250) {
return '重度污染'
} else if (numVal >= 250) {
return '严重污染'
} else {
return '未知'
}
}
function shirtIndex(val: string) {
const numVal = parseFloat(val) || 0
if (numVal < 6) {
return '寒冷'
} else if (numVal < 10.9) {
return '冷'
} else if (numVal < 14.9) {
return '凉'
} else if (numVal < 17.9) {
return '温凉'
} else if (numVal < 20.9) {
return '凉舒适'
} else if (numVal < 23.9) {
return '舒适'
} else if (numVal < 27.9) {
return '热舒适'
} else if (numVal >= 28) {
return '炎热'
} else {
return '温凉'
}
}
function coldIndex(val: string) {
const numVal = parseFloat(val) || 0
if (numVal < 6) {
return '易发感冒'
} else if (numVal < 10.9) {
return '较易发感冒'
} else {
return '不易发感冒'
}
}
return () => (
<div
class="w-full h-full bg-red-50 flex "
style={{
background: 'linear-gradient(180deg,#dcefff 0%,#e7ecff 100%)'
}}
>
<div class={' w-[228px] px-6 pt-6 h-full bg-white/[.7] rounded-[20px]'}>
<div class={'relative '}>
{cascaderTriiger.value && (
<Cascader
class={'w-full shadow-sm rounded-lg border-[1px] '}
options={store.state.cityOptions}
onChange={async (e: any, option: DefaultOptionType[]) => {
cascaderTriiger.value = false
setTimeout(() => {
cascaderTriiger.value = true
}, 0)
if (store.state.weatherList.findIndex((val) => val.id === option[2].value) !== -1) {
message.info('该地区已添加')
return
}
if (store.state.weatherList.length >= 5) {
message.info('最多只能添加5个地区')
return
}
store.state.weatherList.push({
id: option[2].value as number,
name: option[2].label,
data: await store.query(option[2].value as number)
})
store.state.selectedCityCode = option[2].value as number
}}
notFoundContent="没有找到该地区"
onClick={() => {
if (store.state.cityOptions.length === 0) store.queryCity()
}}
searchValue={searchValue.value}
placeholder="搜索其他地区天气"
showSearch={{ filter }}
onSearch={(value) => {
searchValue.value = value
}}
/>
)}
</div>
<div class={'w-full flex flex-col gap-y-2 mt-4'}>
<div
onClick={() => {
store.state.selectedCityCode = -1
}}
class={clsx(
'flex flex-col justify-between gap-x-4 w-full h-[60px] relative py-2 px-3 rounded-lg cursor-pointer hover:bg-gradient-to-b from-[#d6ecff] to-[#dce3ff]',
store.state.selectedCityCode === -1
? 'bg-gradient-to-b from-[#d6ecff] to-[#dce3ff]'
: 'bg-black/[0.05]'
)}
>
<div class={'flex items-center justify-between gap-x-1 text-[#333]'}>
<div class={'flex items-center gap-x-1 flex-1'}>
<OhVueIcon name={HiSolidLocationMarker.name} fill="#333" scale={0.8}></OhVueIcon>
<span class={'text-[14px]'}>{store.weatherData?.city.name}</span>
</div>
<span class={'font-bold text-[16px]'}>{store.weatherData?.base.stemp}°</span>
</div>
<div class={'flex items-center justify-between gap-x-1 text-[#333]'}>
<div class={'flex items-center text-[12px] gap-x-1 '}>
<WeatherIcon weather={store.weatherData?.base.weather} size={16}></WeatherIcon>
<span>{store.weatherData?.base.weather}</span>
</div>
<span class={'text-[12px]'}>
{store.weatherData?.base.ltemp}°/{store.weatherData?.base.htemp}°
</span>
</div>
</div>
{store.state.weatherList.map((item) => (
<div
onClick={() => {
store.state.selectedCityCode = item.id
}}
class={clsx(
'flex flex-col group justify-between gap-x-4 w-full h-[65px] group relative py-2 px-3 rounded-lg cursor-pointer hover:bg-gradient-to-b from-[#d6ecff] to-[#dce3ff]',
store.state.selectedCityCode === item.id
? 'bg-gradient-to-b from-[#d6ecff] to-[#dce3ff]'
: 'bg-black/[0.05]'
)}
key={item.id}
>
<div class={'flex items-center justify-between gap-x-1 text-[#333]'}>
<div class={'flex items-center gap-x-1 flex-1'}>
<span class={'text-[14px]'}>{item.data?.city.name}</span>
</div>
<span class={'font-bold text-[16px]'}>{item.data?.base.stemp}°</span>
</div>
<div class={'flex items-center justify-between gap-x-1 text-[#333]'}>
<div class={'flex items-center text-[12px] gap-x-1 '}>
<WeatherIcon weather={item.data?.base.weather} size={16}></WeatherIcon>
<span>{item.data?.base.weather}</span>
</div>
<span class={'text-[12px]'}>
{item.data?.base.ltemp}°/{item.data?.base.htemp}°
</span>
</div>
<div
class={
'absolute hidden top-[-6px] z-10 right-[-6px] group-hover:flex bg-black/20 cursor-default rounded-full h-[20px] w-[20px] items-center justify-center'
}
onClick={(e) => {
e.stopPropagation()
store.state.weatherList = store.state.weatherList.filter(
(val) => val.id !== item.id
)
store.state.selectedCityCode = -1
}}
>
<div class={'w-[70%] h-[2px] bg-white/80 rounded-sm'}></div>
</div>
</div>
))}
</div>
</div>
<div class={'flex-1 w-0'}>
<div class={'w-full h-full px-7 py-4 flex flex-col gap-y-3'}>
<div class="flex">
<div class={'flex flex-col text-[#333]'}>
<span class="text-[#666]">{store.weatherData?.city.name}</span>
<span class={'text-[57px] font-bold'}>{nowWeather.value?.base.stemp}°</span>
<span class="text-[14px] flex gap-x-1">
{nowWeather.value?.base.weather}
<span>
{nowWeather.value?.base.ltemp}°/{nowWeather.value?.base.htemp}°
</span>
<WeatherIcon weather={nowWeather.value?.base.weather} size={20}></WeatherIcon>
</span>
<span class={'text-[14px] text-[#666]'}>
{nowWeather.value?.base.WD} {nowWeather.value?.base.WS}
</span>
</div>
<div class={'flex-1 flex items-center justify-center'}>
<div class={'grid grid-cols-3 grid-rows-2 text-[#666] text-[12px] gap-x-3 gap-y-2'}>
<div class="flex gap-x-1">
<span>:</span>
<span>{getPracticeIndex(nowWeather.value?.base.aqi || '0')}</span>
</div>
<div class="flex gap-x-1">
<span>:</span>
<span>{pollution(nowWeather.value?.base.pm25 || '0')}</span>
</div>
<div class="flex gap-x-1">
<span>穿:</span>
<span>{shirtIndex(nowWeather.value?.base.stemp || '0')}</span>
</div>
<div class="flex gap-x-1">
<span>:</span>
<span>
{nowWeather.value?.hour24.some((val) => val.weather.includes('雨'))
? '需带伞'
: '无需带伞'}
</span>
</div>
<div class="flex gap-x-1">
<span>:</span>
<span>{coldIndex(nowWeather.value?.base.stemp || '0')}</span>
</div>{' '}
<div class="flex gap-x-1">
<span>线:</span>
<span>线{nowWeather.value?.base.ultraviolet}</span>
</div>
</div>
</div>
</div>
<div class={'w-full bg-white/60 rounded-xl py-2 px-3 '}>
<span class={'text-[#999] text-[14px]'}>24</span>
<div
ref={hourRef}
class="flex w-full flex-grow-0 overflow-x-scroll gap-x-2 mt-1 scrollbar-hide"
onWheel={(e) => {
e.preventDefault()
if (!hourRef.value) return
hourRef.value.scrollLeft += e.deltaY
}}
>
{nowWeather.value?.hour24.map((item, idx) => (
<div
class={clsx(
'flex flex-col shrink-0 items-center w-[60px] text-[#333] rounded-lg py-4 justify-center gap-y-3',
idx === 0 ? 'bg-black/[.05]' : 'hover:bg-black/[0.05]'
)}
key={item.hour}
>
<WeatherIcon weather={item.weather} size={20}></WeatherIcon>
<span>{item.stemp}°</span>
<span>{idx === 0 ? '现在' : item.hour + '时'}</span>
</div>
))}
</div>
</div>
<div class={'flex-1 h-0 w-full'}>
<div class={' h-full w-full bg-slate-50/[.7] rounded-xl py-2 px-3 flex flex-col'}>
<span class={'text-[#999] text-[14px]'}>7</span>
<div class="w-full grid grid-cols-7 gap-x-2 grid-rows-1 flex-1 items-end pb-[6px] mt-1">
{nowWeather.value?.weathers7.map((item, idx) => {
return (
<div
class={clsx(
'flex flex-col items-center justify-between gap-y-[2px] text-[#333] text-[14px] h-full py-[14px] px-[3px] rounded-[15px] ',
idx === 0 ? 'bg-black/[0.05]' : 'hover:bg-black/[0.05]'
)}
>
<span class=" font-bold font-din ">
{item.ltemp + '°/' + item.htemp + '°'}
</span>
<div class={'flex flex-col gap-y-1 justify-center items-center'}>
<WeatherIcon size={20} weather={item.weather}></WeatherIcon>
<span class={'text-[14px]'}>{item.weather}</span>
</div>
<div class={'flex flex-col items-center justify-center text-[12px] '}>
<span class={'text-[#999]'}>
{dayjs(item.date, 'MM月DD日').format('MM/DD')}
</span>
<span class={'text-[14px]'}>
{idx === 0
? '今天'
: idx === 1
? '明天'
: dayjs(item.date, 'MM月DD日').format('ddd')}
</span>
</div>
</div>
)
})}
</div>
</div>
</div>
</div>
</div>
</div>
)
2024-09-29 15:17:52 +08:00
})