release: v1
This commit is contained in:
parent
2598cdc3a9
commit
b010d69b99
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,4 +2,6 @@ node_modules
|
|||||||
target
|
target
|
||||||
dist
|
dist
|
||||||
.env
|
.env
|
||||||
|
.env*
|
||||||
|
!.env.example
|
||||||
.vite-ssg-temp
|
.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
1
src/components.d.ts
vendored
@ -56,6 +56,7 @@ declare module 'vue' {
|
|||||||
SelectSeparator: typeof import('./components/ui/select/SelectSeparator.vue')['default']
|
SelectSeparator: typeof import('./components/ui/select/SelectSeparator.vue')['default']
|
||||||
SelectTrigger: typeof import('./components/ui/select/SelectTrigger.vue')['default']
|
SelectTrigger: typeof import('./components/ui/select/SelectTrigger.vue')['default']
|
||||||
SelectValue: typeof import('./components/ui/select/SelectValue.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']
|
Table: typeof import('./components/ui/table/Table.vue')['default']
|
||||||
TableBody: typeof import('./components/ui/table/TableBody.vue')['default']
|
TableBody: typeof import('./components/ui/table/TableBody.vue')['default']
|
||||||
TableCaption: typeof import('./components/ui/table/TableCaption.vue')['default']
|
TableCaption: typeof import('./components/ui/table/TableCaption.vue')['default']
|
||||||
|
|||||||
@ -7,11 +7,11 @@
|
|||||||
import { useOptions } from '@/composables/dateOpt'
|
import { useOptions } from '@/composables/dateOpt'
|
||||||
import '@amap/amap-jsapi-types'
|
import '@amap/amap-jsapi-types'
|
||||||
|
|
||||||
const amapInstance = ref<null | typeof AMap>(null)
|
let amapInstance: null | typeof AMap = null
|
||||||
const map = ref<null | AMap.Map>(null)
|
let map: null | AMap.Map = null
|
||||||
const previousLayer = ref<null | AMap.ImageLayer>(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]) {
|
function getPostionText(position: [number, number]) {
|
||||||
const [x, y] = position
|
const [x, y] = position
|
||||||
@ -33,7 +33,7 @@ const apiKey = import.meta.env.VITE_AMAP_API_KEY
|
|||||||
const { options, currentFocus, getImageUrl, getHeatPoints } = useOptions()
|
const { options, currentFocus, getImageUrl, getHeatPoints } = useOptions()
|
||||||
|
|
||||||
watch(options, async () => {
|
watch(options, async () => {
|
||||||
if (!map.value || !amapInstance.value) {
|
if (!map || !amapInstance) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (options.castBeginDate === '' || options.castDayNo === -1) {
|
if (options.castBeginDate === '' || options.castDayNo === -1) {
|
||||||
@ -56,24 +56,26 @@ watch(currentFocus, (newFocus) => {
|
|||||||
}
|
}
|
||||||
const [lat, lon] = newFocus()
|
const [lat, lon] = newFocus()
|
||||||
// debugger
|
// debugger
|
||||||
map.value?.setCenter([lat, lon])
|
if (map) {
|
||||||
map.value?.setZoom(4)
|
map.setCenter([lat, lon])
|
||||||
|
map.setZoom(4)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const isDark = useDark()
|
const isDark = useDark()
|
||||||
|
|
||||||
watch(isDark, (newValue) => {
|
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() {
|
async function updateMapImage() {
|
||||||
if (!map.value || !amapInstance.value) {
|
if (!map || !amapInstance) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = await getImageUrl()
|
const url = await getImageUrl()
|
||||||
|
|
||||||
const image = new amapInstance.value.ImageLayer({
|
const image = new amapInstance.ImageLayer({
|
||||||
url,
|
url,
|
||||||
// @ts-expect-error AMap messed up their definition
|
// @ts-expect-error AMap messed up their definition
|
||||||
bounds: new AMap.Bounds([-180, -35], [180, 35]),
|
bounds: new AMap.Bounds([-180, -35], [180, 35]),
|
||||||
@ -82,36 +84,38 @@ async function updateMapImage() {
|
|||||||
opacity: 1,
|
opacity: 1,
|
||||||
// zooms: [15, 20],
|
// zooms: [15, 20],
|
||||||
})
|
})
|
||||||
map.value.add(image)
|
map.add(image)
|
||||||
image.on('complete', () => {
|
image.on('complete', () => {
|
||||||
previousLayer.value?.hide()
|
previousLayer?.hide()
|
||||||
previousLayer.value = image
|
previousLayer = image
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateMapHeatpoints() {
|
async function updateMapHeatpoints() {
|
||||||
if (!map.value || !amapInstance.value) {
|
if (!map || !amapInstance) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
previousLayer?.hide()
|
||||||
const data = await getHeatPoints()
|
const data = await getHeatPoints()
|
||||||
|
|
||||||
let heatMap: any
|
let heatMap: any
|
||||||
const _map = map.value
|
|
||||||
// @ts-expect-error Amap not providing heatmap types
|
// @ts-expect-error Amap not providing heatmap types
|
||||||
_map.plugin(['AMap.Heatmap'], () => {
|
map.plugin(['AMap.HeatMap'], () => {
|
||||||
// @ts-expect-error Amap not providing heatmap types
|
// @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],
|
opacity: [0, 0.8],
|
||||||
|
gradient: {
|
||||||
|
0.0: 'red',
|
||||||
|
1.0: 'red',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
heatMap.setDataSet({
|
heatMap.setDataSet({
|
||||||
data,
|
data,
|
||||||
max: 1000,
|
max: 1000,
|
||||||
})
|
})
|
||||||
heatMap.show()
|
previousLayer = heatMap
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,9 +127,9 @@ onMounted(async () => {
|
|||||||
version: '2.0',
|
version: '2.0',
|
||||||
plugins: [],
|
plugins: [],
|
||||||
})
|
})
|
||||||
amapInstance.value = _AMap
|
amapInstance = _AMap
|
||||||
|
|
||||||
map.value = new _AMap.Map('amap-container', {
|
map = new _AMap.Map('amap-container', {
|
||||||
zoom: 5,
|
zoom: 5,
|
||||||
center: [130, 17],
|
center: [130, 17],
|
||||||
zooms: [4, 20],
|
zooms: [4, 20],
|
||||||
@ -136,13 +140,13 @@ onMounted(async () => {
|
|||||||
// ],
|
// ],
|
||||||
})
|
})
|
||||||
|
|
||||||
map.value.setBounds(new AMap.Bounds([-180, -35], [180, 35]))
|
map.setBounds(new AMap.Bounds([-180, -35], [180, 35]))
|
||||||
map.value.setLimitBounds(new AMap.Bounds([-180, -35], [180, 35]))
|
map.setLimitBounds(new AMap.Bounds([-180, -35], [180, 35]))
|
||||||
map.value.setZoom(5)
|
map.setZoom(5)
|
||||||
|
|
||||||
map.value.on('click', (e) => {
|
map.on('click', (e) => {
|
||||||
const position = [e.lnglat.lng, e.lnglat.lat] as [number, number]
|
const position = [e.lnglat.lng, e.lnglat.lat] as [number, number]
|
||||||
const infoWindow = new amapInstance.value!.InfoWindow({
|
const infoWindow = new amapInstance!.InfoWindow({
|
||||||
content: `
|
content: `
|
||||||
<div class="p-2 text-black">
|
<div class="p-2 text-black">
|
||||||
${getPostionText(position).getX()}
|
${getPostionText(position).getX()}
|
||||||
@ -151,7 +155,7 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
infoWindow.open(map.value!, e.lnglat)
|
infoWindow.open(map!, e.lnglat)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -42,8 +42,8 @@ function getPostionText(position: [number, number]) {
|
|||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<TableRow v-for="postion in currentTcLoc" :key="postion[0]">
|
<TableRow v-for="postion in currentTcLoc" :key="postion[0]">
|
||||||
<TableCell>{{ getPostionText(postion).getX() }}</TableCell>
|
|
||||||
<TableCell>{{ getPostionText(postion).getY() }}</TableCell>
|
<TableCell>{{ getPostionText(postion).getY() }}</TableCell>
|
||||||
|
<TableCell>{{ getPostionText(postion).getX() }}</TableCell>
|
||||||
<TableCell class="text-right">
|
<TableCell class="text-right">
|
||||||
<Button
|
<Button
|
||||||
class="h-6 text-[13.2px]" variant="outline"
|
class="h-6 text-[13.2px]" variant="outline"
|
||||||
|
|||||||
39
src/components/ui/switch/Switch.vue
Normal file
39
src/components/ui/switch/Switch.vue
Normal 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>
|
||||||
1
src/components/ui/switch/index.ts
Normal file
1
src/components/ui/switch/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Switch } from './Switch.vue'
|
||||||
@ -1,4 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
// import Switch from '@/components/ui/switch/Switch.vue'
|
||||||
import { DrawerClose, DrawerTrigger } from 'vaul-vue'
|
import { DrawerClose, DrawerTrigger } from 'vaul-vue'
|
||||||
import Colorbar from '../assets/colorbar.png'
|
import Colorbar from '../assets/colorbar.png'
|
||||||
|
|
||||||
@ -24,7 +25,14 @@ function flipDark() {
|
|||||||
季节内台风预报系统
|
季节内台风预报系统
|
||||||
</h1>
|
</h1>
|
||||||
<div class="size-5">
|
<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>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main
|
<main
|
||||||
@ -41,8 +49,8 @@ function flipDark() {
|
|||||||
地图
|
地图
|
||||||
</Badge>
|
</Badge>
|
||||||
<Card v-if="options.castDayNo !== -1" class="absolute right-3 top-3 z-400 p-3">
|
<Card v-if="options.castDayNo !== -1" class="absolute right-3 top-3 z-400 p-3">
|
||||||
当前发布日期是: <br>{{ castBeginDate }}<br>
|
发布日期: <br>{{ castBeginDate }}<br>
|
||||||
当前目标预报日期是:<br> {{ castTargetDate }}
|
预报日期:<br> {{ castTargetDate }}
|
||||||
</Card>
|
</Card>
|
||||||
<div class="flex-1" />
|
<div class="flex-1" />
|
||||||
<RouterView />
|
<RouterView />
|
||||||
@ -65,7 +73,7 @@ function flipDark() {
|
|||||||
{{ currentTcLoc.length }} 个区域预报出台风
|
{{ currentTcLoc.length }} 个区域预报出台风
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div>
|
<div v-if="options.renderMode === 'image'">
|
||||||
<img :src="Colorbar" mx-auto max-h="60px">
|
<img :src="Colorbar" mx-auto max-h="60px">
|
||||||
</div>
|
</div>
|
||||||
<div grid="~ cols-2 gap-4">
|
<div grid="~ cols-2 gap-4">
|
||||||
@ -104,7 +112,7 @@ function flipDark() {
|
|||||||
<DrawerHeader>
|
<DrawerHeader>
|
||||||
<DrawerTitle>地图设置</DrawerTitle>
|
<DrawerTitle>地图设置</DrawerTitle>
|
||||||
<DrawerDescription>
|
<DrawerDescription>
|
||||||
选择日期和渲染方式
|
选择日期和展示方式
|
||||||
</DrawerDescription>
|
</DrawerDescription>
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
<OptForm />
|
<OptForm />
|
||||||
@ -129,7 +137,7 @@ function flipDark() {
|
|||||||
<span i-mdi-play />
|
<span i-mdi-play />
|
||||||
{{
|
{{
|
||||||
options.castDayNo === -1 ? "选择一个日期后可以自动播放"
|
options.castDayNo === -1 ? "选择一个日期后可以自动播放"
|
||||||
: "开始自动播放"
|
: "自动播放"
|
||||||
}}
|
}}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@ -139,7 +147,7 @@ function flipDark() {
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<span i-material-symbols-pause />
|
<span i-material-symbols-pause />
|
||||||
暂停自动播放
|
暂停播放
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user