feat: add SingleRecordMnt component for student record maintenance with search, add, edit, view, and delete functionalities

This commit is contained in:
skytek_xinliang
2026-03-26 11:24:37 +08:00
parent 507afcc99c
commit 069141794e
116 changed files with 15247 additions and 107 deletions
+118
View File
@@ -0,0 +1,118 @@
<template>
<v-app>
<v-app-bar color="surface">
<v-toolbar-title>{{ systemTitle }}</v-toolbar-title>
<v-spacer />
<v-menu
v-model="menuOpen"
:close-on-content-click="false"
:location="menuLocation"
:max-height="menuMaxHeight"
offset="8"
scroll-strategy="reposition"
:width="menuWidth"
>
<template #activator="{ props }">
<v-btn v-bind="props" icon="mdi-dots-vertical" />
</template>
<v-list :density="menuDensity">
<template v-for="(item, index) in menuItems" :key="item?.key ?? item?.path ?? index">
<v-list-group
v-if="smAndDown && item?.subItems?.length"
:value="item?.key ?? item?.path ?? index"
>
<template #activator="{ props }">
<v-list-item v-bind="props" :prepend-icon="item.icon" :title="item.title" />
</template>
<v-list-item
v-for="(subItem, subIndex) in item.subItems"
:key="subItem?.key ?? subItem?.path ?? subIndex"
class="pl-6"
:prepend-icon="subItem.icon"
:title="subItem.title"
@click="handleSelect(subItem)"
/>
</v-list-group>
<v-menu
v-else-if="item?.subItems?.length"
close-delay="120"
:close-on-content-click="false"
location="end top"
offset="0"
open-delay="80"
:open-on-hover="submenuOpenOnHover"
origin="start top"
scroll-strategy="reposition"
submenu
>
<template #activator="{ props }">
<v-list-item
v-bind="props"
append-icon="mdi-chevron-right"
:prepend-icon="item.icon"
:title="item.title"
/>
</template>
<v-list :density="menuDensity">
<v-list-item
v-for="(subItem, subIndex) in item.subItems"
:key="subItem?.key ?? subItem?.path ?? subIndex"
:prepend-icon="subItem.icon"
:title="subItem.title"
@click="handleSelect(subItem)"
/>
</v-list>
</v-menu>
<v-list-item
v-else
:prepend-icon="item.icon"
:title="item.title"
@click="handleSelect(item)"
/>
</template>
</v-list>
</v-menu>
</v-app-bar>
<v-main>
<v-container fluid height="100%">
<slot></slot>
</v-container>
</v-main>
</v-app>
</template>
<script setup>
import { computed, ref } from 'vue'
import { useDisplay } from 'vuetify'
const emit = defineEmits(['select'])
defineProps({
systemTitle: { type: String, default: '簡潔模式' },
menuItems: {
type: Array,
default: () => [],
},
})
const menuOpen = ref(false)
const { smAndDown } = useDisplay()
const menuDensity = computed(() => (smAndDown.value ? 'default' : 'compact'))
const menuWidth = computed(() => (smAndDown.value ? 280 : 240))
const menuMaxHeight = computed(() => (smAndDown.value ? 420 : 360))
const menuLocation = computed(() => (smAndDown.value ? 'bottom end' : 'bottom end'))
const submenuOpenOnHover = computed(() => !smAndDown.value)
function handleSelect (item) {
menuOpen.value = false
emit('select', item)
}
</script>