feat: 补全赛事管理下所有页面 补全系统管理下所有页面
This commit is contained in:
@@ -32,6 +32,71 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 面包屑
|
||||
.breadcrumb {
|
||||
flex: 1;
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
|
||||
:global {
|
||||
.ant-breadcrumb-link,
|
||||
.ant-breadcrumb-separator,
|
||||
.ant-breadcrumb-item a {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.ant-breadcrumb-item:last-child {
|
||||
.ant-breadcrumb-link,
|
||||
.ant-breadcrumb-separator,
|
||||
a {
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
font-weight: 500;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Header 右侧:账号信息 + 退出图标
|
||||
.headerRight {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.userPhone {
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.logoutIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
opacity: 0.45;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉菜单项:图标 + 文字
|
||||
.menuItem {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
:global(.anticon) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.menuItemDanger {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 16px;
|
||||
border-radius: 8px;
|
||||
|
||||
+107
-1
@@ -1,11 +1,13 @@
|
||||
import { computed, defineComponent, h, ref } from 'vue';
|
||||
import type { CSSProperties } from 'vue';
|
||||
import { useRouter, useRoute, RouterView } from 'vue-router';
|
||||
import { Layout, Menu } from 'ant-design-vue';
|
||||
import { Layout, Menu, Modal, Breadcrumb, Dropdown, message } from 'ant-design-vue';
|
||||
import type { MenuProps } from 'ant-design-vue';
|
||||
import {
|
||||
DashboardOutlined,
|
||||
EditOutlined,
|
||||
InfoCircleOutlined,
|
||||
LogoutOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
SettingOutlined,
|
||||
@@ -13,6 +15,10 @@ import {
|
||||
TrophyOutlined,
|
||||
} from '@ant-design/icons-vue';
|
||||
import { useMenuStore } from '@/stores/menuStore';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
import { useState, useBreadcrumb, auth } from '@/hooks';
|
||||
import { ChangePasswordModal } from '@/components';
|
||||
import logoutSvg from '@/assets/img/icon/logout.svg';
|
||||
import styles from './BasicLayout.module.less';
|
||||
|
||||
const { Header, Sider, Content, Footer } = Layout;
|
||||
@@ -86,6 +92,8 @@ export default defineComponent({
|
||||
const collapsed = ref(false);
|
||||
|
||||
const { menuItems } = useMenuStore();
|
||||
const { phone } = useUserStore();
|
||||
const { breadcrumbs } = useBreadcrumb();
|
||||
|
||||
const selectedKeys = computed<string[]>(() => [route.path]);
|
||||
|
||||
@@ -99,6 +107,53 @@ export default defineComponent({
|
||||
collapsed.value = !collapsed.value;
|
||||
};
|
||||
|
||||
/** ===== 退出登录 ===== */
|
||||
const handleLogout = () => {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '是否退出登录?',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
const { clearUser } = useUserStore();
|
||||
clearUser();
|
||||
auth.logout();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/** ===== 修改密码弹窗 ===== */
|
||||
const [changePwdVisible, setChangePwdVisible] = useState<boolean>(false);
|
||||
const [changePwdSubmitting, setChangePwdSubmitting] = useState<boolean>(false);
|
||||
|
||||
const handleChangePassword = () => {
|
||||
setChangePwdVisible(true);
|
||||
};
|
||||
|
||||
const handleChangePwdSave = async (payload: { newPassword: string }) => {
|
||||
if (changePwdSubmitting.value) return;
|
||||
setChangePwdSubmitting(true);
|
||||
try {
|
||||
// TODO: 替换为真实 API 调用
|
||||
console.log('修改密码:', payload);
|
||||
message.success('修改成功');
|
||||
setChangePwdVisible(false);
|
||||
} catch (err: any) {
|
||||
message.error(err?.msg || '修改失败');
|
||||
} finally {
|
||||
setChangePwdSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
/** ===== 头像下拉菜单:修改密码 / 退出登录 ===== */
|
||||
const handleUserMenuClick: MenuProps['onClick'] = ({ key }) => {
|
||||
if (key === 'changePassword') {
|
||||
handleChangePassword();
|
||||
} else if (key === 'logout') {
|
||||
handleLogout();
|
||||
}
|
||||
};
|
||||
|
||||
return () => (
|
||||
<Layout class={styles.container}>
|
||||
<Sider style={siderStyle} collapsed={collapsed.value} trigger={null} collapsible>
|
||||
@@ -119,6 +174,49 @@ export default defineComponent({
|
||||
<span class={styles.trigger} onClick={toggleCollapsed}>
|
||||
{collapsed.value ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
</span>
|
||||
|
||||
{/* 面包屑:紧跟在折叠按钮之后 */}
|
||||
<Breadcrumb separator=">" class={styles.breadcrumb}>
|
||||
{breadcrumbs.value.map((item, index) => {
|
||||
const isLast = index === breadcrumbs.value.length - 1;
|
||||
return (
|
||||
<Breadcrumb.Item key={item.path || index}>
|
||||
{isLast || !item.path ? (
|
||||
<span>{item.title}</span>
|
||||
) : (
|
||||
<a onClick={() => router.push(item.path as string)}>{item.title}</a>
|
||||
)}
|
||||
</Breadcrumb.Item>
|
||||
);
|
||||
})}
|
||||
</Breadcrumb>
|
||||
|
||||
{/* 右侧: 账号信息 + 退出 */}
|
||||
<div class={styles.headerRight}>
|
||||
<span class={styles.userPhone}>{phone.value}</span>
|
||||
<Dropdown trigger={['hover']} placement="bottomLeft">
|
||||
{{
|
||||
default: () => <img src={logoutSvg} class={styles.logoutIcon} />,
|
||||
overlay: () => (
|
||||
<Menu onClick={handleUserMenuClick}>
|
||||
<Menu.Item key="changePassword">
|
||||
<span class={styles.menuItem}>
|
||||
<EditOutlined />
|
||||
修改密码
|
||||
</span>
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item key="logout">
|
||||
<span class={`${styles.menuItem} ${styles.menuItemDanger}`}>
|
||||
<LogoutOutlined />
|
||||
退出登录
|
||||
</span>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
),
|
||||
}}
|
||||
</Dropdown>
|
||||
</div>
|
||||
</Header>
|
||||
|
||||
<Content class={styles.content}>
|
||||
@@ -127,6 +225,14 @@ export default defineComponent({
|
||||
|
||||
<Footer style={footerStyle}>六个羽友赛事运营平台 ©{new Date().getFullYear()}</Footer>
|
||||
</Layout>
|
||||
|
||||
{/* 修改密码弹窗 */}
|
||||
<ChangePasswordModal
|
||||
visible={changePwdVisible.value}
|
||||
submitting={changePwdSubmitting.value}
|
||||
onClose={() => setChangePwdVisible(false)}
|
||||
onSave={handleChangePwdSave}
|
||||
/>
|
||||
</Layout>
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user