feat: add SingleRecordMnt component for student record maintenance with search, add, edit, view, and delete functionalities
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<v-container class="fill-height">
|
||||
<v-row align="center" justify="center" no-gutters>
|
||||
<v-col cols="12" md="8" sm="10">
|
||||
<v-card border class="pa-6" variant="flat">
|
||||
<v-card-title class="d-flex align-center ga-3">
|
||||
<v-icon :color="color" size="36">{{ icon }}</v-icon>
|
||||
<div class="text-h5">{{ title }}</div>
|
||||
<div class="text-caption text-medium-emphasis">{{ codeLabel }}</div>
|
||||
<v-spacer />
|
||||
<img alt="robot" class="robot-icon" height="32" src="@/assets/robot-svgrepo-com.svg" width="32" />
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text v-if="description" class="text-body-1 mt-4">
|
||||
<p class="py-3">
|
||||
{{ description }}
|
||||
</p>
|
||||
<v-alert v-if="backendMessage" class="mt-6" :color="color" density="compact" type="warning" variant="tonal">
|
||||
{{ backendMessage }}
|
||||
</v-alert>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="mt-6">
|
||||
<v-btn v-if="showBack" variant="text" @click="router.back()">返回上一頁</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn v-if="showHome" color="primary" :to="{ name: 'home' }" variant="flat">回到首頁</v-btn>
|
||||
<v-btn v-if="showLogin" class="ml-2" color="primary" :to="{ name: 'login' }" variant="outlined">
|
||||
前往登入
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
type Props = {
|
||||
code: string | number
|
||||
title: string
|
||||
description?: string
|
||||
icon?: string
|
||||
color?: string
|
||||
showHome?: boolean
|
||||
showLogin?: boolean
|
||||
showBack?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
description: undefined,
|
||||
icon: 'mdi-alert-circle-outline',
|
||||
color: 'warning',
|
||||
showHome: true,
|
||||
showLogin: true,
|
||||
showBack: true
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const codeLabel = computed(() => {
|
||||
const code = props.code
|
||||
return typeof code === 'number' ? `HTTP ${code}` : String(code)
|
||||
})
|
||||
|
||||
const backendMessage = computed(() => {
|
||||
const raw = route.query.message
|
||||
if (typeof raw !== 'string') return ''
|
||||
return raw.trim()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@keyframes breathe {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%, 45%, 55%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.robot-icon {
|
||||
animation: breathe 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.robot-icon path:nth-child(2),
|
||||
.robot-icon path:nth-child(3) {
|
||||
animation: blink 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.robot-icon path:nth-child(3) {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user