2
This commit is contained in:
parent
63bb246e17
commit
663270d9af
25
components.d.ts
vendored
25
components.d.ts
vendored
@ -7,10 +7,35 @@ export {}
|
|||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
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']
|
||||||
|
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']
|
||||||
|
NumberFieldContent: typeof import('./src/components/ui/number-field/NumberFieldContent.vue')['default']
|
||||||
|
NumberFieldDecrement: typeof import('./src/components/ui/number-field/NumberFieldDecrement.vue')['default']
|
||||||
|
NumberFieldIncrement: typeof import('./src/components/ui/number-field/NumberFieldIncrement.vue')['default']
|
||||||
|
NumberFieldInput: typeof import('./src/components/ui/number-field/NumberFieldInput.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
Select: typeof import('./src/components/ui/select/Select.vue')['default']
|
||||||
|
SelectContent: typeof import('./src/components/ui/select/SelectContent.vue')['default']
|
||||||
|
SelectGroup: typeof import('./src/components/ui/select/SelectGroup.vue')['default']
|
||||||
|
SelectItem: typeof import('./src/components/ui/select/SelectItem.vue')['default']
|
||||||
|
SelectItemText: typeof import('./src/components/ui/select/SelectItemText.vue')['default']
|
||||||
|
SelectLabel: typeof import('./src/components/ui/select/SelectLabel.vue')['default']
|
||||||
|
SelectScrollDownButton: typeof import('./src/components/ui/select/SelectScrollDownButton.vue')['default']
|
||||||
|
SelectScrollUpButton: typeof import('./src/components/ui/select/SelectScrollUpButton.vue')['default']
|
||||||
|
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']
|
TheCounter: typeof import('./src/components/TheCounter.vue')['default']
|
||||||
TheFooter: typeof import('./src/components/TheFooter.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']
|
TheInput: typeof import('./src/components/TheInput.vue')['default']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
components.json
Normal file
18
components.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://shadcn-vue.com/schema.json",
|
||||||
|
"style": "default",
|
||||||
|
"typescript": true,
|
||||||
|
"tsConfigPath": "./tsconfig.json",
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.js",
|
||||||
|
"css": "src/assets/index.css",
|
||||||
|
"baseColor": "neutral",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"framework": "vite",
|
||||||
|
"aliases": {
|
||||||
|
"components": "~/components",
|
||||||
|
"utils": "~/lib/utils"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -14,6 +14,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vueuse/core": "^12.0.0",
|
"@vueuse/core": "^12.0.0",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-vue-next": "^0.468.0",
|
||||||
|
"radix-vue": "^1.9.11",
|
||||||
|
"shadcn-vue": "^0.11.3",
|
||||||
|
"tailwind-merge": "^2.5.5",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0"
|
||||||
},
|
},
|
||||||
@ -36,6 +43,8 @@
|
|||||||
"taze": "^0.18.0",
|
"taze": "^0.18.0",
|
||||||
"typescript": "~5.6.3",
|
"typescript": "~5.6.3",
|
||||||
"unocss": "^0.65.1",
|
"unocss": "^0.65.1",
|
||||||
|
"unocss-preset-animations": "^1.1.0",
|
||||||
|
"unocss-preset-shadcn": "^0.3.1",
|
||||||
"unplugin-auto-import": "^0.19.0",
|
"unplugin-auto-import": "^0.19.0",
|
||||||
"unplugin-vue-components": "^0.28.0",
|
"unplugin-vue-components": "^0.28.0",
|
||||||
"unplugin-vue-macros": "^2.13.6",
|
"unplugin-vue-macros": "^2.13.6",
|
||||||
|
|||||||
1464
pnpm-lock.yaml
generated
1464
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div class="w-full flex items-center justify-center">
|
||||||
|
<div class="max-w-[980px]">
|
||||||
|
<TheHeader />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<main font-sans p="x-4 y-10" text="center gray-700 dark:gray-200">
|
<main font-sans p="x-4 y-10" text="center gray-700 dark:gray-200">
|
||||||
|
<div class="mx-auto max-w-[980px]">
|
||||||
<RouterView />
|
<RouterView />
|
||||||
<TheFooter />
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
<TheFooter />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
1
src/CONSTANT.ts
Normal file
1
src/CONSTANT.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const API_BASE_URL = 'http://localhost:5000'
|
||||||
78
src/assets/index.css
Normal file
78
src/assets/index.css
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 222.2 84% 4.9%;
|
||||||
|
|
||||||
|
--muted: 210 40% 96.1%;
|
||||||
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
|
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 222.2 84% 4.9%;
|
||||||
|
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 222.2 84% 4.9%;
|
||||||
|
|
||||||
|
--border: 214.3 31.8% 91.4%;
|
||||||
|
--input: 214.3 31.8% 91.4%;
|
||||||
|
|
||||||
|
--primary: 222.2 47.4% 11.2%;
|
||||||
|
--primary-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--secondary: 210 40% 96.1%;
|
||||||
|
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--accent: 210 40% 96.1%;
|
||||||
|
--accent-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--ring: 222.2 84% 4.9%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 222.2 84% 4.9%;
|
||||||
|
--foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--muted: 217.2 32.6% 17.5%;
|
||||||
|
--muted-foreground: 215 20.2% 65.1%;
|
||||||
|
|
||||||
|
--popover: 222.2 84% 4.9%;
|
||||||
|
--popover-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--card: 222.2 84% 4.9%;
|
||||||
|
--card-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--border: 217.2 32.6% 17.5%;
|
||||||
|
--input: 217.2 32.6% 17.5%;
|
||||||
|
|
||||||
|
--primary: 210 40% 98%;
|
||||||
|
--primary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--secondary: 217.2 32.6% 17.5%;
|
||||||
|
--secondary-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--accent: 217.2 32.6% 17.5%;
|
||||||
|
--accent-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--ring: 212.7 26.8% 83.9%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +0,0 @@
|
|||||||
## Components
|
|
||||||
|
|
||||||
Components in this dir will be auto-registered and on-demand, powered by [`unplugin-vue-components`](https://github.com/antfu/unplugin-vue-components).
|
|
||||||
|
|
||||||
### Icons
|
|
||||||
|
|
||||||
You can use icons from almost any icon sets by the power of [Iconify](https://iconify.design/).
|
|
||||||
|
|
||||||
It will only bundle the icons you use. Check out [`unplugin-icons`](https://github.com/antfu/unplugin-icons) for more details.
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const props = defineProps<{
|
|
||||||
initial: number
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const { count, inc, dec } = useCounter(props.initial)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
{{ count }}
|
|
||||||
<button class="inc" @click="inc()">
|
|
||||||
+
|
|
||||||
</button>
|
|
||||||
<button class="dec" @click="dec()">
|
|
||||||
-
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@ -1,15 +1,3 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav mt-6 inline-flex gap-2 text-xl>
|
<div />
|
||||||
<button icon-btn @click="toggleDark()">
|
|
||||||
<div i-carbon-sun dark:i-carbon-moon />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<a
|
|
||||||
i-carbon-logo-github icon-btn
|
|
||||||
rel="noreferrer"
|
|
||||||
href="https://github.com/antfu-collective/vitesse-lite"
|
|
||||||
target="_blank"
|
|
||||||
title="GitHub"
|
|
||||||
/>
|
|
||||||
</nav>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
139
src/components/TheHeader.vue
Normal file
139
src/components/TheHeader.vue
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
NavigationMenu,
|
||||||
|
NavigationMenuContent,
|
||||||
|
NavigationMenuItem,
|
||||||
|
NavigationMenuLink,
|
||||||
|
NavigationMenuList,
|
||||||
|
NavigationMenuTrigger,
|
||||||
|
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.',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NavigationMenu>
|
||||||
|
<NavigationMenuList>
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<NavigationMenuLink href="/" :class="navigationMenuTriggerStyle()">
|
||||||
|
主页
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</NavigationMenuItem>
|
||||||
|
<NavigationMenuItem>
|
||||||
|
<NavigationMenuTrigger>探空气球</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">
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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>
|
||||||
@ -1,18 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const modelValue = defineModel()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<input
|
|
||||||
id="input"
|
|
||||||
v-model="modelValue"
|
|
||||||
type="text"
|
|
||||||
v-bind="$attrs"
|
|
||||||
p="x-4 y-2"
|
|
||||||
w="250px"
|
|
||||||
text="center"
|
|
||||||
bg="transparent"
|
|
||||||
border="~ rounded gray-200 dark:gray-700"
|
|
||||||
outline="none active:none"
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
33
src/components/ui/navigation-menu/NavigationMenu.vue
Normal file
33
src/components/ui/navigation-menu/NavigationMenu.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
NavigationMenuRoot,
|
||||||
|
type NavigationMenuRootEmits,
|
||||||
|
type NavigationMenuRootProps,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import NavigationMenuViewport from './NavigationMenuViewport.vue'
|
||||||
|
|
||||||
|
const props = defineProps<NavigationMenuRootProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const emits = defineEmits<NavigationMenuRootEmits>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NavigationMenuRoot
|
||||||
|
v-bind="forwarded"
|
||||||
|
:class="cn('relative z-10 flex max-w-max flex-1 items-center justify-center', props.class)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<NavigationMenuViewport />
|
||||||
|
</NavigationMenuRoot>
|
||||||
|
</template>
|
||||||
34
src/components/ui/navigation-menu/NavigationMenuContent.vue
Normal file
34
src/components/ui/navigation-menu/NavigationMenuContent.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
NavigationMenuContent,
|
||||||
|
type NavigationMenuContentEmits,
|
||||||
|
type NavigationMenuContentProps,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<NavigationMenuContentProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const emits = defineEmits<NavigationMenuContentEmits>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<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',
|
||||||
|
props.class,
|
||||||
|
)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</NavigationMenuContent>
|
||||||
|
</template>
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { NavigationMenuIndicator, type NavigationMenuIndicatorProps, useForwardProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<NavigationMenuIndicatorProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NavigationMenuIndicator
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
:class="cn('top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in', props.class)"
|
||||||
|
>
|
||||||
|
<div class="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
||||||
|
</NavigationMenuIndicator>
|
||||||
|
</template>
|
||||||
11
src/components/ui/navigation-menu/NavigationMenuItem.vue
Normal file
11
src/components/ui/navigation-menu/NavigationMenuItem.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { NavigationMenuItem, type NavigationMenuItemProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<NavigationMenuItemProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NavigationMenuItem v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</NavigationMenuItem>
|
||||||
|
</template>
|
||||||
19
src/components/ui/navigation-menu/NavigationMenuLink.vue
Normal file
19
src/components/ui/navigation-menu/NavigationMenuLink.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
NavigationMenuLink,
|
||||||
|
type NavigationMenuLinkEmits,
|
||||||
|
type NavigationMenuLinkProps,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<NavigationMenuLinkProps>()
|
||||||
|
const emits = defineEmits<NavigationMenuLinkEmits>()
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(props, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NavigationMenuLink v-bind="forwarded">
|
||||||
|
<slot />
|
||||||
|
</NavigationMenuLink>
|
||||||
|
</template>
|
||||||
29
src/components/ui/navigation-menu/NavigationMenuList.vue
Normal file
29
src/components/ui/navigation-menu/NavigationMenuList.vue
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { NavigationMenuList, type NavigationMenuListProps, useForwardProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<NavigationMenuListProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NavigationMenuList
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'group flex flex-1 list-none items-center justify-center gap-x-1',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</NavigationMenuList>
|
||||||
|
</template>
|
||||||
34
src/components/ui/navigation-menu/NavigationMenuTrigger.vue
Normal file
34
src/components/ui/navigation-menu/NavigationMenuTrigger.vue
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ChevronDown } from 'lucide-vue-next'
|
||||||
|
import {
|
||||||
|
NavigationMenuTrigger,
|
||||||
|
type NavigationMenuTriggerProps,
|
||||||
|
useForwardProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import { navigationMenuTriggerStyle } from '.'
|
||||||
|
|
||||||
|
const props = defineProps<NavigationMenuTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NavigationMenuTrigger
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
:class="cn(navigationMenuTriggerStyle(), 'group', props.class)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<ChevronDown
|
||||||
|
class="relative top-px ml-1 h-3 w-3 transition duration-200 group-data-[state=open]:rotate-180"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</NavigationMenuTrigger>
|
||||||
|
</template>
|
||||||
33
src/components/ui/navigation-menu/NavigationMenuViewport.vue
Normal file
33
src/components/ui/navigation-menu/NavigationMenuViewport.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
NavigationMenuViewport,
|
||||||
|
type NavigationMenuViewportProps,
|
||||||
|
useForwardProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<NavigationMenuViewportProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="absolute left-0 top-full flex justify-center">
|
||||||
|
<NavigationMenuViewport
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'origin-top-center relative mt-1.5 h-[--radix-navigation-menu-viewport-height] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[--radix-navigation-menu-viewport-width]',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
13
src/components/ui/navigation-menu/index.ts
Normal file
13
src/components/ui/navigation-menu/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { cva } from 'class-variance-authority'
|
||||||
|
|
||||||
|
export { default as NavigationMenu } from './NavigationMenu.vue'
|
||||||
|
export { default as NavigationMenuContent } from './NavigationMenuContent.vue'
|
||||||
|
export { default as NavigationMenuItem } from './NavigationMenuItem.vue'
|
||||||
|
export { default as NavigationMenuLink } from './NavigationMenuLink.vue'
|
||||||
|
export { default as NavigationMenuList } from './NavigationMenuList.vue'
|
||||||
|
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',
|
||||||
|
)
|
||||||
23
src/components/ui/number-field/NumberField.vue
Normal file
23
src/components/ui/number-field/NumberField.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { NumberFieldRootEmits, NumberFieldRootProps } from 'radix-vue'
|
||||||
|
import { NumberFieldRoot, useForwardPropsEmits } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<NumberFieldRootProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
const emits = defineEmits<NumberFieldRootEmits>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NumberFieldRoot v-bind="forwarded" :class="cn('grid gap-1.5', props.class)">
|
||||||
|
<slot />
|
||||||
|
</NumberFieldRoot>
|
||||||
|
</template>
|
||||||
14
src/components/ui/number-field/NumberFieldContent.vue
Normal file
14
src/components/ui/number-field/NumberFieldContent.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="cn('relative [&>[data-slot=input]]:has-[[data-slot=increment]]:pr-5 [&>[data-slot=input]]:has-[[data-slot=decrement]]:pl-5', props.class)">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
25
src/components/ui/number-field/NumberFieldDecrement.vue
Normal file
25
src/components/ui/number-field/NumberFieldDecrement.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { NumberFieldDecrementProps } from 'radix-vue'
|
||||||
|
import { Minus } from 'lucide-vue-next'
|
||||||
|
import { NumberFieldDecrement, useForwardProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<NumberFieldDecrementProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwarded = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NumberFieldDecrement data-slot="decrement" v-bind="forwarded" :class="cn('absolute top-1/2 -translate-y-1/2 left-0 p-3 disabled:cursor-not-allowed disabled:opacity-20', props.class)">
|
||||||
|
<slot>
|
||||||
|
<Minus class="h-4 w-4" />
|
||||||
|
</slot>
|
||||||
|
</NumberFieldDecrement>
|
||||||
|
</template>
|
||||||
25
src/components/ui/number-field/NumberFieldIncrement.vue
Normal file
25
src/components/ui/number-field/NumberFieldIncrement.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { NumberFieldIncrementProps } from 'radix-vue'
|
||||||
|
import { Plus } from 'lucide-vue-next'
|
||||||
|
import { NumberFieldIncrement, useForwardProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<NumberFieldIncrementProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwarded = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NumberFieldIncrement data-slot="increment" v-bind="forwarded" :class="cn('absolute top-1/2 -translate-y-1/2 right-0 disabled:cursor-not-allowed disabled:opacity-20 p-3', props.class)">
|
||||||
|
<slot>
|
||||||
|
<Plus class="h-4 w-4" />
|
||||||
|
</slot>
|
||||||
|
</NumberFieldIncrement>
|
||||||
|
</template>
|
||||||
16
src/components/ui/number-field/NumberFieldInput.vue
Normal file
16
src/components/ui/number-field/NumberFieldInput.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { NumberFieldInput } from 'radix-vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NumberFieldInput
|
||||||
|
data-slot="input"
|
||||||
|
:class="cn('flex h-10 w-full rounded-md border border-input bg-background py-2 text-sm text-center ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', props.class)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
5
src/components/ui/number-field/index.ts
Normal file
5
src/components/ui/number-field/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export { default as NumberField } from './NumberField.vue'
|
||||||
|
export { default as NumberFieldContent } from './NumberFieldContent.vue'
|
||||||
|
export { default as NumberFieldDecrement } from './NumberFieldDecrement.vue'
|
||||||
|
export { default as NumberFieldIncrement } from './NumberFieldIncrement.vue'
|
||||||
|
export { default as NumberFieldInput } from './NumberFieldInput.vue'
|
||||||
15
src/components/ui/select/Select.vue
Normal file
15
src/components/ui/select/Select.vue
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { SelectRootEmits, SelectRootProps } from 'radix-vue'
|
||||||
|
import { SelectRoot, useForwardPropsEmits } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<SelectRootProps>()
|
||||||
|
const emits = defineEmits<SelectRootEmits>()
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(props, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectRoot v-bind="forwarded">
|
||||||
|
<slot />
|
||||||
|
</SelectRoot>
|
||||||
|
</template>
|
||||||
53
src/components/ui/select/SelectContent.vue
Normal file
53
src/components/ui/select/SelectContent.vue
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
SelectContent,
|
||||||
|
type SelectContentEmits,
|
||||||
|
type SelectContentProps,
|
||||||
|
SelectPortal,
|
||||||
|
SelectViewport,
|
||||||
|
useForwardPropsEmits,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
import { SelectScrollDownButton, SelectScrollUpButton } from '.'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<SelectContentProps & { class?: HTMLAttributes['class'] }>(),
|
||||||
|
{
|
||||||
|
position: 'popper',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
const emits = defineEmits<SelectContentEmits>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectPortal>
|
||||||
|
<SelectContent
|
||||||
|
v-bind="{ ...forwarded, ...$attrs }" :class="cn(
|
||||||
|
'relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
|
position === 'popper'
|
||||||
|
&& 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<SelectScrollUpButton />
|
||||||
|
<SelectViewport :class="cn('p-1', position === 'popper' && 'h-[--radix-select-trigger-height] w-full min-w-[--radix-select-trigger-width]')">
|
||||||
|
<slot />
|
||||||
|
</SelectViewport>
|
||||||
|
<SelectScrollDownButton />
|
||||||
|
</SelectContent>
|
||||||
|
</SelectPortal>
|
||||||
|
</template>
|
||||||
19
src/components/ui/select/SelectGroup.vue
Normal file
19
src/components/ui/select/SelectGroup.vue
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { SelectGroup, type SelectGroupProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<SelectGroupProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectGroup :class="cn('p-1 w-full', props.class)" v-bind="delegatedProps">
|
||||||
|
<slot />
|
||||||
|
</SelectGroup>
|
||||||
|
</template>
|
||||||
44
src/components/ui/select/SelectItem.vue
Normal file
44
src/components/ui/select/SelectItem.vue
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Check } from 'lucide-vue-next'
|
||||||
|
import {
|
||||||
|
SelectItem,
|
||||||
|
SelectItemIndicator,
|
||||||
|
type SelectItemProps,
|
||||||
|
SelectItemText,
|
||||||
|
useForwardProps,
|
||||||
|
} from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<SelectItemProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectItem
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
|
props.class,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span class="absolute left-2 h-3.5 w-3.5 flex items-center justify-center">
|
||||||
|
<SelectItemIndicator>
|
||||||
|
<Check class="h-4 w-4" />
|
||||||
|
</SelectItemIndicator>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<SelectItemText>
|
||||||
|
<slot />
|
||||||
|
</SelectItemText>
|
||||||
|
</SelectItem>
|
||||||
|
</template>
|
||||||
11
src/components/ui/select/SelectItemText.vue
Normal file
11
src/components/ui/select/SelectItemText.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { SelectItemText, type SelectItemTextProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<SelectItemTextProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectItemText v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</SelectItemText>
|
||||||
|
</template>
|
||||||
13
src/components/ui/select/SelectLabel.vue
Normal file
13
src/components/ui/select/SelectLabel.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { HTMLAttributes } from 'vue'
|
||||||
|
import { SelectLabel, type SelectLabelProps } from 'radix-vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<SelectLabelProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectLabel :class="cn('py-1.5 pl-8 pr-2 text-sm font-semibold', props.class)">
|
||||||
|
<slot />
|
||||||
|
</SelectLabel>
|
||||||
|
</template>
|
||||||
24
src/components/ui/select/SelectScrollDownButton.vue
Normal file
24
src/components/ui/select/SelectScrollDownButton.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ChevronDown } from 'lucide-vue-next'
|
||||||
|
import { SelectScrollDownButton, type SelectScrollDownButtonProps, useForwardProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<SelectScrollDownButtonProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectScrollDownButton v-bind="forwardedProps" :class="cn('flex cursor-default items-center justify-center py-1', props.class)">
|
||||||
|
<slot>
|
||||||
|
<ChevronDown class="h-4 w-4" />
|
||||||
|
</slot>
|
||||||
|
</SelectScrollDownButton>
|
||||||
|
</template>
|
||||||
24
src/components/ui/select/SelectScrollUpButton.vue
Normal file
24
src/components/ui/select/SelectScrollUpButton.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ChevronUp } from 'lucide-vue-next'
|
||||||
|
import { SelectScrollUpButton, type SelectScrollUpButtonProps, useForwardProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<SelectScrollUpButtonProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectScrollUpButton v-bind="forwardedProps" :class="cn('flex cursor-default items-center justify-center py-1', props.class)">
|
||||||
|
<slot>
|
||||||
|
<ChevronUp class="h-4 w-4" />
|
||||||
|
</slot>
|
||||||
|
</SelectScrollUpButton>
|
||||||
|
</template>
|
||||||
17
src/components/ui/select/SelectSeparator.vue
Normal file
17
src/components/ui/select/SelectSeparator.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { SelectSeparator, type SelectSeparatorProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<SelectSeparatorProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectSeparator v-bind="delegatedProps" :class="cn('-mx-1 my-1 h-px bg-muted', props.class)" />
|
||||||
|
</template>
|
||||||
31
src/components/ui/select/SelectTrigger.vue
Normal file
31
src/components/ui/select/SelectTrigger.vue
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ChevronDown } from 'lucide-vue-next'
|
||||||
|
import { SelectIcon, SelectTrigger, type SelectTriggerProps, useForwardProps } from 'radix-vue'
|
||||||
|
import { computed, type HTMLAttributes } from 'vue'
|
||||||
|
import { cn } from '~/lib/utils'
|
||||||
|
|
||||||
|
const props = defineProps<SelectTriggerProps & { class?: HTMLAttributes['class'] }>()
|
||||||
|
|
||||||
|
const delegatedProps = computed(() => {
|
||||||
|
const { class: _, ...delegated } = props
|
||||||
|
|
||||||
|
return delegated
|
||||||
|
})
|
||||||
|
|
||||||
|
const forwardedProps = useForwardProps(delegatedProps)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectTrigger
|
||||||
|
v-bind="forwardedProps"
|
||||||
|
:class="cn(
|
||||||
|
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:truncate text-start',
|
||||||
|
props.class,
|
||||||
|
)"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<SelectIcon as-child>
|
||||||
|
<ChevronDown class="h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</SelectIcon>
|
||||||
|
</SelectTrigger>
|
||||||
|
</template>
|
||||||
11
src/components/ui/select/SelectValue.vue
Normal file
11
src/components/ui/select/SelectValue.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { SelectValue, type SelectValueProps } from 'radix-vue'
|
||||||
|
|
||||||
|
const props = defineProps<SelectValueProps>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<SelectValue v-bind="props">
|
||||||
|
<slot />
|
||||||
|
</SelectValue>
|
||||||
|
</template>
|
||||||
11
src/components/ui/select/index.ts
Normal file
11
src/components/ui/select/index.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export { default as Select } from './Select.vue'
|
||||||
|
export { default as SelectContent } from './SelectContent.vue'
|
||||||
|
export { default as SelectGroup } from './SelectGroup.vue'
|
||||||
|
export { default as SelectItem } from './SelectItem.vue'
|
||||||
|
export { default as SelectItemText } from './SelectItemText.vue'
|
||||||
|
export { default as SelectLabel } from './SelectLabel.vue'
|
||||||
|
export { default as SelectScrollDownButton } from './SelectScrollDownButton.vue'
|
||||||
|
export { default as SelectScrollUpButton } from './SelectScrollUpButton.vue'
|
||||||
|
export { default as SelectSeparator } from './SelectSeparator.vue'
|
||||||
|
export { default as SelectTrigger } from './SelectTrigger.vue'
|
||||||
|
export { default as SelectValue } from './SelectValue.vue'
|
||||||
6
src/lib/utils.ts
Normal file
6
src/lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { type ClassValue, clsx } from 'clsx'
|
||||||
|
import { twMerge } from 'tailwind-merge'
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ import App from './App.vue'
|
|||||||
|
|
||||||
import '@unocss/reset/tailwind.css'
|
import '@unocss/reset/tailwind.css'
|
||||||
import './styles/main.css'
|
import './styles/main.css'
|
||||||
|
import './assets/index.css'
|
||||||
import 'uno.css'
|
import 'uno.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|||||||
94
src/pages/balloon/single.vue
Normal file
94
src/pages/balloon/single.vue
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const selectedMode = ref('观测的二阶多项式拟合')
|
||||||
|
|
||||||
|
const imageUrl = ref('')
|
||||||
|
const hasWaveThisDay = ref(false)
|
||||||
|
|
||||||
|
const modes = [
|
||||||
|
'观测的二阶多项式拟合',
|
||||||
|
'扰动分量的正弦波拟合',
|
||||||
|
'径向风-纬向风矢量图',
|
||||||
|
'温度-水平风矢量图',
|
||||||
|
]
|
||||||
|
|
||||||
|
const selectedDate = ref('2022-01-01')
|
||||||
|
|
||||||
|
const dates = ref([] as string[])
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const resp = await fetch('http://localhost:5000/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)}`)
|
||||||
|
// check for MIME Type, check if is png
|
||||||
|
const isPng = resp.headers.get('Content-Type') === 'image/png'
|
||||||
|
if (!isPng) {
|
||||||
|
hasWaveThisDay.value = false
|
||||||
|
return
|
||||||
|
// return alert('No image available for this mode and date')
|
||||||
|
}
|
||||||
|
hasWaveThisDay.value = true
|
||||||
|
const blob = await resp.blob()
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
imageUrl.value = url
|
||||||
|
}
|
||||||
|
|
||||||
|
watch([selectedMode, selectedDate], () => {
|
||||||
|
get_image()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<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="selectedDate">
|
||||||
|
<SelectTrigger class="w-[180px]">
|
||||||
|
<SelectValue placeholder="Select a fruit" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectLabel>天</SelectLabel>
|
||||||
|
<SelectItem v-for="date in dates" :key="date" :value="date">
|
||||||
|
{{ date.match(/_(\d{8}T\d{6})/)?.[1] }}
|
||||||
|
</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ selectedMode }}
|
||||||
|
{{ selectedDate }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div v-if="hasWaveThisDay">
|
||||||
|
<img :src="imageUrl">
|
||||||
|
</div>
|
||||||
|
<div v-else h-40 flex="~ col items-center justify-center" text-xl>
|
||||||
|
{{ selectedDate.match(/_(\d{8}T\d{6})/)?.[1] }} <br>
|
||||||
|
这个时刻没有数据
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
115
src/pages/balloon/year.vue
Normal file
115
src/pages/balloon/year.vue
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const selectedMode = ref('w/f值统计结果')
|
||||||
|
|
||||||
|
const isIllegal = ref(false)
|
||||||
|
const imageUrl = ref('')
|
||||||
|
|
||||||
|
const startYear = ref(2017)
|
||||||
|
const endYear = ref(2024)
|
||||||
|
|
||||||
|
const modes = [
|
||||||
|
|
||||||
|
'w/f值统计结果',
|
||||||
|
'周期统计结果',
|
||||||
|
'垂直波长分布',
|
||||||
|
'水平波长分布',
|
||||||
|
'纬向本征相速度',
|
||||||
|
'经向本征相速度',
|
||||||
|
'垂直本征相速度',
|
||||||
|
'Zonal wind amplitude (m/s)',
|
||||||
|
'扰动振幅统计结果',
|
||||||
|
'Temperature amplitude (K)',
|
||||||
|
'纬向动量通量统计结果',
|
||||||
|
'经向动量通量统计结果',
|
||||||
|
'horizontal propagation',
|
||||||
|
'每月上传/下传重力波占比',
|
||||||
|
'动能和势能分布情况',
|
||||||
|
]
|
||||||
|
|
||||||
|
async function refreshImage() {
|
||||||
|
const url = `http://localhost:5000/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)
|
||||||
|
imageUrl.value = u
|
||||||
|
}
|
||||||
|
|
||||||
|
watch([selectedMode, startYear, endYear], () => {
|
||||||
|
if (startYear.value > endYear.value) {
|
||||||
|
isIllegal.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isIllegal.value = false
|
||||||
|
refreshImage()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<div flex="row ~ items-center justify-center">
|
||||||
|
<div>
|
||||||
|
<Label>计算模式</Label>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<NumberField
|
||||||
|
id="start"
|
||||||
|
v-model:model-value="startYear" :format-options="{
|
||||||
|
useGrouping: false,
|
||||||
|
}" :default-value="2017" :min="2017" :max="2024"
|
||||||
|
>
|
||||||
|
<Label for="start">起始年</Label>
|
||||||
|
<NumberFieldContent>
|
||||||
|
<NumberFieldDecrement />
|
||||||
|
<NumberFieldInput />
|
||||||
|
<NumberFieldIncrement />
|
||||||
|
</NumberFieldContent>
|
||||||
|
</NumberField>
|
||||||
|
<NumberField
|
||||||
|
id="end"
|
||||||
|
v-model:model-value="endYear" :format-options="{
|
||||||
|
style: 'decimal',
|
||||||
|
notation: 'standard',
|
||||||
|
useGrouping: false,
|
||||||
|
}" :default-value="2017" :min="2017" :max="2024"
|
||||||
|
>
|
||||||
|
<Label for="end">终止年</Label>
|
||||||
|
<NumberFieldContent>
|
||||||
|
<NumberFieldDecrement />
|
||||||
|
<NumberFieldInput />
|
||||||
|
<NumberFieldIncrement />
|
||||||
|
</NumberFieldContent>
|
||||||
|
</NumberField>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div v-if="!isIllegal && imageUrl">
|
||||||
|
<img :src="imageUrl">
|
||||||
|
</div>
|
||||||
|
<div v-else h-40 flex="~ col items-center justify-center" text-xl>
|
||||||
|
<div v-if="isIllegal">
|
||||||
|
<div>Start year should be less than end year</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div>There is no data</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -1,22 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const params = useRoute('/hi/[name]').params
|
|
||||||
const router = useRouter()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div i-carbon-pedestrian inline-block text-4xl />
|
|
||||||
<p>
|
|
||||||
Hi, {{ params.name }}
|
|
||||||
</p>
|
|
||||||
<p text-sm op50>
|
|
||||||
<em>Dynamic route!</em>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button m-3 mt-8 text-sm btn @click="router.back()">
|
|
||||||
Back
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
129
src/pages/radar/v1.vue
Normal file
129
src/pages/radar/v1.vue
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { API_BASE_URL } from '~/CONSTANT'
|
||||||
|
|
||||||
|
const selectedMode = ref('潮汐波')
|
||||||
|
const selectedStation = ref('武汉左岭镇站')
|
||||||
|
const selectedYear = ref('2017')
|
||||||
|
const selectedWindType = ref('uwind')
|
||||||
|
const selectedH = ref(90000)
|
||||||
|
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 windType = encodeURIComponent(selectedWindType.value)
|
||||||
|
const query = `?station=${station}&year=${year}&model_name=${mode}&wind_type=${windType}&H=${selectedH.value}`
|
||||||
|
const path = `${API_BASE_URL}/radar/render/v1${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('http://localhost:5000/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 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" />
|
||||||
|
</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 !== ''">
|
||||||
|
<img :src="imageUrl">
|
||||||
|
</div>
|
||||||
|
<div v-else h-40 flex="~ col items-center justify-center" text-xl>
|
||||||
|
loading...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
13
src/pages/radar/v2.vue
Normal file
13
src/pages/radar/v2.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
a
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
5
typed-router.d.ts
vendored
5
typed-router.d.ts
vendored
@ -20,6 +20,9 @@ declare module 'vue-router/auto-routes' {
|
|||||||
export interface RouteNamedMap {
|
export interface RouteNamedMap {
|
||||||
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
|
||||||
'/[...all]': RouteRecordInfo<'/[...all]', '/:all(.*)', { all: ParamValue<true> }, { all: ParamValue<false> }>,
|
'/[...all]': RouteRecordInfo<'/[...all]', '/:all(.*)', { all: ParamValue<true> }, { all: ParamValue<false> }>,
|
||||||
'/hi/[name]': RouteRecordInfo<'/hi/[name]', '/hi/:name', { name: ParamValue<true> }, { name: ParamValue<false> }>,
|
'/balloon/single': RouteRecordInfo<'/balloon/single', '/balloon/single', Record<never, never>, Record<never, never>>,
|
||||||
|
'/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>>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import {
|
|||||||
presetUno,
|
presetUno,
|
||||||
presetWebFonts,
|
presetWebFonts,
|
||||||
} from 'unocss'
|
} from 'unocss'
|
||||||
|
import presetAnimations from 'unocss-preset-animations'
|
||||||
|
import { presetShadcn } from 'unocss-preset-shadcn'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
shortcuts: [
|
shortcuts: [
|
||||||
@ -25,5 +27,20 @@ export default defineConfig({
|
|||||||
mono: 'DM Mono',
|
mono: 'DM Mono',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
presetAnimations(),
|
||||||
|
presetShadcn({
|
||||||
|
// With default setting for SolidUI, you need to set the darkSelector option.
|
||||||
|
darkSelector: '[data-kb-theme="dark"]',
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
|
content: {
|
||||||
|
pipeline: {
|
||||||
|
include: [
|
||||||
|
// the default
|
||||||
|
/\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html)($|\?)/,
|
||||||
|
// include js/ts files
|
||||||
|
'(components|src)/**/*.{js,ts}',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user