页面顶部导航栏(Navbar)的功能(Navbar/index.vue)
这段代码是一个 Vue.js 组件,实现了页面顶部导航栏(Navbar)的功能。我将分块分析它的各个部分:
模板 (Template):
<!-- spid-admin/src/layout/components/Navbar/index.vue -->
<template><div class="navbar"><hamburgerid="hamburger-container":is-active="sidebar.opened"class="hamburger-container"@toggleClick="toggleSideBar"/><breadcrumbid="breadcrumb-container"class="breadcrumb-container"/><!-- <lang-select /> --><div class="right-menu"><template v-if="device!=='mobile'"><div style="position: relative; display: inline-block; padding: 0 8px; height: 100%; color: #5a5e66; vertical-align: text-bottom; cursor: pointer;" @click="handleToNotice"><el-image :src="noticePic" style="width: 35px; height: 35px; position: relative; top: 7px;" /></div><div v-if="isVip" style="position: relative; display: inline-block; padding: 0 4px; height: 100%; color: #5a5e66; vertical-align: text-bottom; cursor: pointer;" @click="handleToMission"><el-image :src="systemTaskPic" style="width: 35px; height: 35px; position: relative; top: 7px;" /></div><!-- <header-search class="right-menu-item" /> --><!-- <error-log class="errLog-container right-menu-item hover-effect" /> --><screenfull class="right-menu-item hover-effect" /><!-- <el-tooltip:content="$t('navbar.size')"effect="dark"placement="bottom"><size-select class="right-menu-item hover-effect" /></el-tooltip> --><!-- <lang-select class="right-menu-item hover-effect" /> --><span class="username">{{ username }}</span></template><el-dropdownclass="avatar-container right-menu-item hover-effect"trigger="click"><div class="avatar-wrapper"><imgv-if="avatar":src="oss + avatar"class="user-avatar"><imgv-else:src="require('@/assets/images/logo.jpg')"class="user-avatar"><i class="el-icon-caret-bottom" /></div><el-dropdown-menu slot="dropdown"><router-link to="/"><el-dropdown-item>{{ $t('navbar.dashboard') }}</el-dropdown-item></router-link><router-link to="/setting/profile/"><el-dropdown-item>{{ $t('navbar.profile') }}</el-dropdown-item></router-link><router-link to="/subscribe/list/"><el-dropdown-item>订阅续期</el-dropdown-item></router-link><router-link to="/account/child/"><el-dropdown-item>账号分身</el-dropdown-item></router-link><el-dropdown-itemdivided@click.native="logout"><span style="display:block;">{{ $t('navbar.logOut') }}</span></el-dropdown-item></el-dropdown-menu></el-dropdown></div></div>
</template>
navbar: 导航栏的容器。hamburger: 汉堡按钮,用于切换侧边栏的显示和隐藏,@toggleClick="toggleSideBar"点击事件触发toggleSideBar方法。breadcrumb: 面包屑导航。right-menu: 右侧菜单区域。v-if="device!=='mobile'": 判断当前设备是否为移动设备,如果是,则不显示下列内容。- 通知图标: 使用
el-image显示通知图标,@click="handleToNotice"点击事件触发handleToNotice方法,跳转到通知列表。
- 通知图标: 使用
- 任务图标: 使用
el-image显示任务图标,v-if="isVip"判断用户是否是vip,如果是vip则显示任务图标。@click="handleToMission"点击事件触发handleToMission方法,跳转到任务列表。
*screenfull: 全屏切换按钮。
*username: 显示用户名。 el-dropdown: 下拉菜单,用于显示用户头像和一些操作选项。avatar-wrapper: 头像容器。v-if="avatar": 判断用户是否有头像, 如果有头像,则拼接oss和avatar作为头像的url, 否则使用默认的logo作为头像el-dropdown-menu: 下拉菜单项。router-link: 使用router-link组件创建路由链接。el-dropdown-item: 下拉菜单项。@click.native="logout": 点击退出登录按钮,触发logout方法。
脚本 (Script):
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import { AppModule } from '@/store/modules/app'
import { UserModule } from '@/store/modules/user'
import Breadcrumb from '@/components/Breadcrumb/index.vue'
import ErrorLog from '@/components/ErrorLog/index.vue'
import Hamburger from '@/components/Hamburger/index.vue'
import HeaderSearch from '@/components/HeaderSearch/index.vue'
import LangSelect from '@/components/LangSelect/index.vue'
import Screenfull from '@/components/Screenfull/index.vue'
import SizeSelect from '@/components/SizeSelect/index.vue'@Component({name: 'Navbar',components: {Breadcrumb,ErrorLog,Hamburger,HeaderSearch,LangSelect,Screenfull,SizeSelect}
})
export default class extends Vue {get sidebar() {return AppModule.sidebar}get device() {return AppModule.device.toString()}get avatar() {return UserModule.avatar}get username() {return UserModule.username}get oss() {return AppModule.oss}get noticeNum() {// console.log('改变触发')// console.log(AppModule.notices)return AppModule.notices}@Watch('noticeNum')watchV(v: any) {this.setData()}public role = UserModule.roles[0]public isVip: boolean = JSON.parse(UserModule.token).vippublic noticePic = require(`@/assets/images/notice-0.png`)public systemTaskPic = require(`@/assets/images/system-task.png`)private toggleSideBar() {AppModule.ToggleSideBar(false)}private async logout() {await UserModule.LogOut()this.$router.push(`/login?redirect=${this.$route.fullPath}`)}private handleToNotice() {this.$router.push(`/notice/list`)}private handleToMission() {if (this.role === 'ROLE_SUPER') {this.$router.push(`/mission/super-list`)} else {this.$router.push(`/mission/company-list`)}}private async setData() {const num = parseInt(this.noticeNum)this.noticePic = require(`@/assets/images/notice-${!num ? '0' : num > 9 ? 'n' : num}.png`)}created() {this.setData()}
}
</script>
- 引入依赖: 引入
vue-property-decorator,AppModule,UserModule, 以及各种组件。 - 组件定义: 使用
@Component装饰器定义组件,名称为Navbar。 - 计算属性:
sidebar: 获取AppModule中的侧边栏状态。device: 获取AppModule中的设备类型。avatar: 获取UserModule中的用户头像。username: 获取UserModule中的用户名。oss: 获取AppModule中的 OSS 地址前缀。noticeNum: 获取AppModule中的通知数量。
@Watch('noticeNum'): 监听noticeNum的变化,如果发生变化,则调用setData方法。- 数据属性:
role: 获取UserModule中的用户角色。isVip: 获取UserModule中的用户是否是 VIP。noticePic: 通知图标的路径。systemTaskPic: 任务图标的路径。
- 方法:
toggleSideBar(): 调用AppModule.ToggleSideBar()方法切换侧边栏的显示和隐藏。logout(): 调用UserModule.LogOut()方法退出登录,并跳转到登录页。handleToNotice(): 跳转到通知列表页。handleToMission(): 根据用户角色跳转到对应的任务列表页。setData(): 根据通知数量设置noticePic的路径。
- 生命周期钩子:
created(): 调用setData()方法,初始化通知图标。
样式 (Style):
<style lang="scss" scoped>
.navbar {// ... 导航栏样式
}
</style>
navbar: 导航栏容器的样式,设置高度、背景颜色、阴影等。hamburger-container: 汉堡按钮容器的样式,设置高度、浮动、内边距、光标等。breadcrumb-container: 面包屑导航容器的样式,设置浮动。errLog-container: 错误日志容器的样式,设置display: inline-block和vertical-align: top。right-menu: 右侧菜单容器的样式,设置浮动、高度、行高。right-menu-item: 右侧菜单项的样式,设置display: inline-block、内边距、高度、字体大小、颜色等。hover-effect: 鼠标悬停效果,设置光标和过渡效果。avatar-container: 头像容器的样式,设置外边距。avatar-wrapper: 头像包装器的样式,设置外边距和相对定位。user-avatar: 用户头像的样式,设置光标、宽度、高度、圆角。el-icon-caret-bottom: 下拉图标的样式,设置光标、绝对定位、字体大小。username: 用户名的样式,设置display: inline-block、vertical-align: text-bottom、内边距、颜色、字体大小和鼠标悬停效果。
总结:
这段代码实现了一个功能完备的页面顶部导航栏,包括:
- 汉堡按钮
- 面包屑导航
- 通知图标
- 系统任务图标 (VIP用户专属)
- 全屏切换按钮
- 用户名显示
- 用户头像下拉菜单(包含常用操作和退出登录)
代码结构清晰,使用了 Vuex 管理状态,并使用了 vue-property-decorator 简化组件编写。
可以改进的地方:
- 组件拆分: 可以将右侧菜单区域拆分成更小的组件,例如将通知图标、任务图标、用户名和头像下拉菜单拆分成独立的组件,提高代码的可复用性和可维护性。
- 国际化: 可以将硬编码的中文文本替换成国际化资源,例如“订阅续期”、“账号分身”等。
- 错误处理: 可以添加更详细的错误处理,例如在
logout方法中捕获错误并提示用户。 - 代码注释: 可以添加更详细的代码注释,方便其他开发人员理解代码的逻辑。
<!-- spid-admin/src/layout/components/Navbar/index.vue -->
<template><div class="navbar"><hamburgerid="hamburger-container":is-active="sidebar.opened"class="hamburger-container"@toggleClick="toggleSideBar"/><breadcrumbid="breadcrumb-container"class="breadcrumb-container"/><!-- <lang-select /> --><div class="right-menu"><template v-if="device!=='mobile'"><div style="position: relative; display: inline-block; padding: 0 8px; height: 100%; color: #5a5e66; vertical-align: text-bottom; cursor: pointer;" @click="handleToNotice"><el-image :src="noticePic" style="width: 35px; height: 35px; position: relative; top: 7px;" /></div><div v-if="isVip" style="position: relative; display: inline-block; padding: 0 4px; height: 100%; color: #5a5e66; vertical-align: text-bottom; cursor: pointer;" @click="handleToMission"><el-image :src="systemTaskPic" style="width: 35px; height: 35px; position: relative; top: 7px;" /></div><!-- <header-search class="right-menu-item" /> --><!-- <error-log class="errLog-container right-menu-item hover-effect" /> --><screenfull class="right-menu-item hover-effect" /><!-- <el-tooltip:content="$t('navbar.size')"effect="dark"placement="bottom"><size-select class="right-menu-item hover-effect" /></el-tooltip> --><!-- <lang-select class="right-menu-item hover-effect" /> --><span class="username">{{ username }}</span></template><el-dropdownclass="avatar-container right-menu-item hover-effect"trigger="click"><div class="avatar-wrapper"><imgv-if="avatar":src="oss + avatar"class="user-avatar"><imgv-else:src="require('@/assets/images/logo.jpg')"class="user-avatar"><i class="el-icon-caret-bottom" /></div><el-dropdown-menu slot="dropdown"><router-link to="/"><el-dropdown-item>{{ $t('navbar.dashboard') }}</el-dropdown-item></router-link><router-link to="/setting/profile/"><el-dropdown-item>{{ $t('navbar.profile') }}</el-dropdown-item></router-link><router-link to="/subscribe/list/"><el-dropdown-item>订阅续期</el-dropdown-item></router-link><router-link to="/account/child/"><el-dropdown-item>账号分身</el-dropdown-item></router-link><el-dropdown-itemdivided@click.native="logout"><span style="display:block;">{{ $t('navbar.logOut') }}</span></el-dropdown-item></el-dropdown-menu></el-dropdown></div></div>
</template><script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import { AppModule } from '@/store/modules/app'
import { UserModule } from '@/store/modules/user'
import Breadcrumb from '@/components/Breadcrumb/index.vue'
import ErrorLog from '@/components/ErrorLog/index.vue'
import Hamburger from '@/components/Hamburger/index.vue'
import HeaderSearch from '@/components/HeaderSearch/index.vue'
import LangSelect from '@/components/LangSelect/index.vue'
import Screenfull from '@/components/Screenfull/index.vue'
import SizeSelect from '@/components/SizeSelect/index.vue'@Component({name: 'Navbar',components: {Breadcrumb,ErrorLog,Hamburger,HeaderSearch,LangSelect,Screenfull,SizeSelect}
})
export default class extends Vue {get sidebar() {return AppModule.sidebar}get device() {return AppModule.device.toString()}get avatar() {return UserModule.avatar}get username() {return UserModule.username}get oss() {return AppModule.oss}get noticeNum() {// console.log('改变触发')// console.log(AppModule.notices)return AppModule.notices}@Watch('noticeNum')watchV(v: any) {this.setData()}public role = UserModule.roles[0]public isVip: boolean = JSON.parse(UserModule.token).vippublic noticePic = require(`@/assets/images/notice-0.png`)public systemTaskPic = require(`@/assets/images/system-task.png`)private toggleSideBar() {AppModule.ToggleSideBar(false)}private async logout() {await UserModule.LogOut()this.$router.push(`/login?redirect=${this.$route.fullPath}`)}private handleToNotice() {this.$router.push(`/notice/list`)}private handleToMission() {if (this.role === 'ROLE_SUPER') {this.$router.push(`/mission/super-list`)} else {this.$router.push(`/mission/company-list`)}}private async setData() {const num = parseInt(this.noticeNum)this.noticePic = require(`@/assets/images/notice-${!num ? '0' : num > 9 ? 'n' : num}.png`)}created() {this.setData()}
}
</script><style lang="scss" scoped>
.navbar {height: 50px;overflow: hidden;position: relative;background: #fff;box-shadow: 0 1px 4px rgba(0,21,41,.08);.hamburger-container {line-height: 46px;height: 100%;float: left;padding: 0 15px;cursor: pointer;transition: background .3s;-webkit-tap-highlight-color:transparent;&:hover {background: rgba(0, 0, 0, .025)}}.breadcrumb-container {float: left;}.errLog-container {display: inline-block;vertical-align: top;}.right-menu {float: right;height: 100%;line-height: 50px;&:focus {outline: none;}.right-menu-item {display: inline-block;padding: 0 8px;height: 100%;font-size: 22px;color: #5a5e66;vertical-align: text-bottom;&.hover-effect {cursor: pointer;transition: background .3s;&:hover {background: rgba(0, 0, 0, .025)}}}.avatar-container {margin-right: 30px;.avatar-wrapper {margin-top: 5px;position: relative;.user-avatar {cursor: pointer;width: 40px;height: 40px;border-radius: 10px;}.el-icon-caret-bottom {cursor: pointer;position: absolute;right: -20px;top: 25px;font-size: 12px;}}}}.username {display: inline-block;vertical-align: text-bottom;padding: 0 8px;color: #1890ff;font-size: 12px;&:hover {color: #46a6ff;}}
}
</style>

相关文章:
页面顶部导航栏(Navbar)的功能(Navbar/index.vue)
这段代码是一个 Vue.js 组件,实现了页面顶部导航栏(Navbar)的功能。我将分块分析它的各个部分: 模板 (Template): <!-- spid-admin/src/layout/components/Navbar/index.vue --> <template><div class"navb…...
thinnkphp5.1和 thinkphp6以及nginx,apache 解决跨域问题
ThinkPHP 5.1 使用中间件设置响应头 ThinkPHP 5.1 及以上版本支持中间件,可以通过中间件统一设置跨域响应头。 步骤: 创建一个中间件文件,例如 CorsMiddleware.php: namespace app\middleware;class CorsMiddleware {public fu…...
vue2新增删除
(只是页面实现,不涉及数据库) list组件: <button click"onAdd">新增</button><el-table:header-cell-style"{ textAlign: center }" :cell-style"{ textAlign: center }":data&quo…...
测试ip端口-telnet开启与使用
前言 开发过程中我们总会要去测试ip通不通,或者ip下某个端口是否可以联通,为此我们可以使用telnet 命令来实现。 一、telnet 开启 可能有些人使用telnet报错,不是内部命令,可以如下开启: 1、打开控制面板ÿ…...
Python爬虫基础——XPath表达式
首先说一下这节内容在学习过程中存在的问题吧,在爬取百度网页文字时,出现了问题,就是通过表达式在网页搜索中可以定位,但是通过代码无法定位,请教了一位老师,他说是动态链接,目前这部分内容比较…...
ansible-性能优化
一. 简述: 搞过运维自动化工具的人,肯定会发现很多运维伙伴们经常用saltstack和ansible做比较,单从执行效率上来说,ansible确实比不上saltstack(ansible使用的是ssh,salt使用的是zeromq消息队列[暂没深入了解]),但其实…...
高等数学学习笔记 ☞ 一元函数微分的基础知识
1. 微分的定义 (1)定义:设函数在点的某领域内有定义,取附近的点,对应的函数值分别为和, 令,若可以表示成,则称函数在点是可微的。 【 若函数在点是可微的,则可以表达为】…...
前后端实现防抖节流实现
在前端和 Java 后端中实现防抖(Debounce)和节流(Throttle)主要用于减少频繁请求或事件触发对系统的压力。前端和后端的实现方式有些不同,以下是两种方法的具体实现: 1. 前端实现防抖和节流 在前端中&…...
【笔记】算法记录
1、求一个数的素因子(试除法) // 获取一个数的所有素因子 set<int> getPrimeFactors(int num) {set<int> primeFactors;for (int i 2; i * i < num; i) {while (num % i 0) {primeFactors.insert(i);num / i;}}if (num > 1) {prime…...
【网络云SRE运维开发】2025第2周-每日【2025/01/08】小测-【第8章 STP生成树协议】理论和实操解析
文章目录 一、选择题二、理论题三、实操题 【网络云SRE运维开发】2025第2周-每日【2025/01/08】小测-【第8章 STP生成树协议】理论和实操解析 一、选择题 生成树协议的主要作用是 B. 防止网络环路解释:生成树协议(STP)的主要目的是防止网络中…...
git push -f 指定分支
要将本地代码推送到指定的远程分支,你可以使用以下步骤和命令: 确认远程仓库: 确保你的本地仓库已经与远程仓库关联。你可以使用以下命令查看当前的远程仓库状态: git remote -v查看本地分支: 使用命令查看当前存在的本…...
CTF知识点总结(二)
异或注入:两个条件相同(同真或同假)即为假。 http://120.24.86.145:9004/1ndex.php?id1^(length(union)!0)-- 如上,如果union被过滤,则 length(union)!0 为假,那么返回页面正常。 2|0updatexml() 函数报…...
解决Edge打开PDF总是没有焦点
【问题描述】 使用Edge浏览器作为默认PDF阅读器打开本地PDF文件,Edge窗口总是不获得焦点,而是在任务栏以橙色显示,需要再手动点击一次才能查看文件内容。 本强迫症来治一治这个问题! 【解决方法】 GPT老师指出问题出在Edge的启动…...
69.基于SpringBoot + Vue实现的前后端分离-家乡特色推荐系统(项目 + 论文PPT)
项目介绍 在Internet高速发展的今天,我们生活的各个领域都涉及到计算机的应用,其中包括家乡特色推荐的网络应用,在外国家乡特色推荐系统已经是很普遍的方式,不过国内的管理网站可能还处于起步阶段。家乡特色推荐系统采用java技术&…...
计算机视觉目标检测-DETR网络
目录 摘要abstractDETR目标检测网络详解二分图匹配和损失函数 DETR总结总结 摘要 DETR(DEtection TRansformer)是由Facebook AI提出的一种基于Transformer架构的端到端目标检测方法。它通过将目标检测建模为集合预测问题,摒弃了锚框设计和非…...
《自动驾驶与机器人中的SLAM技术》ch1:自动驾驶
目录 1.1 自动驾驶技术 1.2 自动驾驶中的定位与地图 1.1 自动驾驶技术 1.2 自动驾驶中的定位与地图 L2 在技术实现上会更倾向于实时感知,乃至可以使用感知结果直接构建鸟瞰图(bird eye view, BEV),而 L4 则依赖离线地图。 高精地…...
【UE5 C++课程系列笔记】23——多线程基础——AsyncTask
目录 概念 函数说明 注意事项 (1)线程安全问题 (2)依赖特定线程执行的任务限制 (3)任务执行顺序和时间不确定性 使用示例 概念 AsyncTask 允许开发者将一个函数或者一段代码逻辑提交到特定的线程去执…...
基于Python的音乐播放器 毕业设计-附源码73733
摘 要 本项目基于Python开发了一款简单而功能强大的音乐播放器。通过该音乐播放器,用户可以轻松管理自己的音乐库,播放喜爱的音乐,并享受音乐带来的愉悦体验。 首先,我们使用Python语言结合相关库开发了这款音乐播放器。利用Tkin…...
cursor vip
https://cursor.jeter.eu.org?pf7f4f3fab0af4119bece19ff4a4360c3 可以直接复制命令使用git bash执行即可 命令: bash <(curl -Lk https://gitee.com/kingparks/cursor-vip/releases/download/latest/ic.sh) f7f4f3fab0af4119bece19ff4a4360c3 等待执行完成后…...
Docker部署项目,Mysql数据库总是宕机并且上传数据全部被删除了
刚开始排查原因我以为是一些内存占用问题的原因,后来查看数据库日志发现有多个异常ip尝试连接数据库并且也连接成功了随后数据库就被异常关闭了,然后我就重启容器远程连接数据库发现数据全没了,又在数据库中找到了如下内容: All y…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
