84 lines
3.0 KiB
Vue
84 lines
3.0 KiB
Vue
<script setup>
|
||
import { ref, onMounted, watch } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import MajorCategoryCard from '@/components/MajorCategoryCard.vue'
|
||
import CategoryCard from '@/components/CategoryCard.vue'
|
||
import Skeleton from '@/components/Skeleton.vue'
|
||
import { useAuthStore } from '@/store/auth'
|
||
import { useUiStore } from '@/store/ui'
|
||
import { fetchCategoriesByMajor } from '@/api/material'
|
||
|
||
defineOptions({ name: 'Home' })
|
||
const router = useRouter()
|
||
const auth = useAuthStore()
|
||
const ui = useUiStore()
|
||
|
||
const majors = [
|
||
{ value: 'architecture', label: '建筑' },
|
||
{ value: 'landscape', label: '景观' },
|
||
{ value: 'equipment', label: '设备' },
|
||
{ value: 'decoration', label: '装修' },
|
||
]
|
||
|
||
const selected = ref(ui.selectedMajor)
|
||
const categories = ref([])
|
||
const loading = ref(false)
|
||
|
||
const loadCategories = async (major) => {
|
||
if (!major) { categories.value = []; return }
|
||
loading.value = true
|
||
try { categories.value = await fetchCategoriesByMajor(major) }
|
||
finally { loading.value = false }
|
||
}
|
||
|
||
const onSelect = (v) => {
|
||
selected.value = selected.value === v ? '' : v
|
||
ui.setMajor(selected.value)
|
||
}
|
||
|
||
watch(selected, loadCategories, { immediate: true })
|
||
|
||
onMounted(async () => {
|
||
if (!auth.user) { try { await auth.loadUser() } catch {} }
|
||
})
|
||
|
||
const goCategory = (c) => router.push({ name: 'CategoryDetail', params: { major: selected.value, category: c.value } })
|
||
const onLogout = () => { auth.logout(); router.replace('/login') }
|
||
</script>
|
||
|
||
<template>
|
||
<div class="min-h-screen">
|
||
<header class="h-12 px-4 flex items-center justify-between bg-white border-b border-line">
|
||
<div class="text-sm text-muted">你好,<span class="text-neutral-800 font-medium">{{ auth.user?.username || '' }}</span></div>
|
||
<button class="text-sm text-muted active:text-danger" @click="onLogout">退出</button>
|
||
</header>
|
||
|
||
<section class="p-4 grid grid-cols-2 gap-3">
|
||
<MajorCategoryCard v-for="m in majors" :key="m.value"
|
||
:label="m.label" :value="m.value" :active="selected === m.value"
|
||
@click="onSelect(m.value)" />
|
||
</section>
|
||
|
||
<section class="px-4 pb-6">
|
||
<transition name="fade">
|
||
<div v-if="selected">
|
||
<div class="text-xs text-muted mb-2">细分种类</div>
|
||
<div v-if="loading" class="grid grid-cols-2 gap-3">
|
||
<Skeleton v-for="n in 4" :key="n" class="h-14" />
|
||
</div>
|
||
<div v-else-if="categories.length" class="grid grid-cols-2 gap-3">
|
||
<CategoryCard v-for="c in categories" :key="c.value" :value="c.value" :count="c.count" @click="goCategory(c)" />
|
||
</div>
|
||
<div v-else class="py-10 text-center text-sm text-muted">该大类暂无已审核材料</div>
|
||
</div>
|
||
<div v-else class="py-10 text-center text-sm text-muted">点击上方分类查看细分种类</div>
|
||
</transition>
|
||
</section>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.fade-enter-active,.fade-leave-active{transition:all .2s ease}
|
||
.fade-enter-from,.fade-leave-to{opacity:0;transform:translateY(-4px)}
|
||
</style>
|