Default Changelist

feature/qzw
秦泽旺 2 months ago
parent 449117fb99
commit 201c0a045e

@ -1,17 +1,23 @@
import { Tag } from 'ant-design-vue' // 'ant-design-vue' Tag Ant Design Vue Vue UI Tag
const { CheckableTag } = Tag import { Tag } from 'ant-design-vue';
// Tag CheckableTag
const { CheckableTag } = Tag;
export default { export default {
// 'TagSelectOption' Vue 使
name: 'TagSelectOption', name: 'TagSelectOption',
props: { props: {
// prefixCls 'ant-pro-tag-select-option'便便使
prefixCls: { prefixCls: {
type: String, type: String,
default: 'ant-pro-tag-select-option' default: 'ant-pro-tag-select-option'
}, },
// value 便使
value: { value: {
type: [String, Number, Object], type: [String, Number, Object],
default: '' default: ''
}, },
// checked false true 便使
checked: { checked: {
type: Boolean, type: Boolean,
default: false default: false
@ -19,27 +25,40 @@ export default {
}, },
data () { data () {
return { return {
// localChecked checked checked true false使 false便
localChecked: this.checked || false localChecked: this.checked || false
} }
}, },
watch: { watch: {
// checked checked localChecked checked 便
'checked' (val) { 'checked' (val) {
this.localChecked = val this.localChecked = val;
}, },
// '$parent.items' '$parent' 'items' 使 deep: true '$parent.items' handler
'$parent.items': { '$parent.items': {
handler: function (val) { handler: function (val) {
this.value && val.hasOwnProperty(this.value) && (this.localChecked = val[this.value]) // '$parent.items' value '$parent.items' value localChecked '$parent.items'
this.value && val.hasOwnProperty(this.value) && (this.localChecked = val[this.value]);
}, },
deep: true deep: true
} }
}, },
render () { render () {
const { $slots, value } = this // $slots value $slots 使value 使
const { $slots, value } = this;
const onChange = (checked) => { const onChange = (checked) => {
this.$emit('change', { value, checked }) // onChange $emit 'change' value checked 'change'
} this.$emit('change', { value, checked });
return (<CheckableTag key={value} vModel={this.localChecked} onChange={onChange}> };
{$slots.default} return (
</CheckableTag>) // 使 CheckableTag Ant Design Vue Tag
// key value Vue DOM
// vModel localChecked使
// onChange onChange
// CheckableTag $slots.default使
<CheckableTag key={value} vModel={this.localChecked} onChange={onChange}>
{$slots.default}
</CheckableTag>
)
} }
} }

@ -1,31 +1,41 @@
import PropTypes from 'ant-design-vue/es/_util/vue-types' // 'ant-design-vue/es/_util/vue-types' PropTypes Vue props React PropTypes
import Option from './TagSelectOption.jsx' import PropTypes from 'ant-design-vue/es/_util/vue-types';
import { filterEmpty } from '../../components/_util/util' // 'TagSelectOption.jsx' Option TagSelect
import Option from './TagSelectOption.jsx';
// '../../components/_util/util' filterEmpty
import { filterEmpty } from '../../components/_util/util';
export default { export default {
// Option 使使 <Option> Option
Option, Option,
name: 'TagSelect', name: 'TagSelect',
// v-model 'checked' 'change'使 Vue v-model 'change' 'checked' 'checked' 'change'
model: { model: {
prop: 'checked', prop: 'checked',
event: 'change' event: 'change'
}, },
props: { props: {
// prefixCls 'ant-pro-tag-select'便便
prefixCls: { prefixCls: {
type: String, type: String,
default: 'ant-pro-tag-select' default: 'ant-pro-tag-select'
}, },
// defaultValue PropTypes.array PropTypes null null使
defaultValue: { defaultValue: {
type: PropTypes.array, type: PropTypes.array,
default: null default: null
}, },
// value PropTypes.array null defaultValue
value: { value: {
type: PropTypes.array, type: PropTypes.array,
default: null default: null
}, },
// expandable false
expandable: { expandable: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// hideCheckAll false
hideCheckAll: { hideCheckAll: {
type: Boolean, type: Boolean,
default: false default: false
@ -33,71 +43,108 @@ export default {
}, },
data () { data () {
return { return {
// expand false
expand: false, expand: false,
// localCheckAll false
localCheckAll: false, localCheckAll: false,
// getItemsKey this.$slots.default filterEmpty getItemsKey
items: this.getItemsKey(filterEmpty(this.$slots.default)), items: this.getItemsKey(filterEmpty(this.$slots.default)),
// value defaultValue value 使 defaultValue val null val
val: this.value || this.defaultValue || [] val: this.value || this.defaultValue || []
} }
}, },
methods: { methods: {
/**
* 当选项的选中状态发生改变时调用的方法接收一个包含选中信息的对象checked参数用于更新组件内部的选中状态相关数据以及全选状态的判断
* @param checked 包含选中信息的对象可能包含类似 value选项的值checked是否选中的布尔值等属性具体结构取决于调用时传入的参数情况以及相关组件的约定
*/
onChange (checked) { onChange (checked) {
const key = Object.keys(this.items).filter(key => key === checked.value) // Object.keys items checked.value
this.items[key] = checked.checked const key = Object.keys(this.items).filter(key => key === checked.value);
const bool = Object.values(this.items).lastIndexOf(false) // items checked.checked
this.items[key] = checked.checked;
// Object.values items false -1 false
const bool = Object.values(this.items).lastIndexOf(false);
if (bool === -1) { if (bool === -1) {
this.localCheckAll = true // localCheckAll true
this.localCheckAll = true;
} else { } else {
this.localCheckAll = false // localCheckAll false
this.localCheckAll = false;
} }
}, },
/**
* 当点击全选按钮时调用的方法接收一个包含全选按钮选中状态信息的对象checked参数用于更新所有选项的选中状态以及组件内部的全选状态记录
* @param checked 包含全选按钮选中状态的对象通常包含 checked 属性布尔值表示全选按钮是否被选中用于统一设置所有选项的选中状态
*/
onCheckAll (checked) { onCheckAll (checked) {
// items checked.checked
Object.keys(this.items).forEach(v => { Object.keys(this.items).forEach(v => {
this.items[v] = checked.checked this.items[v] = checked.checked;
}) });
this.localCheckAll = checked.checked // localCheckAll checked.checked
this.localCheckAll = checked.checked;
}, },
/**
* 用于获取每个选项对应的键值对象根据传入的选项数组items创建一个新的对象对象的键为每个选项的特定值通常是用于唯一标识选项的值比如选项的 ID 值初始化为 false表示默认未选中状态方便后续根据选项的键来记录和查询其选中状态等信息
* @param items 选项数组数组元素可能是包含选项相关信息的虚拟 DOM 节点或者对象具体取决于组件插槽传入内容的结构和格式
* @returns {Object} 返回一个对象对象的键为选项的特定值值为布尔类型的选中状态初始化为 false
*/
getItemsKey (items) { getItemsKey (items) {
const totalItem = {} const totalItem = {};
items.forEach(item => { items.forEach(item => {
totalItem[item.componentOptions.propsData && item.componentOptions.propsData.value] = false // item item.componentOptions.propsData.value 'value' totalItem false
}) totalItem[item.componentOptions.propsData && item.componentOptions.propsData.value] = false;
return totalItem });
return totalItem;
}, },
// CheckAll Button // CheckAll Button
/**
* 用于渲染全选按钮的方法根据 hideCheckAll 属性判断是否需要隐藏全选按钮如果不隐藏hideCheckAll false则返回一个 <Option> 组件之前导入的组件作为全选按钮设置其 key 属性为 'total'选中状态为 localCheckAll 属性的值即当前组件内部记录的全选状态并绑定 onChange 事件为 onCheckAll 方法按钮显示文本为 'All'如果需要隐藏hideCheckAll true则返回 null表示不渲染全选按钮
*/
renderCheckAll () { renderCheckAll () {
return !this.hideCheckAll && (<Option key={'total'} checked={this.localCheckAll} onChange={this.onCheckAll}>All</Option>) || null return!this.hideCheckAll && (<Option key={'total'} checked={this.localCheckAll} onChange={this.onCheckAll}>All</Option>) || null;
}, },
// expandable // expandable
/**
* 用于处理组件可展开相关逻辑的方法目前函数体为空可能后续需要在这里添加代码来实现组件展开时的具体行为比如展开显示更多选项展开后加载更多数据等相关操作具体功能取决于组件的设计需求
*/
renderExpandable () { renderExpandable () {
}, },
// render option // render option
/**
* 用于渲染选项的方法接收选项数组items参数主要功能是为每个选项添加 change 事件监听器当选项的选中状态改变时先触发组件内部的 onChange 方法更新内部状态再通过 $emit 触发 'change' 事件将选中状态变化通知给外部组件实现双向数据绑定或者让外部组件能响应选项变化等功能最后返回处理后的选项数组包含添加了事件监听器的虚拟 DOM 节点等用于在组件模板中进行渲染展示
* @param items 选项数组同前面提到的选项相关数组包含要渲染的各个选项的信息
*/
renderTags (items) { renderTags (items) {
const listeners = { const listeners = {
change: (checked) => { change: (checked) => {
this.onChange(checked) this.onChange(checked);
this.$emit('change', checked) this.$emit('change', checked);
} }
} };
return items.map(vnode => { return items.map(vnode => {
const options = vnode.componentOptions const options = vnode.componentOptions;
options.listeners = listeners options.listeners = listeners;
return vnode return vnode;
}) });
} }
}, },
render () { render () {
const { $props: { prefixCls } } = this // $props prefixCls
const { $props: { prefixCls } } = this;
const classString = { const classString = {
// classString `${prefixCls}` true使 :class 便
[`${prefixCls}`]: true [`${prefixCls}`]: true
} };
const tagItems = filterEmpty(this.$slots.default) const tagItems = filterEmpty(this.$slots.default);
return ( return (
<div class={classString}> <div class={classString}>
{this.renderCheckAll()} {this.renderCheckAll()}
{this.renderTags(tagItems)} {this.renderTags(tagItems)}
</div> </div>
) );
} }
} }

@ -1,10 +1,14 @@
<template> <template>
<!-- 使用 Ant Design Vue 框架中的 a-breadcrumb 组件创建一个面包屑导航栏为其添加 "breadcrumb" 类名方便后续通过 CSS 对其样式进行定制化设置 -->
<a-breadcrumb class="breadcrumb"> <a-breadcrumb class="breadcrumb">
<!-- 使用 v-for 指令循环遍历 breadList 数组中的每个元素item以及对应的索引index为每个元素创建一个 a-breadcrumb-item 组件并且通过 :key 绑定每个元素的唯一标识这里使用 item.name 作为唯一标识用于 Vue 的虚拟 DOM 渲染优化帮助 Vue 准确识别每个元素的变化情况 -->
<a-breadcrumb-item v-for="(item, index) in breadList" :key="item.name"> <a-breadcrumb-item v-for="(item, index) in breadList" :key="item.name">
<!-- 使用 router-link 组件创建一个路由链接只有当 item.name 不等于当前组件的 name 属性值并且索引 index 不等于 1 时才显示为路由链接可能是为了排除特定的某个或某些面包屑项不做成可点击的链接形式通过 :to 属性绑定一个对象根据 item.path 的值来设置链接的目标路径如果 item.path 为空字符串则设置目标路径为根路径 '/'否则使用 item.path 本身的值作为目标路径 -->
<router-link <router-link
v-if="item.name != name && index != 1" v-if="item.name!= name && index!= 1"
:to="{ path: item.path === '' ? '/' : item.path }" :to="{ path: item.path === ''? '/' : item.path }"
>{{ item.meta.title }}</router-link> >{{ item.meta.title }}</router-link>
<!-- 当不满足上面 router-link 的显示条件时即要么 item.name 等于当前组件的 name 属性值要么索引 index 等于 1则以普通的 <span> 元素形式显示该项面包屑的标题item.meta.title不具备路由跳转功能 -->
<span v-else>{{ item.meta.title }}</span> <span v-else>{{ item.meta.title }}</span>
</a-breadcrumb-item> </a-breadcrumb-item>
</a-breadcrumb> </a-breadcrumb>
@ -14,28 +18,36 @@
export default { export default {
data () { data () {
return { return {
// name 使
name: '', name: '',
// breadList
breadList: [] breadList: []
} }
}, },
created () { created () {
this.getBreadcrumb() // getBreadcrumb
this.getBreadcrumb();
}, },
methods: { methods: {
getBreadcrumb () { getBreadcrumb () {
this.breadList = [] // breadList
this.breadList = [];
// breadList 'index' '/dashboard/' ''
// this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: ''}}) // this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: ''}})
this.name = this.$route.name // name
this.name = this.$route.name;
// $route.matched item.name!== 'index' breadList
this.$route.matched.forEach(item => { this.$route.matched.forEach(item => {
// item.name !== 'index' && this.breadList.push(item) // item.name!== 'index' && this.breadList.push(item)
this.breadList.push(item) this.breadList.push(item);
}) });
} }
}, },
watch: { watch: {
// $route 退 getBreadcrumb
$route () { $route () {
this.getBreadcrumb() this.getBreadcrumb();
} }
} }
} }

@ -1,5 +1,10 @@
<script> <script>
// 使
/* WARNING: 兼容老引入,请勿继续使用 */ /* WARNING: 兼容老引入,请勿继续使用 */
import DescriptionList from '../../components/DescriptionList'
export default DescriptionList // '../../components/DescriptionList' DescriptionList Vue DescriptionList
</script> import DescriptionList from '../../components/DescriptionList';
// DescriptionList DescriptionList便
export default DescriptionList;
</script>

@ -1,27 +1,34 @@
<template> <template>
<!-- 创建一个带有 "head-info" 类名的 div 容器同时通过动态绑定 class 属性根据组件传入的 center 属性值布尔类型来决定是否添加 "center" 类名以此实现不同的样式布局效果 -->
<div class="head-info" :class="center && 'center'"> <div class="head-info" :class="center && 'center'">
<!-- 使用双括号插值语法展示 title 属性的值将其作为一个文本内容放在 span 元素内展示该文本通常用于显示标题相关的信息 -->
<span>{{ title }}</span> <span>{{ title }}</span>
<!-- 同样使用双括号插值语法展示 content 属性的值把它放置在 p 元素中展示这里的内容大概率是主要的正文信息字体等样式上会相对突出一些 -->
<p>{{ content }}</p> <p>{{ content }}</p>
<!-- 通过 v-if 指令根据 bordered 属性的值布尔类型来决定是否渲染 em 元素如果 bordered 属性为 true则渲染该元素可能用于添加一些装饰性的分割线等样式效果具体样式由后续的样式部分定义 -->
<em v-if="bordered"/> <em v-if="bordered"/>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: 'HeadInfo', name: 'HeadInfo',
props: { props: {
// title
title: { title: {
type: String, type: String,
default: '' default: ''
}, },
// content
content: { content: {
type: String, type: String,
default: '' default: ''
}, },
// bordered em 线 false true
bordered: { bordered: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// center true false
center: { center: {
type: Boolean, type: Boolean,
default: true default: true
@ -31,37 +38,58 @@ export default {
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.head-info { // "head-info"
position: relative; .head-info {
text-align: left; // 便 em
padding: 0 32px 0 0; position: relative;
min-width: 125px; // "center" center
text-align: left;
// 32px 0使
padding: 0 32px 0 0;
// 125px
min-width: 125px;
&.center { // "center" center true
text-align: center; &.center {
padding: 0 32px; text-align: center;
} padding: 0 32px;
}
span { span {
color: rgba(0, 0, 0, .45); // span 使 rgba 0.45 使
display: inline-block; color: rgba(0, 0, 0,.45);
font-size: 14px; // span 使便
line-height: 22px; display: inline-block;
margin-bottom: 4px; // span 14px
} font-size: 14px;
p { // span 22px
color: rgba(0, 0, 0, .85); line-height: 22px;
font-size: 24px; // span 4px p 使
line-height: 32px; margin-bottom: 4px;
margin: 0; }
} p {
em { // p 0.85 span
background-color: #e8e8e8; color: rgba(0, 0, 0,.85);
position: absolute; // p 24px使
height: 56px; font-size: 24px;
width: 1px; // p 32px
top: 0; line-height: 32px;
right: 0; // p 0使
} margin: 0;
} }
</style> em {
// em #e8e8e8线
background-color: #e8e8e8;
// em .head-info
position: absolute;
// em 56px线
height: 56px;
// em 1px使线线
width: 1px;
// em 0使.head-info
top: 0;
// em 0使.head-info线
right: 0;
}
}
</style>

@ -1,26 +1,62 @@
<template> <template>
<!-- 创建一个带有 "logo" 类名的 div 容器用于包裹整个 logo 相关的元素方便后续通过 CSS 对这个区域进行样式设置统一管理 logo 的布局和外观表现 -->
<div class="logo"> <div class="logo">
<!-- 使用 router-link 组件创建一个路由链接通过 :to 属性绑定一个对象指定链接的目标路由这里目标路由是名为 'dashboard' 的路由具体该路由对应的页面和功能需要看路由配置文件中的定义点击这个链接会跳转到对应的 'dashboard' 页面 -->
<router-link :to="{name:'dashboard'}"> <router-link :to="{name:'dashboard'}">
<!-- 引入名为 LogoSvg 的组件用于展示 logo 的图标部分具体该组件如何渲染 SVG 图形以及样式表现要看其内部实现这里它接收一个 alt 属性用于设置图片的替代文本当图片无法正常显示时或者辅助设备读取时会显示该文本内容 -->
<LogoSvg alt="logo" /> <LogoSvg alt="logo" />
<!-- 通过 v-if 指令根据 showTitle 属性布尔类型的值来决定是否渲染 h1 元素如果 showTitle true则渲染 h1 元素并展示 title 属性的值作为标题内容如果 showTitle false则不渲染 h1 元素即不显示标题部分以此实现根据条件控制标题是否显示的功能 -->
<h1 v-if="showTitle">{{ title }}</h1> <h1 v-if="showTitle">{{ title }}</h1>
</router-link> </router-link>
</div> </div>
</template> </template>
<script> <script>
import LogoSvg from '../../assets/logo.svg?inline' // '../../assets/logo.svg?inline' LogoSvg '?inline' SVG Vue 使 SVG 便使
import LogoSvg from '../../assets/logo.svg?inline';
export default { export default {
name: 'Logo', name: 'Logo',
components: { components: {
// LogoSvg 使使 <LogoSvg> SVG
LogoSvg LogoSvg
}, },
props: { props: {
// title 'Online Exam' 'Online Exam'required: false
title: { title: {
type: String, type: String,
default: 'Online Exam', default: 'Online Exam',
required: false required: false
}, },
// showTitle h1 title true false required: false
showTitle: {
type: Boolean,
default: true,
required: false
}
}
}
</script>
<script>
// '../../assets/logo.svg?inline' LogoSvg Vue SVG '?inline' 使 Vue CLI SVG 使 Vue 便使
import LogoSvg from '../../assets/logo.svg?inline';
export default {
// 'Logo' Vue 便
name: 'Logo',
components: {
// `components` LogoSvg 使template使 `<LogoSvg>` SVG 使
LogoSvg
},
props: {
// `title` `String`使 'Online Exam' `title` 使 'Online Exam' `required: false`
title: {
type: String,
default: 'Online Exam',
required: false
},
// `showTitle` `Boolean` `true` `false` `<h1>` `required: false`使
showTitle: { showTitle: {
type: Boolean, type: Boolean,
default: true, default: true,

@ -41,6 +41,7 @@
<script> <script>
export default { export default {
props: { props: {
// visible false true v-model a-modal
visible: { visible: {
type: Boolean, type: Boolean,
default: false default: false
@ -48,42 +49,51 @@ export default {
}, },
data () { data () {
return { return {
// stepLoading false true false
stepLoading: false, stepLoading: false,
// form null a-form :auto-form-create 便
form: null form: null
} }
}, },
methods: { methods: {
handleStepOk () { handleStepOk () {
const vm = this const vm = this;
this.stepLoading = true // "" stepLoading true使
this.stepLoading = true;
// this.form validateFields 6 err null values
this.form.validateFields((err, values) => { this.form.validateFields((err, values) => {
if (!err) { if (!err) {
console.log('values', values) // err null values 便 setTimeout 2 stepLoading false $emit 'success' values便
console.log('values', values);
setTimeout(() => { setTimeout(() => {
vm.stepLoading = false vm.stepLoading = false;
vm.$emit('success', { values }) vm.$emit('success', { values });
}, 2000) }, 2000);
return return;
} }
this.stepLoading = false // err null stepLoading false $emit 'error' err便
this.$emit('error', { err }) this.stepLoading = false;
}) this.$emit('error', { err });
});
}, },
handleCancel () { handleCancel () {
this.visible = false // "" visible false $emit 'cancel'
this.$emit('cancel') this.visible = false;
this.$emit('cancel');
}, },
onForgeStepCode () { onForgeStepCode () {
// "?"
} }
} }
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.step-form-wrapper { // "step-form-wrapper"
margin: 0 auto; .step-form-wrapper {
width: 80%; // auto使
max-width: 400px; margin: 0 auto;
} // 80% 400px使
width: 80%;
max-width: 400px;
}
</style> </style>

@ -1,20 +1,32 @@
<template> <template>
<!-- 创建一个带有 "user-wrapper" 类名的 div 容器用于包裹整个用户相关菜单的结构方便后续通过 CSS 对这个区域进行样式设置统一管理其布局和外观表现 -->
<div class="user-wrapper"> <div class="user-wrapper">
<!-- "user-wrapper" 内部再创建一个带有 "content-box" 类名的 div 容器进一步细化布局结构后续相关的用户菜单交互组件等都放置在这个容器内 -->
<div class="content-box"> <div class="content-box">
<!-- 使用 Ant Design Vue 库中的 a-dropdown 组件创建一个下拉菜单组件用于展示用户相关的操作选项如账户设置退出登录等点击后会弹出相应的菜单选项供用户选择操作 -->
<a-dropdown> <a-dropdown>
<!-- 创建一个带有 "action ant-dropdown-link user-dropdown-menu" 类名的 span 元素作为触发下拉菜单显示的操作区域它内部包含用户头像通过 a-avatar 组件展示和用户昵称通过双括号插值语法展示用户点击这个区域时会弹出下拉菜单 -->
<span class="action ant-dropdown-link user-dropdown-menu"> <span class="action ant-dropdown-link user-dropdown-menu">
<!-- 使用 Ant Design Vue 库中的 a-avatar 组件展示用户头像设置其 class 属性为 "avatar"size 属性为 "small" 表示以小尺寸展示头像通过 :src 属性绑定一个函数从脚本部分可知是从全局变量获取头像地址的函数来动态获取并显示用户头像 -->
<a-avatar class="avatar" size="small" :src="avatar()"/> <a-avatar class="avatar" size="small" :src="avatar()"/>
<!-- 使用双括号插值语法展示用户昵称昵称的值同样是通过函数从全局变量获取昵称的函数动态获取得到具体函数的实现和数据来源看脚本部分代码 -->
<span>{{ nickname() }}</span> <span>{{ nickname() }}</span>
</span> </span>
<!-- 使用 slot="overlay" 插槽来定义下拉菜单弹出时显示的具体内容也就是具体的菜单选项列表将其包裹在带有 "user-dropdown-menu-wrapper" 类名的 a-menu 组件中使其呈现为一个菜单的样式结构 -->
<a-menu slot="overlay" class="user-dropdown-menu-wrapper"> <a-menu slot="overlay" class="user-dropdown-menu-wrapper">
<!-- 创建一个 a-menu-item 菜单子项组件设置 key 属性为 "1"用于在菜单中唯一标识这个选项方便 Vue 在进行虚拟 DOM 操作等场景下进行区分和管理 -->
<a-menu-item key="1"> <a-menu-item key="1">
<!-- 使用 router-link 组件创建一个路由链接通过 :to 属性绑定一个对象指定链接的目标路由为名为 'settings' 的路由具体该路由对应的页面和功能需要看路由配置文件中的定义点击这个选项会跳转到账户设置相关的页面在这个菜单子项内部还包含了一个 a-icon 组件用于展示设置图标以及一个 span 元素用于显示 "账户设置" 文本内容 -->
<router-link :to="{ name: 'settings' }"> <router-link :to="{ name: 'settings' }">
<a-icon type="setting"/> <a-icon type="setting"/>
<span>账户设置</span> <span>账户设置</span>
</router-link> </router-link>
</a-menu-item> </a-menu-item>
<!-- 使用 a-menu-divider 组件创建一个菜单分隔线用于在菜单选项之间进行视觉上的分隔使不同功能的选项分组更加清晰这里将账户设置选项和退出登录选项进行了分隔 -->
<a-menu-divider/> <a-menu-divider/>
<!-- 再创建一个 a-menu-item 菜单子项组件设置 key 属性为 "3"同样用于唯一标识这个选项用于展示退出登录相关的操作选项 -->
<a-menu-item key="3"> <a-menu-item key="3">
<!-- 使用普通的 <a> 标签创建一个链接这里使用 "javascript:;" 作为 href 属性值阻止默认的页面跳转行为因为实际的退出登录操作是通过绑定的 @click 事件来处理绑定 @click 事件为 handleLogout 方法点击这个选项会调用 handleLogout 方法来执行退出登录相关的逻辑在这个菜单子项内部同样包含了一个 a-icon 组件用于展示退出图标以及一个 span 元素用于显示 "退出登录" 文本内容 -->
<a href="javascript:;" @click="handleLogout"> <a href="javascript:;" @click="handleLogout">
<a-icon type="logout"/> <a-icon type="logout"/>
<span>退出登录</span> <span>退出登录</span>
@ -27,32 +39,37 @@
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex' // Vuex mapActions mapGetters mapActions Vuex action 便 action mapGetters Vuex getters 使便 Vuex store
import { mapActions, mapGetters } from 'vuex';
export default { export default {
name: 'UserMenu', name: 'UserMenu',
methods: { methods: {
...mapActions(['Logout']), // tokenlocalStorage // 使 Vuex Logout action this.Logout() action token localStorage 退
...mapGetters(['nickname', 'avatar']), // ...mapActions(['Logout']),
// 使 Vuex nickname avatar getters this.nickname() this.avatar() 便
...mapGetters(['nickname', 'avatar']),
handleLogout () { handleLogout () {
const that = this const that = this;
// 使 this.$confirm Ant Design Vue "" "?"
this.$confirm({ this.$confirm({
title: '提示', title: '提示',
content: '真的要注销登录吗 ?', content: '真的要注销登录吗?',
onOk () { onOk () {
// this.Logout({}) mapActions 退 action Promise 退.then Promise resolve window.location.reload() Promise reject.catch 使 this.$message.error Ant Design Vue "" err.message
return that.Logout({}).then(() => { return that.Logout({}).then(() => {
window.location.reload() window.location.reload();
}).catch(err => { }).catch(err => {
that.$message.error({ that.$message.error({
title: '错误', title: '错误',
description: err.message description: err.message
}) });
}) });
}, },
onCancel () { onCancel () {
// 退
} }
}) });
} }
} }
} }

Loading…
Cancel
Save