staging: v1
This commit is contained in:
parent
5f2483c511
commit
383909fe89
9
components.d.ts
vendored
9
components.d.ts
vendored
@ -7,12 +7,17 @@ export {}
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Button: typeof import('./src/components/ui/button/Button.vue')['default']
|
||||
Image: typeof import('./src/components/Image.vue')['default']
|
||||
Label: typeof import('./src/components/ui/label/Label.vue')['default']
|
||||
Loading: typeof import('./src/components/Loading.vue')['default']
|
||||
NavigationMenu: typeof import('./src/components/ui/navigation-menu/NavigationMenu.vue')['default']
|
||||
NavigationMenuContent: typeof import('./src/components/ui/navigation-menu/NavigationMenuContent.vue')['default']
|
||||
NavigationMenuIndicator: typeof import('./src/components/ui/navigation-menu/NavigationMenuIndicator.vue')['default']
|
||||
NavigationMenuItem: typeof import('./src/components/ui/navigation-menu/NavigationMenuItem.vue')['default']
|
||||
NavigationMenuLink: typeof import('./src/components/ui/navigation-menu/NavigationMenuLink.vue')['default']
|
||||
NavigationMenuList: typeof import('./src/components/ui/navigation-menu/NavigationMenuList.vue')['default']
|
||||
NavigationMenuListItem: typeof import('./src/components/NavigationMenuListItem.vue')['default']
|
||||
NavigationMenuTrigger: typeof import('./src/components/ui/navigation-menu/NavigationMenuTrigger.vue')['default']
|
||||
NavigationMenuViewport: typeof import('./src/components/ui/navigation-menu/NavigationMenuViewport.vue')['default']
|
||||
NumberField: typeof import('./src/components/ui/number-field/NumberField.vue')['default']
|
||||
@ -33,9 +38,9 @@ declare module 'vue' {
|
||||
SelectSeparator: typeof import('./src/components/ui/select/SelectSeparator.vue')['default']
|
||||
SelectTrigger: typeof import('./src/components/ui/select/SelectTrigger.vue')['default']
|
||||
SelectValue: typeof import('./src/components/ui/select/SelectValue.vue')['default']
|
||||
TheCounter: typeof import('./src/components/TheCounter.vue')['default']
|
||||
Skeleton: typeof import('./src/components/ui/skeleton/Skeleton.vue')['default']
|
||||
TestHeader: typeof import('./src/components/TestHeader.vue')['default']
|
||||
TheFooter: typeof import('./src/components/TheFooter.vue')['default']
|
||||
TheHeader: typeof import('./src/components/TheHeader.vue')['default']
|
||||
TheInput: typeof import('./src/components/TheInput.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
7
cspell.config.yaml
Normal file
7
cspell.config.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
version: '0.2'
|
||||
ignorePaths: []
|
||||
dictionaryDefinitions: []
|
||||
dictionaries: []
|
||||
words: []
|
||||
ignoreWords: []
|
||||
import: []
|
||||
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<title>Vitesse Lite</title>
|
||||
<title>中高层大气波动解析识别技术系统</title>
|
||||
<meta name="description" content="Opinionated Vite Starter Template" />
|
||||
</head>
|
||||
<body class="font-sans dark:text-white dark:bg-hex-121212">
|
||||
|
||||
BIN
public/pack.png
Normal file
BIN
public/pack.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 249 KiB |
@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div class="w-full flex items-center justify-center">
|
||||
<div class="max-w-[980px]">
|
||||
<div class="relative w-full flex-col items-center justify-center">
|
||||
<div p-4 text-center text-2xl font-bold>
|
||||
中高层大气波动解析识别技术系统
|
||||
</div>
|
||||
<TheHeader />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1 +1 @@
|
||||
export const API_BASE_URL = 'http://localhost:5000'
|
||||
export const API_BASE_URL = 'https://gca-api.dustella.net:8443'
|
||||
|
||||
27
src/components/Image.vue
Normal file
27
src/components/Image.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
imageUrl: string
|
||||
}>()
|
||||
|
||||
function download() {
|
||||
const a = document.createElement('a')
|
||||
a.href = props.imageUrl
|
||||
a.download = 'image.png'
|
||||
a.click()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<img id="image_" :src="imageUrl">
|
||||
<div my-2 w-full>
|
||||
<Button class="w-2/3" @click="download">
|
||||
下载
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
13
src/components/Loading.vue
Normal file
13
src/components/Loading.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div h-full w-full flex="~ col items-center justify-center">
|
||||
<Skeleton class="h-50 w-3/5" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
25
src/components/NavigationMenuListItem.vue
Normal file
25
src/components/NavigationMenuListItem.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { NavigationMenuLink } from 'radix-vue'
|
||||
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li>
|
||||
<NavigationMenuLink as-child>
|
||||
<a
|
||||
v-bind="$attrs"
|
||||
class="hover:bg-mauve3 block select-none rounded-[6px] p-3 text-[15px] leading-none no-underline outline-none transition-colors focus:shadow-[0_0_0_2px] focus:shadow-green7"
|
||||
>
|
||||
<div class="text-green12 mb-[5px] font-medium leading-[1.2]">
|
||||
{{ props.title }}
|
||||
</div>
|
||||
<p class="text-mauve11 my-0 leading-[1.4]">
|
||||
<slot />
|
||||
</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
</template>
|
||||
150
src/components/TestHeader.vue
Normal file
150
src/components/TestHeader.vue
Normal file
@ -0,0 +1,150 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
NavigationMenuContent,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuList,
|
||||
NavigationMenuRoot,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuViewport,
|
||||
} from 'radix-vue'
|
||||
import { ref } from 'vue'
|
||||
import NavigationMenuListItem from './NavigationMenuListItem.vue'
|
||||
|
||||
const currentTrigger = ref('')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NavigationMenuRoot
|
||||
v-model="currentTrigger"
|
||||
class="relative z-[1] mx-auto flex justify-center"
|
||||
>
|
||||
<NavigationMenuList class="center shadow-blackA7 m-0 flex list-none rounded-[6px] bg-white p-1 shadow-[0_2px_10px]">
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger
|
||||
class="group text-grass11 flex select-none items-center justify-between gap-[2px] rounded-[4px] px-3 py-2 text-[15px] font-medium leading-none outline-none hover:bg-green3 focus:shadow-[0_0_0_2px] focus:shadow-green7"
|
||||
>
|
||||
Learn
|
||||
<Icon
|
||||
icon="radix-icons:caret-down"
|
||||
class="text-green10 relative top-[1px] transition-transform duration-[250ms] ease-in group-data-[state=open]:-rotate-180"
|
||||
/>
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent
|
||||
class="data-[motion=from-start]:animate-enterFromLeft data-[motion=from-end]:animate-enterFromRight data-[motion=to-start]:animate-exitToLeft data-[motion=to-end]:animate-exitToRight absolute left-0 top-0 w-full sm:w-auto"
|
||||
>
|
||||
<ul class="one grid m-0 list-none gap-x-[10px] p-[22px] sm:grid-cols-[0.75fr_1fr] sm:w-[500px]">
|
||||
<li class="grid row-span-3">
|
||||
<NavigationMenuLink as-child>
|
||||
<a
|
||||
class="h-full w-full flex flex-col select-none justify-end rounded-[6px] from-green9 to-teal9 bg-gradient-to-b p-[25px] no-underline outline-none focus:shadow-[0_0_0_2px] focus:shadow-green7"
|
||||
href="/"
|
||||
>
|
||||
<img
|
||||
class="w-16"
|
||||
src="https://www.radix-vue.com/logo.svg"
|
||||
>
|
||||
<div class="mb-[7px] mt-4 text-[18px] text-white font-medium leading-[1.2]">Radix Primitives</div>
|
||||
<p class="text-mauve4 text-[14px] leading-[1.3]">Unstyled, accessible components for Vue.</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
|
||||
<NavigationMenuListItem
|
||||
href="https://stitches.dev/"
|
||||
title="Stitches"
|
||||
>
|
||||
CSS-in-JS with best-in-class developer experience.
|
||||
</NavigationMenuListItem>
|
||||
<NavigationMenuListItem
|
||||
href="/colors"
|
||||
title="Colors"
|
||||
>
|
||||
Beautiful, thought-out palettes with auto dark mode.
|
||||
</NavigationMenuListItem>
|
||||
<NavigationMenuListItem
|
||||
href="https://icons.radix-ui.com/"
|
||||
title="Icons"
|
||||
>
|
||||
A crisp set of 15x15 icons, balanced and consistent.
|
||||
</NavigationMenuListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger
|
||||
class="text-grass11 group flex select-none items-center justify-between gap-[2px] rounded-[4px] px-3 py-2 text-[15px] font-medium leading-none outline-none hover:bg-green3 focus:shadow-[0_0_0_2px] focus:shadow-green7"
|
||||
>
|
||||
Overview
|
||||
<Icon
|
||||
icon="radix-icons:caret-down"
|
||||
class="text-green10 relative top-[1px] transition-transform duration-[250ms] ease-in group-data-[state=open]:-rotate-180"
|
||||
/>
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent class="data-[motion=from-start]:animate-enterFromLeft data-[motion=from-end]:animate-enterFromRight data-[motion=to-start]:animate-exitToLeft data-[motion=to-end]:animate-exitToRight absolute left-0 top-0 w-full sm:w-auto">
|
||||
<ul class="grid m-0 list-none gap-x-[10px] p-[22px] sm:grid-flow-col sm:grid-rows-3 sm:w-[600px]">
|
||||
<NavigationMenuListItem
|
||||
title="Introduction"
|
||||
href="/docs/primitives/overview/introduction"
|
||||
>
|
||||
Build high-quality, accessible design systems and web apps.
|
||||
</NavigationMenuListItem>
|
||||
<NavigationMenuListItem
|
||||
title="Getting started"
|
||||
href="/docs/primitives/overview/getting-started"
|
||||
>
|
||||
A quick tutorial to get you up and running with Radix Primitives.
|
||||
</NavigationMenuListItem>
|
||||
<NavigationMenuListItem
|
||||
title="Styling"
|
||||
href="/docs/primitives/guides/styling"
|
||||
>
|
||||
Unstyled and compatible with any styling solution.
|
||||
</NavigationMenuListItem>
|
||||
<NavigationMenuListItem
|
||||
title="Animation"
|
||||
href="/docs/primitives/guides/animation"
|
||||
>
|
||||
Use CSS keyframes or any animation library of your choice.
|
||||
</NavigationMenuListItem>
|
||||
<NavigationMenuListItem
|
||||
title="Accessibility"
|
||||
href="/docs/primitives/overview/accessibility"
|
||||
>
|
||||
Tested in a range of browsers and assistive technologies.
|
||||
</NavigationMenuListItem>
|
||||
<NavigationMenuListItem
|
||||
title="Releases"
|
||||
href="/docs/primitives/overview/releases"
|
||||
>
|
||||
Radix Primitives releases and their changelogs.
|
||||
</NavigationMenuListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink
|
||||
class="text-grass11 block select-none rounded-[4px] px-3 py-2 text-[15px] font-medium leading-none no-underline outline-none hover:bg-green3 focus:shadow-[0_0_0_2px] focus:shadow-green7"
|
||||
href="https://github.com/unovue/radix-vue"
|
||||
>
|
||||
Github
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuIndicator
|
||||
class="transition-[all,transform_250ms_ease] data-[state=visible]:animate-fadeIn data-[state=hidden]:animate-fadeOut top-full z-[1] h-[10px] flex items-end justify-center overflow-hidden duration-200 data-[state=hidden]:opacity-0"
|
||||
>
|
||||
<div class="relative top-[70%] h-[10px] w-[10px] rotate-[45deg] rounded-tl-[2px] bg-white" />
|
||||
</NavigationMenuIndicator>
|
||||
</NavigationMenuList>
|
||||
|
||||
<div class="absolute left-0 top-full w-full flex perspective-[2000px] justify-center">
|
||||
<NavigationMenuViewport
|
||||
class="transition-[width,_height] data-[state=open]:animate-scaleIn data-[state=closed]:animate-scaleOut relative mt-[10px] h-[var(--radix-navigation-menu-viewport-height)] w-full origin-[top_center] overflow-hidden rounded-[10px] bg-white duration-300 sm:w-[var(--radix-navigation-menu-viewport-width)]"
|
||||
/>
|
||||
</div>
|
||||
</NavigationMenuRoot>
|
||||
</template>
|
||||
@ -9,131 +9,70 @@ import {
|
||||
navigationMenuTriggerStyle,
|
||||
} from './ui/navigation-menu'
|
||||
|
||||
/**
|
||||
* comboType = [
|
||||
"探空气球",
|
||||
"流星雷达",
|
||||
"Saber",
|
||||
"TIDI",
|
||||
"COSMIC",
|
||||
]
|
||||
|
||||
comboMode = [
|
||||
["重力波单次", "重力波统计"],
|
||||
["重力波月统计", "潮汐波单次", "潮汐波月统计"],
|
||||
["行星波月统计", "重力波单次", "重力波月统计"],
|
||||
["行星波月统计"],
|
||||
["行星波月统计"],
|
||||
]
|
||||
*/
|
||||
const components: { title: string, href: string, description: string }[] = [
|
||||
{
|
||||
title: '探空气球',
|
||||
href: '/ballon',
|
||||
description:
|
||||
'探空气球',
|
||||
},
|
||||
{
|
||||
title: 'Hover Card',
|
||||
href: '/docs/components/hover-card',
|
||||
description:
|
||||
'For sighted users to preview content available behind a link.',
|
||||
},
|
||||
{
|
||||
title: 'Progress',
|
||||
href: '/docs/components/progress',
|
||||
description:
|
||||
'Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.',
|
||||
},
|
||||
{
|
||||
title: 'Scroll-area',
|
||||
href: '/docs/components/scroll-area',
|
||||
description: 'Visually or semantically separates content.',
|
||||
},
|
||||
{
|
||||
title: 'Tabs',
|
||||
href: '/docs/components/tabs',
|
||||
description:
|
||||
'A set of layered sections of content—known as tab panels—that are displayed one at a time.',
|
||||
},
|
||||
{
|
||||
title: 'Tooltip',
|
||||
href: '/docs/components/tooltip',
|
||||
description:
|
||||
'A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.',
|
||||
},
|
||||
]
|
||||
const header_data: Record<string, { title: string, href: string }[]> = {
|
||||
探空气球: [{
|
||||
title: '重力波单次',
|
||||
href: '/balloon/single',
|
||||
}, {
|
||||
title: '重力波统计',
|
||||
href: '/balloon/year',
|
||||
}],
|
||||
流星雷达: [{
|
||||
title: '潮汐波强度',
|
||||
href: '/radar/v1',
|
||||
}, {
|
||||
title: '潮汐波时空变化',
|
||||
href: '/radar/v2',
|
||||
}],
|
||||
Saber: [
|
||||
{
|
||||
title: '波动拟合图',
|
||||
href: '/saber/plot_wave_fitting',
|
||||
},
|
||||
{
|
||||
title: '日数据傅里叶变换分析',
|
||||
href: '/saber/day_fft_ifft_plot',
|
||||
},
|
||||
{
|
||||
title: '日周期波动能量分析',
|
||||
href: '/saber/day_cycle_power_wave_plot',
|
||||
},
|
||||
{
|
||||
// month_power_wave_plot
|
||||
title: ' 月度波动能量分析',
|
||||
href: '/saber/month_power_wave_plot',
|
||||
},
|
||||
],
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NavigationMenu>
|
||||
<NavigationMenu class="mx-auto">
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink href="/" :class="navigationMenuTriggerStyle()">
|
||||
主页
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>探空气球</NavigationMenuTrigger>
|
||||
<NavigationMenuItem v-for="(thisHeader, headers) in header_data" :key="headers">
|
||||
<NavigationMenuTrigger>{{ headers }}</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul class="grid gap-3 p-6 lg:grid-cols-[minmax(0,.75fr)_minmax(0,1fr)] lg:w-[500px] md:w-[400px]">
|
||||
<li class="row-span-3">
|
||||
<ul class="w-60 flex flex-col gap-3 p-6">
|
||||
<li v-for="header in thisHeader" :key="header.href" class="row-span-3">
|
||||
<NavigationMenuLink as-child>
|
||||
<a
|
||||
class="block select-none rounded-md p-3 leading-none no-underline outline-none transition-colors space-y-1 focus:bg-accent hover:bg-accent focus:text-accent-foreground hover:text-accent-foreground"
|
||||
href="/balloon/single"
|
||||
:href="header.href"
|
||||
>
|
||||
<div class="text-sm font-medium leading-none">
|
||||
探空气球 - 重力波单次
|
||||
{{ header.title }}
|
||||
</div>
|
||||
<p class="line-clamp-2 text-sm text-muted-foreground leading-tight">
|
||||
探空气球 - 重力波单次
|
||||
</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavigationMenuLink as-child>
|
||||
<a
|
||||
href="/balloon/year"
|
||||
class="block select-none rounded-md p-3 leading-none no-underline outline-none transition-colors space-y-1 focus:bg-accent hover:bg-accent focus:text-accent-foreground hover:text-accent-foreground"
|
||||
>
|
||||
<div class="text-sm font-medium leading-none">探空气球 - 重力波统计</div>
|
||||
<p class="line-clamp-2 text-sm text-muted-foreground leading-tight">
|
||||
探空气球 - 重力波统计
|
||||
</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Components</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul class="grid w-[400px] gap-3 p-4 md:grid-cols-2 lg:w-[600px] md:w-[500px]">
|
||||
<li v-for="component in components" :key="component.title">
|
||||
<NavigationMenuLink as-child>
|
||||
<a
|
||||
:href="component.href"
|
||||
class="block select-none rounded-md p-3 leading-none no-underline outline-none transition-colors space-y-1 focus:bg-accent hover:bg-accent focus:text-accent-foreground hover:text-accent-foreground"
|
||||
>
|
||||
<div class="text-sm font-medium leading-none">{{ component.title }}</div>
|
||||
<p class="line-clamp-2 text-sm text-muted-foreground leading-snug">
|
||||
{{ component.description }}
|
||||
</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuLink href="/docs/introduction" :class="navigationMenuTriggerStyle()">
|
||||
Documentation
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
</template>
|
||||
|
||||
26
src/components/ui/button/Button.vue
Normal file
26
src/components/ui/button/Button.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { Primitive, type PrimitiveProps } from 'radix-vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
import { type ButtonVariants, buttonVariants } from '.'
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
variant?: ButtonVariants['variant']
|
||||
size?: ButtonVariants['size']
|
||||
class?: HTMLAttributes['class']
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
as: 'button',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn(buttonVariants({ variant, size }), props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
35
src/components/ui/button/index.ts
Normal file
35
src/components/ui/button/index.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
|
||||
export { default as Button } from './Button.vue'
|
||||
|
||||
export const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: 'h-9 px-4 py-2',
|
||||
sm: 'h-8 rounded-md px-3 text-xs',
|
||||
lg: 'h-10 rounded-md px-8',
|
||||
icon: 'h-9 w-9',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export type ButtonVariants = VariantProps<typeof buttonVariants>
|
||||
27
src/components/ui/label/Label.vue
Normal file
27
src/components/ui/label/Label.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { Label, type LabelProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
const props = defineProps<LabelProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Label
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</Label>
|
||||
</template>
|
||||
1
src/components/ui/label/index.ts
Normal file
1
src/components/ui/label/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Label } from './Label.vue'
|
||||
@ -25,7 +25,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
<template>
|
||||
<NavigationMenuRoot
|
||||
v-bind="forwarded"
|
||||
:class="cn('relative z-10 flex max-w-max flex-1 items-center justify-center', props.class)"
|
||||
:class="cn('relative z-10 flex justify-center', props.class)"
|
||||
>
|
||||
<slot />
|
||||
<NavigationMenuViewport />
|
||||
|
||||
@ -25,7 +25,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
<NavigationMenuContent
|
||||
v-bind="forwarded"
|
||||
:class="cn(
|
||||
'left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto',
|
||||
'absolute w-full left-0 top-0 data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 ',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
|
||||
@ -19,7 +19,7 @@ const forwardedProps = useForwardProps(delegatedProps)
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'group flex flex-1 list-none items-center justify-center gap-x-1',
|
||||
'center flex list-none gap-x-1',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
||||
@ -19,7 +19,7 @@ const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="absolute left-0 top-full flex justify-center">
|
||||
<div class="absolute left-0 top-full w-full flex perspective-[2000px] justify-center">
|
||||
<NavigationMenuViewport
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
|
||||
@ -9,5 +9,5 @@ export { default as NavigationMenuTrigger } from './NavigationMenuTrigger.vue'
|
||||
export { default as NavigationMenuViewport } from './NavigationMenuViewport.vue'
|
||||
|
||||
export const navigationMenuTriggerStyle = cva(
|
||||
'group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50',
|
||||
'group flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50',
|
||||
)
|
||||
|
||||
14
src/components/ui/skeleton/Skeleton.vue
Normal file
14
src/components/ui/skeleton/Skeleton.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '~/lib/utils'
|
||||
|
||||
interface SkeletonProps {
|
||||
class?: HTMLAttributes['class']
|
||||
}
|
||||
|
||||
const props = defineProps<SkeletonProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('animate-pulse rounded-md bg-muted', props.class)" />
|
||||
</template>
|
||||
1
src/components/ui/skeleton/index.ts
Normal file
1
src/components/ui/skeleton/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Skeleton } from './Skeleton.vue'
|
||||
@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { API_BASE_URL } from '~/CONSTANT'
|
||||
|
||||
const selectedMode = ref('观测的二阶多项式拟合')
|
||||
|
||||
const imageUrl = ref('')
|
||||
@ -16,13 +18,13 @@ const selectedDate = ref('2022-01-01')
|
||||
const dates = ref([] as string[])
|
||||
|
||||
onMounted(async () => {
|
||||
const resp = await fetch('http://localhost:5000/balloon/metadata')
|
||||
const resp = await fetch(`${API_BASE_URL}/balloon/metadata`)
|
||||
const data = await resp.json()
|
||||
dates.value = data
|
||||
})
|
||||
|
||||
async function get_image() {
|
||||
const resp = await fetch(`http://localhost:5000/balloon/render/single?mode=${encodeURIComponent(selectedMode.value)}&path=${encodeURIComponent(selectedDate.value)}`)
|
||||
const resp = await fetch(`${API_BASE_URL}/balloon/render/single?mode=${encodeURIComponent(selectedMode.value)}&path=${encodeURIComponent(selectedDate.value)}`)
|
||||
// check for MIME Type, check if is png
|
||||
const isPng = resp.headers.get('Content-Type') === 'image/png'
|
||||
if (!isPng) {
|
||||
@ -79,7 +81,7 @@ watch([selectedMode, selectedDate], () => {
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="hasWaveThisDay">
|
||||
<img :src="imageUrl">
|
||||
<Image :image-url="imageUrl" />
|
||||
</div>
|
||||
<div v-else h-40 flex="~ col items-center justify-center" text-xl>
|
||||
{{ selectedDate.match(/_(\d{8}T\d{6})/)?.[1] }} <br>
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { API_BASE_URL } from '~/CONSTANT'
|
||||
|
||||
const selectedMode = ref('w/f值统计结果')
|
||||
|
||||
const isIllegal = ref(false)
|
||||
@ -27,7 +29,7 @@ const modes = [
|
||||
]
|
||||
|
||||
async function refreshImage() {
|
||||
const url = `http://localhost:5000/balloon/render/year?mode=${encodeURIComponent(selectedMode.value)}&start_year=${startYear.value}&end_year=${endYear.value}`
|
||||
const url = `${API_BASE_URL}/balloon/render/year?mode=${encodeURIComponent(selectedMode.value)}&start_year=${startYear.value}&end_year=${endYear.value}`
|
||||
const resp = await fetch(url)
|
||||
const blob = await resp.blob()
|
||||
const u = URL.createObjectURL(blob)
|
||||
@ -95,7 +97,7 @@ watch([selectedMode, startYear, endYear], () => {
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="!isIllegal && imageUrl">
|
||||
<img :src="imageUrl">
|
||||
<Image :image-url="imageUrl" />
|
||||
</div>
|
||||
<div v-else h-40 flex="~ col items-center justify-center" text-xl>
|
||||
<div v-if="isIllegal">
|
||||
|
||||
@ -1,46 +1,9 @@
|
||||
<script setup lang="ts" generic="T extends any, O extends any">
|
||||
defineOptions({
|
||||
name: 'IndexPage',
|
||||
})
|
||||
|
||||
const name = ref('')
|
||||
|
||||
const router = useRouter()
|
||||
function go() {
|
||||
if (name.value)
|
||||
router.push(`/hi/${encodeURIComponent(name.value)}`)
|
||||
}
|
||||
import manba from '../../public/pack.png'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div i-carbon-campsite inline-block text-4xl />
|
||||
<p>
|
||||
<a rel="noreferrer" href="https://github.com/antfu-collective/vitesse-lite" target="_blank">
|
||||
Vitesse Lite
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<em text-sm op75>Opinionated Vite Starter Template</em>
|
||||
</p>
|
||||
|
||||
<div py-4 />
|
||||
|
||||
<TheInput
|
||||
v-model="name"
|
||||
placeholder="What's your name?"
|
||||
autocomplete="false"
|
||||
@keydown.enter="go"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<button
|
||||
class="m-3 text-sm btn"
|
||||
:disabled="!name"
|
||||
@click="go"
|
||||
>
|
||||
Go
|
||||
</button>
|
||||
</div>
|
||||
<div flex="~ col items-center">
|
||||
<img :src="manba">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -39,7 +39,7 @@ onFetchResponse(async (resp) => {
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
const resp = await fetch('http://localhost:5000/radar/metadata')
|
||||
const resp = await fetch(`${API_BASE_URL}/radar/metadata`)
|
||||
const data = await resp.json()
|
||||
// use regex to extract the year from the path,
|
||||
// ./radar/data\\武汉左岭镇站\\2017\\ZLT_MET01_DLL_L21_01D_20170316.txt
|
||||
@ -66,8 +66,6 @@ onMounted(async () => {
|
||||
<div flex="~ col items-center">
|
||||
<div>
|
||||
<div flex="~ row items-center gap-3" py-3>
|
||||
{{ stations }}
|
||||
{{ years }}
|
||||
<Select v-model="selectedMode">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="Select a fruit" />
|
||||
@ -115,7 +113,7 @@ onMounted(async () => {
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="!isFetching && imageUrl !== ''">
|
||||
<img :src="imageUrl">
|
||||
<Image :image-url="imageUrl" />
|
||||
</div>
|
||||
<div v-else h-40 flex="~ col items-center justify-center" text-xl>
|
||||
loading...
|
||||
|
||||
@ -1,13 +1,127 @@
|
||||
<script setup lang="ts">
|
||||
import { API_BASE_URL } from '~/CONSTANT'
|
||||
|
||||
const selectedMode = ref('潮汐波')
|
||||
const selectedStation = ref('武汉左岭镇站')
|
||||
const selectedYear = ref('2017')
|
||||
const imageUrl = ref('')
|
||||
|
||||
const modes = [
|
||||
'潮汐波',
|
||||
'2日行星波',
|
||||
'5日行星波',
|
||||
'10日行星波',
|
||||
'16日行星波',
|
||||
]
|
||||
|
||||
const paths = ref([] as string[])
|
||||
const stations = ref<Set<string>>(new Set())
|
||||
const years = ref<Set<string>>(new Set())
|
||||
|
||||
const queryUrl = computed(() => {
|
||||
const station = encodeURIComponent(selectedStation.value)
|
||||
const year = encodeURIComponent(selectedYear.value)
|
||||
const mode = encodeURIComponent(selectedMode.value)
|
||||
const query = `?station=${station}&year=${year}&model_name=${mode}`
|
||||
const path = `${API_BASE_URL}/radar/render/v2${query}`
|
||||
return path
|
||||
})
|
||||
|
||||
const { onFetchResponse, isFetching } = useFetch(queryUrl, { refetch: true })
|
||||
|
||||
onFetchResponse(async (resp) => {
|
||||
const blob = await resp.blob()
|
||||
const url = URL.createObjectURL(blob)
|
||||
imageUrl.value = url
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
const resp = await fetch(`${API_BASE_URL}/radar/metadata`)
|
||||
const data = await resp.json()
|
||||
// use regex to extract the year from the path,
|
||||
// ./radar/data\\武汉左岭镇站\\2017\\ZLT_MET01_DLL_L21_01D_20170316.txt
|
||||
const station_pattern = /data\\(.*?)\\/
|
||||
const year_pattern = /(\d{4})\\ZLT_/
|
||||
const pairs = data.map((source_text: string) => {
|
||||
const station = source_text.match(station_pattern)?.[1]
|
||||
const year = source_text.match(year_pattern)?.[1]
|
||||
return { station, year }
|
||||
})
|
||||
pairs.forEach(({ station, year }: {
|
||||
station: string
|
||||
year: string
|
||||
}) => {
|
||||
stations.value.add(station)
|
||||
years.value.add(year)
|
||||
})
|
||||
|
||||
paths.value = data
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
a
|
||||
<div flex="~ col items-center">
|
||||
<div>
|
||||
<div flex="~ row items-center gap-3" py-3>
|
||||
<Select v-model="selectedMode">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="Select a fruit" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>计算模式</SelectLabel>
|
||||
<SelectItem v-for="mode in modes" :key="mode" :value="mode">
|
||||
{{ mode }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select v-model="selectedStation">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="Select a fruit" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>观测站</SelectLabel>
|
||||
<SelectItem v-for="station in stations" :key="station" :value="station">
|
||||
{{ station }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select v-model="selectedYear">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="Select a fruit" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>天</SelectLabel>
|
||||
<SelectItem v-for="year in years" :key="year" :value="year">
|
||||
{{ year }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{{ selectedMode }}
|
||||
{{ selectedYear }}
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="!isFetching && imageUrl !== ''">
|
||||
<Image :image-url="imageUrl" />
|
||||
</div>
|
||||
<div v-else h-40 flex="~ col items-center justify-center" text-xl>
|
||||
警告:由于计算一年的数据,需要等待大约1分钟出图<br>
|
||||
请勿刷新页面或者离开当前页面、改动参数<br>
|
||||
否则需要重新等待 3 分钟
|
||||
loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
|
||||
110
src/pages/saber/day_cycle_power_wave_plot.vue
Normal file
110
src/pages/saber/day_cycle_power_wave_plot.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<script setup lang="ts">
|
||||
import { API_BASE_URL } from '~/CONSTANT'
|
||||
import { currentSaberDays, refreshCurrentSaberDays, refreshPath, saberPaths } from './utils'
|
||||
|
||||
const selected = reactive({
|
||||
path: '',
|
||||
day: '',
|
||||
cycle_no: 1,
|
||||
})
|
||||
|
||||
const imageUrl = ref('')
|
||||
|
||||
const urll = computed(() => {
|
||||
const path = encodeURIComponent(selected.path)
|
||||
const day = encodeURIComponent(selected.day)
|
||||
const query = `path=${path}&day=${day}&cycle_no=${selected.cycle_no}`
|
||||
return `${API_BASE_URL}/saber/render/day_cycle_power_wave_plot?${query}`
|
||||
})
|
||||
const { onFetchResponse, isFetching } = useFetch(
|
||||
urll,
|
||||
{ refetch: true },
|
||||
)
|
||||
|
||||
onFetchResponse(async (resp) => {
|
||||
const blob = await resp.blob()
|
||||
const url = URL.createObjectURL(blob)
|
||||
imageUrl.value = url
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
refreshPath()
|
||||
selected.path = saberPaths.value[0]
|
||||
})
|
||||
|
||||
watch(() => selected.path, () => {
|
||||
refreshCurrentSaberDays(selected.path)
|
||||
if (selected.day === '') {
|
||||
selected.day = currentSaberDays.value[0]
|
||||
}
|
||||
})
|
||||
|
||||
function renderPath(path: string) {
|
||||
const yearPattern = /\/data\\(\d{4})/
|
||||
const year = path.match(yearPattern)?.[1]
|
||||
const monthPattern = /Temp_O3_(.*)(\d{4})/
|
||||
const month = path.match(monthPattern)?.[1]
|
||||
return `${year}年${month}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex="~ col items-center" w-full>
|
||||
<div>
|
||||
<div flex="~ row items-center gap-3" py-3>
|
||||
<Label for="day">年月</Label>
|
||||
<Select v-model="selected.path">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="选择日期" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>月份</SelectLabel>
|
||||
<SelectItem v-for="path in saberPaths" :key="path" :value="path">
|
||||
{{ renderPath(path) }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label for="day">天数</Label>
|
||||
<Select id="day" v-model="selected.day">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="选择日期" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Day</SelectLabel>
|
||||
<SelectItem v-for="day in currentSaberDays" :key="day" :value="day">
|
||||
{{ day }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label for="age">Cycle No.</Label>
|
||||
<NumberField id="age" v-model:model-value="selected.cycle_no" :default-value="1" :min="0">
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{{ selected.day }}
|
||||
{{ selected.path }}
|
||||
</div>
|
||||
<div w-full>
|
||||
<div v-if="!isFetching && imageUrl !== ''">
|
||||
<Image :image-url="imageUrl" />
|
||||
</div>
|
||||
<div v-else flex="~ col items-center justify-center" h-40 w-full text-xl>
|
||||
<Loading />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
110
src/pages/saber/day_fft_ifft_plot.vue
Normal file
110
src/pages/saber/day_fft_ifft_plot.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<script setup lang="ts">
|
||||
import { API_BASE_URL } from '~/CONSTANT'
|
||||
import { currentSaberDays, refreshCurrentSaberDays, refreshPath, saberPaths } from './utils'
|
||||
|
||||
const selected = reactive({
|
||||
path: './saber/data\\2012\\SABER_Temp_O3_April2012_v2.0.nc',
|
||||
day: '',
|
||||
cycle_no: 1,
|
||||
})
|
||||
|
||||
const imageUrl = ref('')
|
||||
|
||||
const urll = computed(() => {
|
||||
const path = encodeURIComponent(selected.path)
|
||||
const day = encodeURIComponent(selected.day)
|
||||
const query = `path=${path}&day=${day}&cycle_no=${selected.cycle_no}`
|
||||
return `${API_BASE_URL}/saber/render/day_fft_ifft_plot?${query}`
|
||||
})
|
||||
const { onFetchResponse, isFetching } = useFetch(
|
||||
urll,
|
||||
{ refetch: true },
|
||||
)
|
||||
|
||||
onFetchResponse(async (resp) => {
|
||||
const blob = await resp.blob()
|
||||
const url = URL.createObjectURL(blob)
|
||||
imageUrl.value = url
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
refreshPath()
|
||||
selected.path = saberPaths.value[0]
|
||||
})
|
||||
|
||||
watch(() => selected.path, () => {
|
||||
refreshCurrentSaberDays(selected.path)
|
||||
if (selected.day === '') {
|
||||
selected.day = currentSaberDays.value[0]
|
||||
}
|
||||
})
|
||||
|
||||
function renderPath(path: string) {
|
||||
const yearPattern = /\/data\\(\d{4})/
|
||||
const year = path.match(yearPattern)?.[1]
|
||||
const monthPattern = /Temp_O3_(.*)(\d{4})/
|
||||
const month = path.match(monthPattern)?.[1]
|
||||
return `${year}年${month}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex="~ col items-center" w-full>
|
||||
<div>
|
||||
<div flex="~ row items-center gap-3" py-3>
|
||||
<Label for="day">年月</Label>
|
||||
<Select v-model="selected.path">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="选择日期" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>月份</SelectLabel>
|
||||
<SelectItem v-for="path in saberPaths" :key="path" :value="path">
|
||||
{{ renderPath(path) }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label for="day">天数</Label>
|
||||
<Select id="day" v-model="selected.day">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="选择日期" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Day</SelectLabel>
|
||||
<SelectItem v-for="day in currentSaberDays" :key="day" :value="day">
|
||||
{{ day }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label for="age">Cycle No.</Label>
|
||||
<NumberField id="age" v-model:model-value="selected.cycle_no" :default-value="1" :min="0">
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{{ selected.day }}
|
||||
{{ selected.path }}
|
||||
</div>
|
||||
<div w-full>
|
||||
<div v-if="!isFetching && imageUrl !== ''">
|
||||
<Image :image-url="imageUrl" />
|
||||
</div>
|
||||
<div v-else flex="~ col items-center justify-center" h-40 w-full text-xl>
|
||||
<Loading />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
77
src/pages/saber/month_power_wave_plot.vue
Normal file
77
src/pages/saber/month_power_wave_plot.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import { API_BASE_URL } from '~/CONSTANT'
|
||||
import { refreshPath, saberPaths } from './utils'
|
||||
|
||||
const selected = reactive({
|
||||
path: '',
|
||||
})
|
||||
|
||||
const imageUrl = ref('')
|
||||
|
||||
const urll = computed(() => {
|
||||
const path = encodeURIComponent(selected.path)
|
||||
const query = `path=${path}`
|
||||
return `${API_BASE_URL}/saber/render/month_power_wave_plot?${query}`
|
||||
})
|
||||
const { onFetchResponse, isFetching } = useFetch(
|
||||
urll,
|
||||
{ refetch: true },
|
||||
)
|
||||
|
||||
onFetchResponse(async (resp) => {
|
||||
const blob = await resp.blob()
|
||||
const url = URL.createObjectURL(blob)
|
||||
imageUrl.value = url
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
refreshPath()
|
||||
selected.path = saberPaths.value[0]
|
||||
})
|
||||
|
||||
function renderPath(path: string) {
|
||||
const yearPattern = /\/data\\(\d{4})/
|
||||
const year = path.match(yearPattern)?.[1]
|
||||
const monthPattern = /Temp_O3_(.*)(\d{4})/
|
||||
const month = path.match(monthPattern)?.[1]
|
||||
return `${year}年${month}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex="~ col items-center" w-full>
|
||||
<div>
|
||||
<div flex="~ row items-center gap-3" py-3>
|
||||
<Label for="day">年月</Label>
|
||||
<Select v-model="selected.path">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="选择日期" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>月份</SelectLabel>
|
||||
<SelectItem v-for="path in saberPaths" :key="path" :value="path">
|
||||
{{ renderPath(path) }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{{ selected.path }}
|
||||
</div>
|
||||
<div w-full>
|
||||
<div v-if="!isFetching && imageUrl !== ''">
|
||||
<Image :image-url="imageUrl" />
|
||||
</div>
|
||||
<div v-else flex="~ col items-center justify-center" h-40 w-full text-xl>
|
||||
<Loading />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
110
src/pages/saber/plot_wave_fitting.vue
Normal file
110
src/pages/saber/plot_wave_fitting.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<script setup lang="ts">
|
||||
import { API_BASE_URL } from '~/CONSTANT'
|
||||
import { currentSaberDays, refreshCurrentSaberDays, refreshPath, saberPaths } from './utils'
|
||||
|
||||
const selected = reactive({
|
||||
path: './saber/data\\2012\\SABER_Temp_O3_April2012_v2.0.nc',
|
||||
day: '',
|
||||
height_no: 1,
|
||||
})
|
||||
|
||||
const imageUrl = ref('')
|
||||
|
||||
const urll = computed(() => {
|
||||
const path = encodeURIComponent(selected.path)
|
||||
const day = encodeURIComponent(selected.day)
|
||||
const query = `path=${path}&day=${day}&height=${selected.height_no}`
|
||||
return `${API_BASE_URL}/saber/render/plot_wave_fitting?${query}`
|
||||
})
|
||||
const { onFetchResponse, isFetching } = useFetch(
|
||||
urll,
|
||||
{ refetch: true },
|
||||
)
|
||||
|
||||
onFetchResponse(async (resp) => {
|
||||
const blob = await resp.blob()
|
||||
const url = URL.createObjectURL(blob)
|
||||
imageUrl.value = url
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
refreshPath()
|
||||
selected.path = saberPaths.value[0]
|
||||
})
|
||||
|
||||
watch(() => selected.path, () => {
|
||||
refreshCurrentSaberDays(selected.path)
|
||||
if (saberPaths.value.length > 0) {
|
||||
selected.path = saberPaths.value[0]
|
||||
}
|
||||
})
|
||||
|
||||
function renderPath(path: string) {
|
||||
const yearPattern = /\/data\\(\d{4})/
|
||||
const year = path.match(yearPattern)?.[1]
|
||||
const monthPattern = /Temp_O3_(.*)(\d{4})/
|
||||
const month = path.match(monthPattern)?.[1]
|
||||
return `${year}年${month}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex="~ col items-center" w-full>
|
||||
<div>
|
||||
<div flex="~ row items-center gap-3" py-3>
|
||||
<Label for="day">年月</Label>
|
||||
<Select v-model="selected.path">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="选择日期" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>月份</SelectLabel>
|
||||
<SelectItem v-for="path in saberPaths" :key="path" :value="path">
|
||||
{{ renderPath(path) }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label for="day">天数</Label>
|
||||
<Select id="day" v-model="selected.day">
|
||||
<SelectTrigger class="w-[180px]">
|
||||
<SelectValue placeholder="选择日期" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Day</SelectLabel>
|
||||
<SelectItem v-for="day in currentSaberDays" :key="day" :value="day">
|
||||
{{ day }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label for="age">高度</Label>
|
||||
<NumberField id="age" :default-value="18" :min="0">
|
||||
<NumberFieldContent>
|
||||
<NumberFieldDecrement />
|
||||
<NumberFieldInput />
|
||||
<NumberFieldIncrement />
|
||||
</NumberFieldContent>
|
||||
</NumberField>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{{ selected.day }}
|
||||
{{ selected.path }}
|
||||
</div>
|
||||
<div w-full>
|
||||
<div v-if="!isFetching && imageUrl !== ''">
|
||||
<Image :image-url="imageUrl" />
|
||||
</div>
|
||||
<div v-else flex="~ col items-center justify-center" h-40 w-full text-xl>
|
||||
<Loading />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
20
src/pages/saber/utils.ts
Normal file
20
src/pages/saber/utils.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { API_BASE_URL } from '~/CONSTANT'
|
||||
|
||||
const saberPaths = ref<string[]>([])
|
||||
const currentSaberDays = ref<string>('')
|
||||
|
||||
async function refreshPath() {
|
||||
if (saberPaths.value.length)
|
||||
return
|
||||
const resp = await fetch(`${API_BASE_URL}/saber/metadata`)
|
||||
const data = await resp.json()
|
||||
saberPaths.value = data
|
||||
}
|
||||
|
||||
async function refreshCurrentSaberDays(path: string) {
|
||||
const resp = await fetch(`${API_BASE_URL}/saber/metadata/list_days?path=${path}`)
|
||||
const data = await resp.json()
|
||||
currentSaberDays.value = data
|
||||
}
|
||||
|
||||
export { currentSaberDays, refreshCurrentSaberDays, refreshPath, saberPaths }
|
||||
4
typed-router.d.ts
vendored
4
typed-router.d.ts
vendored
@ -24,5 +24,9 @@ declare module 'vue-router/auto-routes' {
|
||||
'/balloon/year': RouteRecordInfo<'/balloon/year', '/balloon/year', Record<never, never>, Record<never, never>>,
|
||||
'/radar/v1': RouteRecordInfo<'/radar/v1', '/radar/v1', Record<never, never>, Record<never, never>>,
|
||||
'/radar/v2': RouteRecordInfo<'/radar/v2', '/radar/v2', Record<never, never>, Record<never, never>>,
|
||||
'/saber/day_cycle_power_wave_plot': RouteRecordInfo<'/saber/day_cycle_power_wave_plot', '/saber/day_cycle_power_wave_plot', Record<never, never>, Record<never, never>>,
|
||||
'/saber/day_fft_ifft_plot': RouteRecordInfo<'/saber/day_fft_ifft_plot', '/saber/day_fft_ifft_plot', Record<never, never>, Record<never, never>>,
|
||||
'/saber/month_power_wave_plot': RouteRecordInfo<'/saber/month_power_wave_plot', '/saber/month_power_wave_plot', Record<never, never>, Record<never, never>>,
|
||||
'/saber/plot_wave_fitting': RouteRecordInfo<'/saber/plot_wave_fitting', '/saber/plot_wave_fitting', Record<never, never>, Record<never, never>>,
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import Vue from '@vitejs/plugin-vue'
|
||||
import UnoCSS from 'unocss/vite'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import VueMacros from 'unplugin-vue-macros/vite'
|
||||
import { VueRouterAutoImports } from 'unplugin-vue-router'
|
||||
import VueRouter from 'unplugin-vue-router/vite'
|
||||
import { defineConfig } from 'vite'
|
||||
@ -17,19 +16,12 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
VueMacros({
|
||||
defineOptions: false,
|
||||
defineModels: false,
|
||||
plugins: {
|
||||
vue: Vue({
|
||||
script: {
|
||||
propsDestructure: true,
|
||||
defineModel: true,
|
||||
},
|
||||
}),
|
||||
Vue({
|
||||
script: {
|
||||
propsDestructure: true,
|
||||
defineModel: true,
|
||||
},
|
||||
}),
|
||||
|
||||
// https://github.com/posva/unplugin-vue-router
|
||||
VueRouter(),
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user