124 lines
2.8 KiB
Vue
124 lines
2.8 KiB
Vue
<script setup lang="ts">
|
|
import { computed, onMounted } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
|
|
import { useAppStore } from '@/stores/app'
|
|
import { useLoginStore } from '@/stores/login'
|
|
|
|
const route = useRoute()
|
|
const appStore = useAppStore()
|
|
const loginStore = useLoginStore()
|
|
|
|
const currentResource = computed(() => loginStore.menuForPath(route.path))
|
|
const pageTitle = computed(() => currentResource.value?.name ?? String(route.meta.title ?? '首页'))
|
|
const pageGroup = computed(() => {
|
|
const directory = loginStore.authorization?.navigation.find((item) =>
|
|
item.children?.some((child) => child.id === currentResource.value?.id),
|
|
)
|
|
return directory?.name ?? String(route.meta.group ?? '工作台')
|
|
})
|
|
const selectedCampusId = computed<string>({
|
|
get: () => appStore.selectedCampusId,
|
|
set: (id) => appStore.selectCampus(id),
|
|
})
|
|
const campusPlaceholder = computed(() => {
|
|
if (appStore.campusLoading) {
|
|
return '院区加载中…'
|
|
}
|
|
|
|
return appStore.campusLoadFailed ? '院区加载失败' : '暂无可用院区'
|
|
})
|
|
|
|
onMounted(() => {
|
|
void appStore.loadCampuses()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<header class="clinical-header">
|
|
<div class="header-page">
|
|
<span class="header-title">{{ pageTitle }}</span>
|
|
<span class="header-breadcrumb">{{ pageGroup }} / {{ pageTitle }}</span>
|
|
</div>
|
|
<span class="header-spacer" />
|
|
<label class="campus-selector">
|
|
<span class="campus-label">院区</span>
|
|
<select
|
|
v-model="selectedCampusId"
|
|
:disabled="appStore.campusLoading || !appStore.campusRecords.length"
|
|
aria-label="选择院区"
|
|
>
|
|
<option v-if="!appStore.campusRecords.length" value="">
|
|
{{ campusPlaceholder }}
|
|
</option>
|
|
<option v-for="campus in appStore.campusRecords" :key="campus.id" :value="campus.id">
|
|
{{ campus.name }}
|
|
</option>
|
|
</select>
|
|
</label>
|
|
</header>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.clinical-header {
|
|
display: flex;
|
|
flex-shrink: 0;
|
|
gap: 14px;
|
|
align-items: center;
|
|
height: var(--header-height);
|
|
padding: 0 20px;
|
|
background: #fff;
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
.header-page {
|
|
display: flex;
|
|
gap: 14px;
|
|
align-items: baseline;
|
|
min-width: 0;
|
|
}
|
|
|
|
.header-title {
|
|
flex-shrink: 0;
|
|
color: var(--ink);
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.header-breadcrumb {
|
|
overflow: hidden;
|
|
color: var(--mut);
|
|
font-size: 12px;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.header-spacer {
|
|
flex: 1;
|
|
}
|
|
|
|
.campus-selector {
|
|
display: flex;
|
|
flex-shrink: 0;
|
|
gap: 6px;
|
|
align-items: center;
|
|
}
|
|
|
|
.campus-label {
|
|
color: var(--mut);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.campus-selector select {
|
|
width: auto;
|
|
padding: 4px 8px;
|
|
color: var(--brand-d);
|
|
font-size: 12.5px;
|
|
font-weight: 700;
|
|
background: var(--brand-l);
|
|
border: 1px solid var(--brand);
|
|
border-radius: 6px;
|
|
}
|
|
|
|
</style>
|