release: v1

This commit is contained in:
Dustella 2025-02-13 15:46:58 +08:00
parent 2598cdc3a9
commit b010d69b99
Signed by: Dustella
GPG Key ID: 35AA0AA3DC402D5C
8 changed files with 92 additions and 37 deletions

2
.gitignore vendored
View File

@ -2,4 +2,6 @@ node_modules
target
dist
.env
.env*
!.env.example
.vite-ssg-temp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

1
src/components.d.ts vendored
View File

@ -56,6 +56,7 @@ declare module 'vue' {
SelectSeparator: typeof import('./components/ui/select/SelectSeparator.vue')['default']
SelectTrigger: typeof import('./components/ui/select/SelectTrigger.vue')['default']
SelectValue: typeof import('./components/ui/select/SelectValue.vue')['default']
Switch: typeof import('./components/ui/switch/Switch.vue')['default']
Table: typeof import('./components/ui/table/Table.vue')['default']
TableBody: typeof import('./components/ui/table/TableBody.vue')['default']
TableCaption: typeof import('./components/ui/table/TableCaption.vue')['default']

View File

@ -7,11 +7,11 @@
import { useOptions } from '@/composables/dateOpt'
import '@amap/amap-jsapi-types'
const amapInstance = ref<null | typeof AMap>(null)
const map = ref<null | AMap.Map>(null)
const previousLayer = ref<null | AMap.ImageLayer>(null)
let amapInstance: null | typeof AMap = null
let map: null | AMap.Map = null
let previousLayer: null | AMap.ImageLayer = null
const FORCE_MODE_OVERRIDE = 'image' as 'image' | 'heatmap' | null
const FORCE_MODE_OVERRIDE = null as 'image' | 'heatmap' | null
function getPostionText(position: [number, number]) {
const [x, y] = position
@ -33,7 +33,7 @@ const apiKey = import.meta.env.VITE_AMAP_API_KEY
const { options, currentFocus, getImageUrl, getHeatPoints } = useOptions()
watch(options, async () => {
if (!map.value || !amapInstance.value) {
if (!map || !amapInstance) {
return
}
if (options.castBeginDate === '' || options.castDayNo === -1) {
@ -56,24 +56,26 @@ watch(currentFocus, (newFocus) => {
}
const [lat, lon] = newFocus()
// debugger
map.value?.setCenter([lat, lon])
map.value?.setZoom(4)
if (map) {
map.setCenter([lat, lon])
map.setZoom(4)
}
})
const isDark = useDark()
watch(isDark, (newValue) => {
map.value?.setMapStyle(newValue ? 'amap://styles/darkblue' : 'amap://styles/whitesmoke')
map?.setMapStyle(newValue ? 'amap://styles/darkblue' : 'amap://styles/whitesmoke')
})
async function updateMapImage() {
if (!map.value || !amapInstance.value) {
if (!map || !amapInstance) {
return
}
const url = await getImageUrl()
const image = new amapInstance.value.ImageLayer({
const image = new amapInstance.ImageLayer({
url,
// @ts-expect-error AMap messed up their definition
bounds: new AMap.Bounds([-180, -35], [180, 35]),
@ -82,36 +84,38 @@ async function updateMapImage() {
opacity: 1,
// zooms: [15, 20],
})
map.value.add(image)
map.add(image)
image.on('complete', () => {
previousLayer.value?.hide()
previousLayer.value = image
previousLayer?.hide()
previousLayer = image
})
}
async function updateMapHeatpoints() {
if (!map.value || !amapInstance.value) {
if (!map || !amapInstance) {
return
}
previousLayer?.hide()
const data = await getHeatPoints()
let heatMap: any
const _map = map.value
// @ts-expect-error Amap not providing heatmap types
_map.plugin(['AMap.Heatmap'], () => {
map.plugin(['AMap.HeatMap'], () => {
// @ts-expect-error Amap not providing heatmap types
heatMap = new amapInstance.value.HeatMap(_map, {
heatMap = new amapInstance.HeatMap(map, {
radius: 100, //
radius: 25, //
opacity: [0, 0.8],
gradient: {
0.0: 'red',
1.0: 'red',
},
})
heatMap.setDataSet({
data,
max: 1000,
})
heatMap.show()
previousLayer = heatMap
})
}
@ -123,9 +127,9 @@ onMounted(async () => {
version: '2.0',
plugins: [],
})
amapInstance.value = _AMap
amapInstance = _AMap
map.value = new _AMap.Map('amap-container', {
map = new _AMap.Map('amap-container', {
zoom: 5,
center: [130, 17],
zooms: [4, 20],
@ -136,13 +140,13 @@ onMounted(async () => {
// ],
})
map.value.setBounds(new AMap.Bounds([-180, -35], [180, 35]))
map.value.setLimitBounds(new AMap.Bounds([-180, -35], [180, 35]))
map.value.setZoom(5)
map.setBounds(new AMap.Bounds([-180, -35], [180, 35]))
map.setLimitBounds(new AMap.Bounds([-180, -35], [180, 35]))
map.setZoom(5)
map.value.on('click', (e) => {
map.on('click', (e) => {
const position = [e.lnglat.lng, e.lnglat.lat] as [number, number]
const infoWindow = new amapInstance.value!.InfoWindow({
const infoWindow = new amapInstance!.InfoWindow({
content: `
<div class="p-2 text-black">
${getPostionText(position).getX()}
@ -151,7 +155,7 @@ onMounted(async () => {
</div>
`,
})
infoWindow.open(map.value!, e.lnglat)
infoWindow.open(map!, e.lnglat)
})
}
})

View File

@ -42,8 +42,8 @@ function getPostionText(position: [number, number]) {
</TableHeader>
<TableBody>
<TableRow v-for="postion in currentTcLoc" :key="postion[0]">
<TableCell>{{ getPostionText(postion).getX() }}</TableCell>
<TableCell>{{ getPostionText(postion).getY() }}</TableCell>
<TableCell>{{ getPostionText(postion).getX() }}</TableCell>
<TableCell class="text-right">
<Button
class="h-6 text-[13.2px]" variant="outline"

View File

@ -0,0 +1,39 @@
<script setup lang="ts">
import { cn } from '@/lib/utils'
import {
SwitchRoot,
type SwitchRootEmits,
type SwitchRootProps,
SwitchThumb,
useForwardPropsEmits,
} from 'radix-vue'
import { computed, type HTMLAttributes } from 'vue'
const props = defineProps<SwitchRootProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<SwitchRootEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<SwitchRoot
v-bind="forwarded"
:class="cn(
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
props.class,
)"
>
<SwitchThumb
:class="cn('pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5')"
>
<slot name="thumb" />
</SwitchThumb>
</SwitchRoot>
</template>

View File

@ -0,0 +1 @@
export { default as Switch } from './Switch.vue'

View File

@ -1,4 +1,5 @@
<script setup lang="ts">
// import Switch from '@/components/ui/switch/Switch.vue'
import { DrawerClose, DrawerTrigger } from 'vaul-vue'
import Colorbar from '../assets/colorbar.png'
@ -24,7 +25,14 @@ function flipDark() {
季节内台风预报系统
</h1>
<div class="size-5">
<span class="i-tabler-sun size-5" @click="flipDark" />
<!-- <Switch :checked="isDark" @update:checked="flipDark">
<template #thumb>
<div class="h-5 w-5 flex items-center justify-center">
<div v-if="isDark" class="i-lucide-moon size-3" />
<div v-else class="i-lucide-sun size-3" />
</div>
</template>
</Switch> -->
</div>
</header>
<main
@ -41,8 +49,8 @@ function flipDark() {
地图
</Badge>
<Card v-if="options.castDayNo !== -1" class="absolute right-3 top-3 z-400 p-3">
当前发布日期 <br>{{ castBeginDate }}<br>
当前目标预报日期<br> {{ castTargetDate }}
发布日期 <br>{{ castBeginDate }}<br>
预报日期<br> {{ castTargetDate }}
</Card>
<div class="flex-1" />
<RouterView />
@ -65,7 +73,7 @@ function flipDark() {
{{ currentTcLoc.length }} 个区域预报出台风
</div>
</div> -->
<div>
<div v-if="options.renderMode === 'image'">
<img :src="Colorbar" mx-auto max-h="60px">
</div>
<div grid="~ cols-2 gap-4">
@ -104,7 +112,7 @@ function flipDark() {
<DrawerHeader>
<DrawerTitle>地图设置</DrawerTitle>
<DrawerDescription>
选择日期和渲染方式
选择日期和展示方式
</DrawerDescription>
</DrawerHeader>
<OptForm />
@ -129,7 +137,7 @@ function flipDark() {
<span i-mdi-play />
{{
options.castDayNo === -1 ? "选择一个日期后可以自动播放"
: "开始自动播放"
: "自动播放"
}}
</Button>
<Button
@ -139,7 +147,7 @@ function flipDark() {
}"
>
<span i-material-symbols-pause />
暂停自动播放
暂停播放
</Button>
</div>
</Card>