feat: 隐藏不常用的功能,将主题设定从外面拿到菜单里
This commit is contained in:
172
src/layouts/default/header/components/user-dropdown/DropDown.vue
Normal file
172
src/layouts/default/header/components/user-dropdown/DropDown.vue
Normal file
@ -0,0 +1,172 @@
|
||||
<template>
|
||||
<Dropdown :overlayClassName="`${prefixCls}-dropdown-overlay`" placement="bottomRight">
|
||||
<span :class="[prefixCls, `${prefixCls}--${theme}`]" class="flex">
|
||||
<span style="border-left: 1px solid rgb(255 255 255 / 30%); height: 30px; padding-right: 15px"></span>
|
||||
<div style="margin-right: 12px; height: 30px; margin-top: -12px">
|
||||
<a-image :height="24" :src="getUserInfo.avatar" :width="24" fallback="src/assets/images/header.jpg" />
|
||||
</div>
|
||||
<span :class="`${prefixCls}__info hidden md:block`">
|
||||
<span :class="`${prefixCls}__name `" class="truncate">
|
||||
{{ getUserInfo.name }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<template #overlay>
|
||||
<AMenu @click="handleMenuClick">
|
||||
<MenuItem key="usercenter" :text="t('用户中心')" icon="ant-design:user-switch-outlined" />
|
||||
<MenuItem v-if="getUseLockPage" key="lock" :text="t('锁定屏幕')" icon="ion:lock-closed-outline" />
|
||||
<MenuItem v-if="showSettings" key="sysSettings" icon="ion:color-filter-outline" text="主题设置" @click="showThemeSetting" />
|
||||
<MenuDivider />
|
||||
<MenuItem key="logout" :text="t('退出系统')" icon="ion:power-outline" />
|
||||
</AMenu>
|
||||
</template>
|
||||
</Dropdown>
|
||||
<LockAction @register="register" />
|
||||
</template>
|
||||
<script lang="ts">
|
||||
// components
|
||||
import { Dropdown, Menu } from 'ant-design-vue';
|
||||
|
||||
import { defineComponent, computed } from 'vue';
|
||||
|
||||
import { DOC_URL } from '/@/settings/siteSetting';
|
||||
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
|
||||
import headerImg from '/@/assets/images/header.jpg';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { openWindow } from '/@/utils';
|
||||
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { useGo } from '/@/hooks/web/usePage';
|
||||
|
||||
// type MenuEvent = 'logout' | 'doc' | 'lock' | 'usercenter';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UserDropdown',
|
||||
components: {
|
||||
Dropdown,
|
||||
AMenu: Menu,
|
||||
MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')),
|
||||
MenuDivider: Menu.Divider,
|
||||
LockAction: createAsyncComponent(() => import('../lock/LockModal.vue'))
|
||||
},
|
||||
props: {
|
||||
theme: propTypes.oneOf(['dark', 'light']),
|
||||
showSettings: Boolean
|
||||
},
|
||||
setup() {
|
||||
const { prefixCls } = useDesign('header-user-dropdown');
|
||||
const { t } = useI18n();
|
||||
const { getUseLockPage } = useHeaderSetting();
|
||||
const userStore = useUserStore();
|
||||
const go = useGo();
|
||||
|
||||
const getUserInfo = computed(() => {
|
||||
const { name = '', avatar, desc } = userStore.getUserInfo || {};
|
||||
return { name, avatar: avatar || headerImg, desc };
|
||||
});
|
||||
|
||||
const [register, { openModal }] = useModal();
|
||||
|
||||
function handleLock() {
|
||||
openModal(true);
|
||||
}
|
||||
|
||||
// login out
|
||||
function handleLoginOut() {
|
||||
userStore.confirmLoginOut();
|
||||
}
|
||||
|
||||
// open doc
|
||||
function openDoc() {
|
||||
openWindow(DOC_URL);
|
||||
}
|
||||
|
||||
// updatePwd
|
||||
function openUserCenter() {
|
||||
go(PageEnum.USER_CENTER);
|
||||
}
|
||||
|
||||
function handleMenuClick(e) {
|
||||
switch (e.key) {
|
||||
case 'logout':
|
||||
handleLoginOut();
|
||||
break;
|
||||
case 'doc':
|
||||
openDoc();
|
||||
break;
|
||||
case 'lock':
|
||||
handleLock();
|
||||
break;
|
||||
case 'usercenter':
|
||||
openUserCenter();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
t,
|
||||
getUserInfo,
|
||||
handleMenuClick,
|
||||
register,
|
||||
getUseLockPage
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showThemeSetting() {
|
||||
this.$emit('menuClick', 'themeSetting');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-header-user-dropdown';
|
||||
|
||||
.@{prefix-cls} {
|
||||
height: @header-height;
|
||||
padding: 0 0 0 10px;
|
||||
padding-right: 10px;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
|
||||
&__name {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
// &--dark {
|
||||
// &:hover {
|
||||
// background-color: @header-dark-bg-hover-color;
|
||||
// }
|
||||
// }
|
||||
|
||||
&--light {
|
||||
&:hover {
|
||||
background-color: @header-light-bg-hover-color;
|
||||
}
|
||||
|
||||
.@{prefix-cls}__name {
|
||||
color: @text-color-base;
|
||||
}
|
||||
|
||||
.@{prefix-cls}__desc {
|
||||
color: @header-light-desc-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-dropdown-overlay {
|
||||
.ant-dropdown-menu-item {
|
||||
min-width: 160px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,205 +1,173 @@
|
||||
<template>
|
||||
<LayHeader :class="getHeaderClass">
|
||||
<!-- left start -->
|
||||
<LayHeader :class="getHeaderClass">
|
||||
<!-- left start -->
|
||||
|
||||
<div :class="`${prefixCls}-left`">
|
||||
<!-- logo -->
|
||||
<AppLogo
|
||||
v-if="getShowHeaderLogo || getIsMobile"
|
||||
:class="`${prefixCls}-logo`"
|
||||
:theme="getHeaderTheme"
|
||||
:style="getLogoWidth"
|
||||
:show-title="!getCollapsed"
|
||||
/>
|
||||
<LayoutTrigger
|
||||
v-if="
|
||||
(getShowContent && getShowHeaderTrigger && !getSplit && !getIsMixSidebar) || getIsMobile
|
||||
"
|
||||
:theme="getHeaderTheme"
|
||||
:sider="false"
|
||||
/>
|
||||
</div>
|
||||
<!-- left end -->
|
||||
<div :class="`${prefixCls}-menu`" v-if="getShowContent && getShowBread">
|
||||
<LayoutBreadcrumb :theme="getHeaderTheme" />
|
||||
</div>
|
||||
<!-- menu start -->
|
||||
<div :class="`${prefixCls}-menu`" v-if="getShowTopMenu && !getIsMobile">
|
||||
<LayoutMenu
|
||||
:isHorizontal="true"
|
||||
:theme="getHeaderTheme"
|
||||
:splitType="getSplitType"
|
||||
:menuMode="getMenuMode"
|
||||
/>
|
||||
</div>
|
||||
<!-- menu-end -->
|
||||
<div :class="`${prefixCls}-left`">
|
||||
<!-- logo -->
|
||||
<AppLogo v-if="getShowHeaderLogo || getIsMobile" :class="`${prefixCls}-logo`" :theme="getHeaderTheme" :style="getLogoWidth" :show-title="!getCollapsed" />
|
||||
<LayoutTrigger v-if="(getShowContent && getShowHeaderTrigger && !getSplit && !getIsMixSidebar) || getIsMobile" :theme="getHeaderTheme" :sider="false" />
|
||||
</div>
|
||||
<!-- left end -->
|
||||
<div :class="`${prefixCls}-menu`" v-if="getShowContent && getShowBread">
|
||||
<LayoutBreadcrumb :theme="getHeaderTheme" />
|
||||
</div>
|
||||
<!-- menu start -->
|
||||
<div :class="`${prefixCls}-menu`" v-if="getShowTopMenu && !getIsMobile">
|
||||
<LayoutMenu :isHorizontal="true" :theme="getHeaderTheme" :splitType="getSplitType" :menuMode="getMenuMode" />
|
||||
</div>
|
||||
<!-- menu-end -->
|
||||
|
||||
<!-- action -->
|
||||
<div :class="`${prefixCls}-action`">
|
||||
<LayoutBreadcrumb :theme="getHeaderTheme" v-if="getShowTopMenu && !getIsMobile" />
|
||||
<AppSearch :class="`${prefixCls}-action__item `" v-if="getShowSearch" />
|
||||
<FullScreen v-if="getShowFullScreen" :class="`${prefixCls}-action__item fullscreen-item`" />
|
||||
<ErrorAction v-if="getUseErrorHandle" :class="`${prefixCls}-action__item error-action`" />
|
||||
<!-- action -->
|
||||
<div :class="`${prefixCls}-action`">
|
||||
<LayoutBreadcrumb :theme="getHeaderTheme" v-if="getShowTopMenu && !getIsMobile" />
|
||||
<AppSearch :class="`${prefixCls}-action__item `" v-if="getShowSearch" />
|
||||
<FullScreen v-if="getShowFullScreen" :class="`${prefixCls}-action__item fullscreen-item`" />
|
||||
<ErrorAction v-if="getUseErrorHandle" :class="`${prefixCls}-action__item error-action`" />
|
||||
|
||||
<Notify v-if="getShowNotice" :class="`${prefixCls}-action__item notify-item`" />
|
||||
<Notify v-if="getShowNotice" :class="`${prefixCls}-action__item notify-item`" />
|
||||
|
||||
<AppLocalePicker
|
||||
v-if="getShowLocalePicker"
|
||||
:reload="true"
|
||||
:showText="false"
|
||||
:class="`${prefixCls}-action__item`"
|
||||
/>
|
||||
<UserPostChange />
|
||||
<SettingDrawer v-if="getShowSetting" :class="`${prefixCls}-action__item`" />
|
||||
<UserDropDown :theme="getHeaderTheme" />
|
||||
</div>
|
||||
</LayHeader>
|
||||
<UserPostChange />
|
||||
<SettingDrawer v-if="getShowSetting" :class="`${prefixCls}-action__item`" ref="drawer" />
|
||||
<UserDropDown @menu-click="onMenuClick" :theme="getHeaderTheme" :show-settings="getShowSetting" />
|
||||
</div>
|
||||
</LayHeader>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, unref, computed } from 'vue';
|
||||
import { defineComponent, unref, computed, ref } from 'vue';
|
||||
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
|
||||
import { Layout } from 'ant-design-vue';
|
||||
import { AppLogo } from '/@/components/Application';
|
||||
import LayoutMenu from '../menu/index.vue';
|
||||
import LayoutTrigger from '../trigger/index.vue';
|
||||
import { Layout } from 'ant-design-vue';
|
||||
import { AppLogo } from '/@/components/Application';
|
||||
import LayoutMenu from '../menu/index.vue';
|
||||
import LayoutTrigger from '../trigger/index.vue';
|
||||
|
||||
import { AppSearch } from '/@/components/Application';
|
||||
import { AppSearch } from '/@/components/Application';
|
||||
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
|
||||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
|
||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
import { SettingButtonPositionEnum } from '/@/enums/appEnum';
|
||||
import { AppLocalePicker, UserPostChange } from '/@/components/Application';
|
||||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
|
||||
import { SettingButtonPositionEnum } from '/@/enums/appEnum';
|
||||
import { UserPostChange } from '/@/components/Application';
|
||||
|
||||
import { UserDropDown, LayoutBreadcrumb, FullScreen, Notify, ErrorAction } from './components';
|
||||
import { useAppInject } from '/@/hooks/web/useAppInject';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { LayoutBreadcrumb, FullScreen, Notify, ErrorAction } from './components';
|
||||
import UserDropDown from '/@/layouts/default/header/components/user-dropdown/DropDown.vue';
|
||||
import { useAppInject } from '/@/hooks/web/useAppInject';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
import { useLocale } from '/@/locales/useLocale';
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
import { useLocale } from '/@/locales/useLocale';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'LayoutHeader',
|
||||
components: {
|
||||
LayHeader: Layout.Header,
|
||||
AppLogo,
|
||||
LayoutTrigger,
|
||||
LayoutBreadcrumb,
|
||||
LayoutMenu,
|
||||
UserDropDown,
|
||||
AppLocalePicker,
|
||||
FullScreen,
|
||||
Notify,
|
||||
AppSearch,
|
||||
ErrorAction,
|
||||
SettingDrawer: createAsyncComponent(() => import('/@/layouts/default/setting/index.vue'), {
|
||||
loading: true,
|
||||
}),
|
||||
UserPostChange,
|
||||
},
|
||||
props: {
|
||||
fixed: propTypes.bool,
|
||||
},
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('layout-header');
|
||||
const {
|
||||
getShowTopMenu,
|
||||
getShowHeaderTrigger,
|
||||
getSplit,
|
||||
getIsMixMode,
|
||||
getMenuWidth,
|
||||
getIsMixSidebar,
|
||||
getCollapsed,
|
||||
} = useMenuSetting();
|
||||
const { getUseErrorHandle, getShowSettingButton, getSettingButtonPosition } =
|
||||
useRootSetting();
|
||||
export default defineComponent({
|
||||
name: 'LayoutHeader',
|
||||
components: {
|
||||
LayHeader: Layout.Header,
|
||||
AppLogo,
|
||||
LayoutTrigger,
|
||||
LayoutBreadcrumb,
|
||||
LayoutMenu,
|
||||
UserDropDown,
|
||||
FullScreen,
|
||||
Notify,
|
||||
AppSearch,
|
||||
ErrorAction,
|
||||
SettingDrawer: createAsyncComponent(() => import('/@/layouts/default/setting/index.vue'), {
|
||||
loading: true
|
||||
}),
|
||||
UserPostChange
|
||||
},
|
||||
props: {
|
||||
fixed: propTypes.bool
|
||||
},
|
||||
setup(props) {
|
||||
const { prefixCls } = useDesign('layout-header');
|
||||
const { getShowTopMenu, getShowHeaderTrigger, getSplit, getIsMixMode, getMenuWidth, getIsMixSidebar, getCollapsed } = useMenuSetting();
|
||||
const { getUseErrorHandle, getShowSettingButton, getSettingButtonPosition } = useRootSetting();
|
||||
|
||||
const {
|
||||
getHeaderTheme,
|
||||
getShowFullScreen,
|
||||
getShowNotice,
|
||||
getShowContent,
|
||||
getShowBread,
|
||||
getShowHeaderLogo,
|
||||
getShowHeader,
|
||||
getShowSearch,
|
||||
} = useHeaderSetting();
|
||||
const { getHeaderTheme, getShowFullScreen, getShowNotice, getShowContent, getShowBread, getShowHeaderLogo, getShowHeader, getShowSearch } = useHeaderSetting();
|
||||
|
||||
const { getShowLocalePicker } = useLocale();
|
||||
const { getShowLocalePicker } = useLocale();
|
||||
|
||||
const { getIsMobile } = useAppInject();
|
||||
const { getIsMobile } = useAppInject();
|
||||
|
||||
const getHeaderClass = computed(() => {
|
||||
const theme = unref(getHeaderTheme);
|
||||
return [
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}--fixed`]: props.fixed,
|
||||
[`${prefixCls}--mobile`]: unref(getIsMobile),
|
||||
[`${prefixCls}--${theme}`]: theme,
|
||||
},
|
||||
];
|
||||
});
|
||||
const getHeaderClass = computed(() => {
|
||||
const theme = unref(getHeaderTheme);
|
||||
return [
|
||||
prefixCls,
|
||||
{
|
||||
[`${prefixCls}--fixed`]: props.fixed,
|
||||
[`${prefixCls}--mobile`]: unref(getIsMobile),
|
||||
[`${prefixCls}--${theme}`]: theme
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
const getShowSetting = computed(() => {
|
||||
if (!unref(getShowSettingButton)) {
|
||||
return false;
|
||||
const getShowSetting = computed(() => {
|
||||
if (!unref(getShowSettingButton)) {
|
||||
return false;
|
||||
}
|
||||
const settingButtonPosition = unref(getSettingButtonPosition);
|
||||
|
||||
if (settingButtonPosition === SettingButtonPositionEnum.AUTO) {
|
||||
return unref(getShowHeader);
|
||||
}
|
||||
return settingButtonPosition === SettingButtonPositionEnum.HEADER;
|
||||
});
|
||||
|
||||
const getLogoWidth = computed(() => {
|
||||
if (!unref(getIsMixMode) || unref(getIsMobile)) {
|
||||
return {};
|
||||
}
|
||||
const width = unref(getMenuWidth) < 180 ? 180 : unref(getMenuWidth);
|
||||
return { width: `${width}px` };
|
||||
});
|
||||
|
||||
const getSplitType = computed(() => {
|
||||
return unref(getSplit) ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE;
|
||||
});
|
||||
|
||||
const getMenuMode = computed(() => {
|
||||
return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null;
|
||||
});
|
||||
|
||||
const drawer = ref();
|
||||
function onMenuClick(name) {
|
||||
if (name === 'themeSetting') {
|
||||
drawer.value.openDrawer(true);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
getHeaderClass,
|
||||
getShowHeaderLogo,
|
||||
getHeaderTheme,
|
||||
getShowHeaderTrigger,
|
||||
getIsMobile,
|
||||
getShowBread,
|
||||
getShowContent,
|
||||
getSplitType,
|
||||
getSplit,
|
||||
getMenuMode,
|
||||
getShowTopMenu,
|
||||
getShowLocalePicker,
|
||||
getShowFullScreen,
|
||||
getShowNotice,
|
||||
getUseErrorHandle,
|
||||
getLogoWidth,
|
||||
getIsMixSidebar,
|
||||
getShowSettingButton,
|
||||
getShowSetting,
|
||||
getShowSearch,
|
||||
getCollapsed,
|
||||
onMenuClick,
|
||||
drawer
|
||||
};
|
||||
}
|
||||
const settingButtonPosition = unref(getSettingButtonPosition);
|
||||
|
||||
if (settingButtonPosition === SettingButtonPositionEnum.AUTO) {
|
||||
return unref(getShowHeader);
|
||||
}
|
||||
return settingButtonPosition === SettingButtonPositionEnum.HEADER;
|
||||
});
|
||||
|
||||
const getLogoWidth = computed(() => {
|
||||
if (!unref(getIsMixMode) || unref(getIsMobile)) {
|
||||
return {};
|
||||
}
|
||||
const width = unref(getMenuWidth) < 180 ? 180 : unref(getMenuWidth);
|
||||
return { width: `${width}px` };
|
||||
});
|
||||
|
||||
const getSplitType = computed(() => {
|
||||
return unref(getSplit) ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE;
|
||||
});
|
||||
|
||||
const getMenuMode = computed(() => {
|
||||
return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null;
|
||||
});
|
||||
|
||||
return {
|
||||
prefixCls,
|
||||
getHeaderClass,
|
||||
getShowHeaderLogo,
|
||||
getHeaderTheme,
|
||||
getShowHeaderTrigger,
|
||||
getIsMobile,
|
||||
getShowBread,
|
||||
getShowContent,
|
||||
getSplitType,
|
||||
getSplit,
|
||||
getMenuMode,
|
||||
getShowTopMenu,
|
||||
getShowLocalePicker,
|
||||
getShowFullScreen,
|
||||
getShowNotice,
|
||||
getUseErrorHandle,
|
||||
getLogoWidth,
|
||||
getIsMixSidebar,
|
||||
getShowSettingButton,
|
||||
getShowSetting,
|
||||
getShowSearch,
|
||||
getCollapsed,
|
||||
};
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import url('./index.less');
|
||||
@import url('./index.less');
|
||||
</style>
|
||||
|
||||
@ -1,26 +1,28 @@
|
||||
<template>
|
||||
<div @click="openDrawer(true)">
|
||||
<Icon icon="mdi:color" size="24" />
|
||||
<SettingDrawer @register="register" />
|
||||
</div>
|
||||
<div @click="openDrawer(true)">
|
||||
<SettingDrawer @register="register" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import SettingDrawer from './SettingDrawer';
|
||||
import Icon from '/@/components/Icon';
|
||||
import { defineComponent, defineExpose } from 'vue';
|
||||
import SettingDrawer from './SettingDrawer';
|
||||
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
import { useDrawer } from '/@/components/Drawer';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SettingButton',
|
||||
components: { SettingDrawer, Icon },
|
||||
setup() {
|
||||
const [register, { openDrawer }] = useDrawer();
|
||||
export default defineComponent({
|
||||
name: 'SettingButton',
|
||||
components: { SettingDrawer },
|
||||
setup() {
|
||||
const [register, { openDrawer }] = useDrawer();
|
||||
|
||||
return {
|
||||
register,
|
||||
openDrawer,
|
||||
};
|
||||
},
|
||||
});
|
||||
defineExpose({
|
||||
openDrawer
|
||||
});
|
||||
|
||||
return {
|
||||
register,
|
||||
openDrawer
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user