feat: add SingleRecordMnt component for student record maintenance with search, add, edit, view, and delete functionalities
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user