继续完善页面布局
This commit is contained in:
81
layouts/admin/components/AdminAside.uvue
Normal file
81
layouts/admin/components/AdminAside.uvue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<view class="aside">
|
||||
<view class="aside-header">
|
||||
<view class="logo">
|
||||
<text class="logo-text">{{ collapsed ? '商' : '商城后台' }}</text>
|
||||
</view>
|
||||
<view class="collapse-btn" @click="$emit('toggle')">
|
||||
<text class="collapse-text">{{ collapsed ? '›' : '‹' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="aside-menu">
|
||||
<view
|
||||
v-for="m in menuList"
|
||||
:key="m.id"
|
||||
class="aside-item"
|
||||
:class="{ active: activeMenuId === m.id }"
|
||||
@click="$emit('menu-click', m.id)"
|
||||
>
|
||||
<image class="aside-icon" :src="m.icon" mode="aspectFit" />
|
||||
<text class="aside-title" v-if="!collapsed">{{ m.title }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import type { MenuItem } from '../types.uts'
|
||||
|
||||
defineProps<{
|
||||
collapsed: boolean
|
||||
menuList: MenuItem[]
|
||||
activeMenuId: string
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e:'toggle'): void
|
||||
(e:'menu-click', menuId: string): void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.aside{
|
||||
width: 96px;
|
||||
background: #1f2a37;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
z-index: 1000;
|
||||
}
|
||||
.aside-header{
|
||||
height: 56px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content: space-between;
|
||||
padding: 0 12px;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
.logo-text{ color:#fff; font-size:14px; font-weight:600; }
|
||||
.collapse-btn{ width:28px; height:28px; display:flex; align-items:center; justify-content:center; }
|
||||
.collapse-text{ color:rgba(255,255,255,0.7); }
|
||||
|
||||
.aside-menu{ padding: 8px 0; }
|
||||
.aside-item{
|
||||
height: 54px;
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
gap: 6px;
|
||||
margin: 6px 10px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.aside-item.active{ background:#1677ff; }
|
||||
.aside-icon{ width:22px; height:22px; }
|
||||
.aside-title{ color:#fff; font-size:12px; }
|
||||
</style>
|
||||
20
layouts/admin/components/AdminFooter.uvue
Normal file
20
layouts/admin/components/AdminFooter.uvue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<view class="footer">
|
||||
<text class="footer-text">商城后台 © {{ year }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
const year = new Date().getFullYear()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.footer{
|
||||
height: 44px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
color:#9ca3af;
|
||||
}
|
||||
.footer-text{ font-size:12px; }
|
||||
</style>
|
||||
63
layouts/admin/components/AdminHeader.uvue
Normal file
63
layouts/admin/components/AdminHeader.uvue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<view class="header">
|
||||
<view class="header-left">
|
||||
<text class="crumb">{{ breadcrumb }}</text>
|
||||
</view>
|
||||
|
||||
<view class="header-right">
|
||||
<view class="hbtn" @click="$emit('search')"><text>🔍</text></view>
|
||||
<view class="hbtn" @click="$emit('refresh')"><text>⟳</text></view>
|
||||
<view class="hbtn" @click="$emit('notify')">
|
||||
<text>🔔</text>
|
||||
<view class="dot" v-if="hasNotification"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
defineProps<{
|
||||
breadcrumb: string
|
||||
hasNotification: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e:'search'): void
|
||||
(e:'refresh'): void
|
||||
(e:'notify'): void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.header{
|
||||
height: 56px;
|
||||
background:#fff;
|
||||
border-bottom: 1px solid #eef2f7;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px;
|
||||
}
|
||||
.crumb{ color:#374151; font-size:14px; }
|
||||
|
||||
.header-right{ display:flex; align-items:center; gap: 10px; }
|
||||
.hbtn{
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 8px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
background:#f6f7fb;
|
||||
position: relative;
|
||||
}
|
||||
.dot{
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background:#ff4d4f;
|
||||
position:absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
}
|
||||
</style>
|
||||
123
layouts/admin/components/AdminSubsider.uvue
Normal file
123
layouts/admin/components/AdminSubsider.uvue
Normal file
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<view class="sub-sider" v-if="groups && groups.length > 0">
|
||||
<view class="sub-header">
|
||||
<text class="sub-title">{{ activeMenuTitle }}</text>
|
||||
<view class="sub-collapse" @click="collapsed = !collapsed">
|
||||
<text class="sub-collapse-text">{{ collapsed ? '›' : '‹' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view class="sub-body" scroll-y="true">
|
||||
<view v-for="(g, gi) in groups" :key="gi" class="group">
|
||||
<view class="group-title" @click="toggleGroup(g.title)">
|
||||
<text class="group-title-text">{{ g.title }}</text>
|
||||
<text class="group-arrow">{{ isGroupOpen(g.title) ? '˄' : '˅' }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="isGroupOpen(g.title)">
|
||||
<view
|
||||
v-for="c in g.children"
|
||||
:key="c.id"
|
||||
class="sub-item"
|
||||
:class="{ active: activeSubId === c.id }"
|
||||
@click="$emit('sub-click', c)"
|
||||
>
|
||||
<text class="sub-item-text" :class="{ activeText: activeSubId === c.id }">
|
||||
{{ c.title }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
import type { MenuGroup, MenuChild } from '../types.uts'
|
||||
|
||||
const props = defineProps<{
|
||||
activeMenuTitle: string
|
||||
groups: MenuGroup[]
|
||||
activeSubId: string
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e:'sub-click', child: MenuChild): void
|
||||
}>()
|
||||
|
||||
const collapsed = ref(false)
|
||||
const openGroups = ref<string[]>([])
|
||||
|
||||
const isGroupOpen = (title: string): boolean => {
|
||||
// 默认展开第一个分组 + 当前高亮分组(简单策略)
|
||||
if (openGroups.value.length === 0 && props.groups && props.groups.length > 0) return props.groups[0].title === title
|
||||
return openGroups.value.includes(title)
|
||||
}
|
||||
|
||||
const toggleGroup = (title: string) => {
|
||||
if (openGroups.value.includes(title)) {
|
||||
openGroups.value = openGroups.value.filter(t => t !== title)
|
||||
} else {
|
||||
openGroups.value = [...openGroups.value, title]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.sub-sider{
|
||||
width: 240px;
|
||||
background:#ffffff;
|
||||
border-right: 1px solid #e5e7eb;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 96px; /* 紧贴主侧边栏 */
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 900;
|
||||
}
|
||||
.sub-header{
|
||||
height: 56px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid #eef2f7;
|
||||
}
|
||||
.sub-title{ font-size:16px; font-weight:600; color:#111827; }
|
||||
.sub-collapse{ width:28px; height:28px; display:flex; align-items:center; justify-content:center; }
|
||||
.sub-collapse-text{ color:#6b7280; }
|
||||
|
||||
.sub-body{ height: calc(100vh - 56px); }
|
||||
|
||||
.group{ padding: 8px 0; }
|
||||
.group-title{
|
||||
height: 44px;
|
||||
padding: 0 16px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content: space-between;
|
||||
color:#111827;
|
||||
}
|
||||
.group-title-text{ font-size:15px; font-weight:600; }
|
||||
.group-arrow{ color:#6b7280; }
|
||||
|
||||
.sub-item{
|
||||
height: 44px;
|
||||
padding: 0 16px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
background: transparent;
|
||||
}
|
||||
.sub-item.active{
|
||||
background: #eaf2ff;
|
||||
}
|
||||
.sub-item-text{
|
||||
font-size:14px;
|
||||
color:#111827;
|
||||
}
|
||||
.sub-item-text.activeText{
|
||||
color:#1677ff;
|
||||
font-weight:600;
|
||||
}
|
||||
</style>
|
||||
76
layouts/admin/components/AdminTagsView.uvue
Normal file
76
layouts/admin/components/AdminTagsView.uvue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<view class="tags">
|
||||
<scroll-view class="tags-scroll" scroll-x="true" show-scrollbar="false">
|
||||
<view class="tags-row">
|
||||
<view
|
||||
v-for="t in tabs"
|
||||
:key="t.id"
|
||||
class="tag"
|
||||
:class="{ active: activeTabId === t.id }"
|
||||
@click="$emit('tab-click', t)"
|
||||
>
|
||||
<text class="tag-text">{{ t.title }}</text>
|
||||
<view class="tag-close" @click.stop="$emit('tab-close', t.id)">
|
||||
<text class="tag-close-text">×</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import type { TabItem } from '../types.uts'
|
||||
|
||||
defineProps<{
|
||||
tabs: TabItem[]
|
||||
activeTabId: string
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
(e:'tab-click', tab: TabItem): void
|
||||
(e:'tab-close', tabId: string): void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tags{
|
||||
height: 44px;
|
||||
background:#fff;
|
||||
border-bottom: 1px solid #eef2f7;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
}
|
||||
.tags-scroll{ width: 100%; height: 44px; }
|
||||
.tags-row{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap: 8px;
|
||||
padding: 0 12px;
|
||||
height: 44px;
|
||||
}
|
||||
.tag{
|
||||
height: 30px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #e5e7eb;
|
||||
background:#fff;
|
||||
border-radius: 6px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap: 8px;
|
||||
}
|
||||
.tag.active{
|
||||
border-color:#1677ff;
|
||||
background:#eaf2ff;
|
||||
}
|
||||
.tag-text{ font-size:12px; color:#374151; }
|
||||
.tag-close{
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 8px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:center;
|
||||
}
|
||||
.tag-close-text{ font-size:14px; color:#6b7280; line-height:14px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user