Generating commit message...

This commit is contained in:
2025-08-30 14:33:49 +08:00
parent 4d469e95f0
commit 7f9bfbb381
99 changed files with 69225 additions and 35 deletions

View File

@@ -0,0 +1,350 @@
<template>
<a-layout class="main-layout">
<!-- 侧边栏 -->
<a-layout-sider
v-model:collapsed="collapsed"
:trigger="null"
collapsible
:width="240"
class="layout-sider"
>
<div class="logo">
<img src="@/assets/logo.png" alt="Logo" v-if="!collapsed" />
<h1 v-if="!collapsed">结伴客管理</h1>
<span v-else>JK</span>
</div>
<a-menu
v-model:selectedKeys="selectedKeys"
theme="dark"
mode="inline"
:inline-collapsed="collapsed"
>
<a-menu-item key="dashboard">
<template #icon>
<DashboardOutlined />
</template>
<span>仪表板</span>
<router-link to="/dashboard" />
</a-menu-item>
<a-menu-item key="users">
<template #icon>
<UserOutlined />
</template>
<span>用户管理</span>
<router-link to="/users" />
</a-menu-item>
<a-menu-item key="merchants">
<template #icon>
<ShopOutlined />
</template>
<span>商家管理</span>
<router-link to="/merchants" />
</a-menu-item>
<a-menu-item key="travel">
<template #icon>
<CompassOutlined />
</template>
<span>旅行管理</span>
<router-link to="/travel" />
</a-menu-item>
<a-menu-item key="animals">
<template #icon>
<HeartOutlined />
</template>
<span>动物管理</span>
<router-link to="/animals" />
</a-menu-item>
<a-menu-item key="orders">
<template #icon>
<ShoppingCartOutlined />
</template>
<span>订单管理</span>
<router-link to="/orders" />
</a-menu-item>
<a-menu-item key="promotion">
<template #icon>
<GiftOutlined />
</template>
<span>推广管理</span>
<router-link to="/promotion" />
</a-menu-item>
<a-menu-item key="system">
<template #icon>
<SettingOutlined />
</template>
<span>系统设置</span>
<router-link to="/system" />
</a-menu-item>
</a-menu>
</a-layout-sider>
<!-- 主内容区 -->
<a-layout>
<!-- 顶部导航 -->
<a-layout-header class="layout-header">
<div class="header-left">
<menu-unfold-outlined
v-if="collapsed"
class="trigger"
@click="() => (collapsed = !collapsed)"
/>
<menu-fold-outlined
v-else
class="trigger"
@click="() => (collapsed = !collapsed)"
/>
<a-breadcrumb class="breadcrumb">
<a-breadcrumb-item>首页</a-breadcrumb-item>
<a-breadcrumb-item>{{ currentRouteMeta.title }}</a-breadcrumb-item>
</a-breadcrumb>
</div>
<div class="header-right">
<a-space :size="16">
<a-tooltip title="消息">
<a-badge :count="5" dot>
<bell-outlined class="header-icon" />
</a-badge>
</a-tooltip>
<a-tooltip title="帮助文档">
<question-circle-outlined class="header-icon" />
</a-tooltip>
<a-dropdown :trigger="['click']">
<a-avatar
size="small"
:src="userAvatar"
class="avatar"
/>
<template #overlay>
<a-menu>
<a-menu-item>
<user-outlined />
<span>个人中心</span>
</a-menu-item>
<a-menu-item>
<setting-outlined />
<span>账户设置</span>
</a-menu-item>
<a-menu-divider />
<a-menu-item @click="handleLogout">
<logout-outlined />
<span>退出登录</span>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
<span class="username">{{ userName }}</span>
</a-space>
</div>
</a-layout-header>
<!-- 页面内容 -->
<a-layout-content class="layout-content">
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</a-layout-content>
</a-layout>
</a-layout>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useAppStore } from '@/stores/app'
import { message, Modal } from 'ant-design-vue'
import {
DashboardOutlined,
UserOutlined,
ShopOutlined,
CompassOutlined,
HeartOutlined,
ShoppingCartOutlined,
GiftOutlined,
SettingOutlined,
MenuUnfoldOutlined,
MenuFoldOutlined,
BellOutlined,
QuestionCircleOutlined,
LogoutOutlined
} from '@ant-design/icons-vue'
const router = useRouter()
const route = useRoute()
const appStore = useAppStore()
const collapsed = ref(false)
const selectedKeys = ref<string[]>(['dashboard'])
// 计算属性
const currentRouteMeta = computed(() => route.meta || {})
const userName = computed(() => appStore.state.user?.nickname || '管理员')
const userAvatar = computed(() => appStore.state.user?.avatar || 'https://api.dicebear.com/7.x/miniavs/svg?seed=admin')
// 监听路由变化
router.afterEach((to) => {
selectedKeys.value = [to.name as string]
})
// 退出登录
const handleLogout = () => {
Modal.confirm({
title: '确认退出',
content: '您确定要退出登录吗?',
okText: '确定',
cancelText: '取消',
onOk() {
appStore.logout()
message.success('退出成功')
router.push('/login')
}
})
}
</script>
<style scoped lang="less">
.main-layout {
min-height: 100vh;
}
.layout-sider {
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
z-index: 10;
.logo {
height: 64px;
padding: 16px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.1);
margin: 16px;
border-radius: 6px;
img {
height: 32px;
margin-right: 8px;
}
h1 {
color: white;
font-size: 16px;
font-weight: 600;
margin: 0;
}
span {
color: white;
font-size: 18px;
font-weight: bold;
}
}
:deep(.ant-menu-dark) {
background: transparent;
}
:deep(.ant-menu-item) {
margin: 4px 0;
border-radius: 6px;
&.ant-menu-item-selected {
background: #1890ff;
}
}
}
.layout-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
background: white;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.header-left {
display: flex;
align-items: center;
.trigger {
font-size: 18px;
cursor: pointer;
margin-right: 16px;
color: #666;
&:hover {
color: #1890ff;
}
}
.breadcrumb {
margin-bottom: 0;
}
}
.header-right {
.header-icon {
font-size: 16px;
color: #666;
cursor: pointer;
&:hover {
color: #1890ff;
}
}
.avatar {
cursor: pointer;
background: #1890ff;
}
.username {
font-weight: 500;
color: #333;
}
}
}
.layout-content {
padding: 24px;
background: #f5f5f5;
min-height: calc(100vh - 64px);
overflow: auto;
}
// 响应式设计
@media (max-width: 768px) {
.layout-header {
padding: 0 16px;
.header-left {
.breadcrumb {
display: none;
}
}
.header-right {
.username {
display: none;
}
}
}
.layout-content {
padding: 16px;
}
}
</style>