update: iter 1.5
Some checks are pending
Test / build (lts/*, ubuntu-latest) (push) Waiting to run
Test / build (lts/*, windows-latest) (push) Waiting to run

This commit is contained in:
Dustella 2025-01-21 22:27:09 +08:00
parent 7783049690
commit b8e4e63cf1
Signed by: Dustella
GPG Key ID: 07C9FA0DB5373B8D
13 changed files with 187 additions and 24 deletions

2
auto-imports.d.ts vendored
View File

@ -8,6 +8,7 @@ export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const authCode: typeof import('./src/composables/fetch')['authCode']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const baseFetch: typeof import('./src/composables/fetch')['baseFetch']
const computed: typeof import('vue')['computed']
@ -309,6 +310,7 @@ declare module 'vue' {
interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
readonly authCode: UnwrapRef<typeof import('./src/composables/fetch')['authCode']>
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly baseFetch: UnwrapRef<typeof import('./src/composables/fetch')['baseFetch']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>

1
components.d.ts vendored
View File

@ -14,6 +14,7 @@ declare module 'vue' {
Alert: typeof import('./src/components/ui/alert/Alert.vue')['default']
AlertDescription: typeof import('./src/components/ui/alert/AlertDescription.vue')['default']
AlertTitle: typeof import('./src/components/ui/alert/AlertTitle.vue')['default']
AuthBlock: typeof import('./src/components/AuthBlock.vue')['default']
AutoForm: typeof import('./src/components/ui/auto-form/AutoForm.vue')['default']
AutoFormField: typeof import('./src/components/ui/auto-form/AutoFormField.vue')['default']
AutoFormFieldArray: typeof import('./src/components/ui/auto-form/AutoFormFieldArray.vue')['default']

View File

@ -34,7 +34,22 @@ import {
SidebarProvider,
SidebarRail,
} from '~/components/ui/sidebar'
import { authCode } from './composables'
const router = useRouter()
function logout() {
localStorage.clear()
sessionStorage.clear()
authCode.value = ''
router.push('/auth')
}
onMounted(() => {
if (!authCode.value) {
router.push('/auth')
}
})
// This is sample data.
const data = {
user: {
@ -60,7 +75,7 @@ const data = {
],
},
{
title: 'Saber',
title: 'SABER',
url: '#',
icon: 'game-icons:cracked-saber',
isActive: true,
@ -139,15 +154,14 @@ const data = {
<template>
<div>
<div>
<!-- <TopHeader /> -->
<div v-if="$route.path === '/auth'">
<RouterView />
</div>
<div>
<div v-else>
<SidebarProvider>
<Sidebar collapsible="icon">
<SidebarHeader>
<Alert>
<Terminal class="h-4 w-4" />
<AlertTitle>服务状态正常</AlertTitle>
</Alert>
</SidebarHeader>
@ -183,6 +197,7 @@ const data = {
:to="subItem.url"
active-class="bg-accent-foreground text-accent"
>
<span>&nbsp;</span>
<span>{{ subItem.title }}</span>
</RouterLink>
</SidebarMenuSubButton>
@ -210,11 +225,16 @@ const data = {
<ChevronsUpDown class="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent class="min-w-56 w-[--radix-dropdown-menu-trigger-width] rounded-lg" side="bottom" align="end" :side-offset="4">
<DropdownMenuItem>
<Icon icon="heroicons-solid:logout" class="h-4 w-4" />
退出系统
</DropdownMenuItem>
<DropdownMenuContent
class="min-w-56 w-[--radix-dropdown-menu-trigger-width] rounded-lg" side="bottom" align="end"
:side-offset="4"
>
<div @click="logout">
<DropdownMenuItem>
<Icon icon="heroicons-solid:logout" class="h-4 w-4" />
退出系统
</DropdownMenuItem>
</div>
</DropdownMenuContent>
</DropdownMenu>
<Button variant="outline" size="icon" class="ml-auto h-8 w-8">

View File

@ -1,2 +1,3 @@
export const API_BASE_URL = 'http://localhost:5000'
// export const API_BASE_URL = 'https://gca-api.dustella.net:8443'
// export const API_BASE_URL = 'http://localhost:5000'
// export const API_BASE_URL = 'http://localhost:18200'
export const API_BASE_URL = 'https://gca-api.dustella.net:8443'

BIN
src/assets/map.avif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -0,0 +1,77 @@
<script setup lang="ts">
import { Button } from '~/components/ui/button'
import { Input } from '~/components/ui/input'
import { Label } from '~/components/ui/label'
import { authCode } from '~/composables'
import { API_BASE_URL } from '~/CONSTANT'
import manba from '../../public/pack.png'
const code = ref('')
const router = useRouter()
const remember = ref(false)
function auth() {
authCode.value = code.value
const resp = baseFetch(`${API_BASE_URL}/ping`, {
})
if (!resp.error.value) {
if (remember.value) {
localStorage.setItem('authCode', code.value)
}
else {
sessionStorage.setItem('authCode', code.value)
}
router.push('/')
return
}
// eslint-disable-next-line no-alert
alert('认证失败')
}
</script>
<template>
<div class="w-full lg:grid lg:grid-cols-2 lg:min-h-[600px] xl:min-h-[800px]">
<div class="hidden p-30 lg:block">
<img
:src="manba"
alt="Image"
class="h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
>
</div>
<div class="flex items-center justify-center bg-muted py-12">
<div class="grid mx-auto w-[350px] gap-6">
<div class="grid gap-2 text-center">
<h1 class="text-3xl font-bold">
系统认证
</h1>
<p class="text-balance text-muted-foreground">
本系统仅供内部使用
</p>
</div>
<div class="grid gap-4">
<div class="grid gap-2">
<div class="flex items-center">
<Label for="password">口令</Label>
</div>
<Input id="password" v-model="code" type="password" required />
</div>
<div flex="~ items-center gap-2">
<Checkbox id="remember" v-model="remember" />
<Label for="remember" class="flex items-center">
记住我
</Label>
</div>
<Button type="submit" class="w-full" @click.prevent="auth">
认证
</Button>
</div>
<div class="mt-4 text-center text-sm">
丢失密码请前往后端重置
<!-- <a href="#" class="underline">
Sign up
</a> -->
</div>
</div>
</div>
</div>
</template>

View File

@ -7,9 +7,18 @@ export interface ImageResult {
message?: string
}
defineProps<{
const props = defineProps<{
imageResult: ImageResult
}>()
function download() {
if (props.imageResult.result === 'success') {
const a = document.createElement('a')
a.href = props.imageResult.imageUrl
a.download = 'image.png'
a.click()
}
}
</script>
<template>
@ -26,7 +35,7 @@ defineProps<{
{{ imageResult.message }}
</div>
<Button type="submit" size="sm" class="ml-auto gap-1.5">
<Button type="submit" size="sm" class="ml-auto gap-1.5" @click.prevent="download">
下载图片
<CornerDownLeft class="size-3.5" />
</Button>

View File

@ -1,6 +1,12 @@
import { createFetch } from '@vueuse/core'
import { API_BASE_URL } from '~/CONSTANT'
export const authCode = ref(
localStorage.getItem('authCode')
|| sessionStorage.getItem('authCode')
|| '',
)
export const baseFetch = createFetch({
baseUrl: API_BASE_URL,
options: {

12
src/pages/auth.vue Normal file
View File

@ -0,0 +1,12 @@
<script setup lang="ts">
</script>
<template>
<div>
<AuthBlock />
</div>
</template>
<style scoped>
</style>

View File

@ -37,10 +37,28 @@ const modes = [
'Temperature amplitude (K)',
'纬向动量通量统计结果',
'经向动量通量统计结果',
'horizontal propagation',
'每月上传/下传重力波占比',
'动能和势能分布情况',
]
// 'horizontal propagation',
// '/',
// '',
] as const
const mode_display_mapping = {
'w/f值统计结果': '本征频率与惯性频率的比值',
'周期统计结果': '周期统计',
'垂直波长分布': '垂直波长',
'水平波长分布': '水平波长',
'纬向本征相速度': '纬向本征相速度',
'经向本征相速度': '经向本征相速度',
'垂直本征相速度': '垂直本征相速度',
'Zonal wind amplitude (m/s)': '纬向风扰动幅度',
'扰动振幅统计结果': '经向风扰动幅度',
'Temperature amplitude (K)': '温度扰动振幅',
'纬向动量通量统计结果': '纬向动量通量统计',
'经向动量通量统计结果': '经向动量通量统计',
'horizontal propagation': 'horizontal propagation',
'每月上传/下传重力波占比': '每月上传/下传重力波占比',
'动能和势能分布情况': '动能和势能分布情况',
}
async function refreshImage() {
const url = `${API_BASE_URL}/balloon/render/year?mode=${encodeURIComponent(selectedMode.value)}&start_year=${startYear.value}&end_year=${endYear.value}`
@ -57,7 +75,6 @@ watch([selectedMode, startYear, endYear], () => {
return
}
isIllegal.value = false
refreshImage()
})
</script>
@ -65,16 +82,21 @@ watch([selectedMode, startYear, endYear], () => {
<DenseFramework :image-result="imageResult" @submit="refreshImage">
<div flex="col gap-5 ~ justify-center">
<div>
<Label>计算模式</Label>
<Label>统计参数</Label>
<Select v-model="selectedMode">
<SelectTrigger class="w-[180px]">
<SelectTrigger>
<SelectValue placeholder="Select a fruit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>模式</SelectLabel>
<SelectItem v-for="mode in modes" :key="mode" :value="mode">
{{ mode }}
<SelectItem
v-for="mode in Array.from(modes).sort(((a, b) => {
return mode_display_mapping[a].length - mode_display_mapping[b].length
}))"
:key="mode" :value="mode"
>
{{ mode_display_mapping[mode] }}
</SelectItem>
</SelectGroup>
</SelectContent>

View File

@ -46,7 +46,7 @@ onFetchResponse(async (resp) => {
<template>
<DenseFramework :image-result="imageResult" @submit="execute">
<Label for="T_range">滑动窗口</Label>
<Label for="T_range">行星波周期</Label>
<Tabs id="T_range" v-model="selectedT" default-value="5">
<TabsList class="grid grid-cols-3 w-full">
<TabsTrigger value="5">

View File

@ -1,3 +1,15 @@
<route lang="json">
{
"meta":{
"title":"TIDI 行星波月统计",
"icon":"mdi:telescope",
"group":"TIDI",
"item_name":"行星波月统计"
}
}
</route>
<script setup lang="ts">
import type { ImageResult } from '~/components/ImageContainer.vue'
import { API_BASE_URL } from '~/CONSTANT'

1
typed-router.d.ts vendored
View File

@ -20,6 +20,7 @@ declare module 'vue-router/auto-routes' {
export interface RouteNamedMap {
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
'/[...all]': RouteRecordInfo<'/[...all]', '/:all(.*)', { all: ParamValue<true> }, { all: ParamValue<false> }>,
'/auth': RouteRecordInfo<'/auth', '/auth', Record<never, never>, Record<never, never>>,
'/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>>,
'/cosmic/stats': RouteRecordInfo<'/cosmic/stats', '/cosmic/stats', Record<never, never>, Record<never, never>>,