mirror of
https://gitee.com/fantastic-admin/basic.git
synced 2024-12-02 03:57:37 +08:00
更新依赖
This commit is contained in:
parent
1feea3d0c9
commit
30bf80a3f6
56
package.json
56
package.json
@ -22,12 +22,12 @@
|
||||
"@bytemd/plugin-gfm": "^1.21.0",
|
||||
"@bytemd/vue-next": "^1.21.0",
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@imengyu/vue3-context-menu": "^1.4.2",
|
||||
"@imengyu/vue3-context-menu": "^1.4.3",
|
||||
"@tinymce/tinymce-vue": "^6.0.1",
|
||||
"@visactor/vchart": "^1.12.6",
|
||||
"@visactor/vchart": "^1.12.8",
|
||||
"@vue-office/docx": "^1.6.2",
|
||||
"@vue-office/excel": "^1.7.11",
|
||||
"@vue-office/pdf": "^2.0.2",
|
||||
"@vue-office/pdf": "^2.0.6",
|
||||
"@vueuse/components": "^11.1.0",
|
||||
"@vueuse/core": "^11.1.0",
|
||||
"@vueuse/integrations": "^11.1.0",
|
||||
@ -37,10 +37,10 @@
|
||||
"cropperjs": "^1.6.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"defu": "^6.1.4",
|
||||
"disable-devtool": "^0.3.7",
|
||||
"disable-devtool": "^0.3.8",
|
||||
"echarts": "^5.5.1",
|
||||
"element-plus": "^2.8.3",
|
||||
"eruda": "^3.3.0",
|
||||
"element-plus": "^2.8.5",
|
||||
"eruda": "^3.4.0",
|
||||
"floating-vue": "5.2.2",
|
||||
"hotkeys-js": "^3.13.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
@ -50,17 +50,17 @@
|
||||
"overlayscrollbars": "^2.10.0",
|
||||
"overlayscrollbars-vue": "^0.5.9",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^8.1.0",
|
||||
"pinia": "^2.2.2",
|
||||
"path-to-regexp": "^8.2.0",
|
||||
"pinia": "^2.2.4",
|
||||
"print-js": "^1.6.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"qs": "^6.13.0",
|
||||
"scule": "^1.3.0",
|
||||
"splitpanes": "^3.1.5",
|
||||
"swiper": "^11.1.14",
|
||||
"tinymce": "^7.3.0",
|
||||
"tinymce": "^7.4.1",
|
||||
"vconsole": "^3.15.1",
|
||||
"vue": "^3.5.7",
|
||||
"vue": "^3.5.12",
|
||||
"vue-currency-input": "^3.1.0",
|
||||
"vue-esign": "^1.1.4",
|
||||
"vue-hooks-plus": "^2.2.1",
|
||||
@ -68,11 +68,11 @@
|
||||
"vue-router": "^4.4.5",
|
||||
"vue3-count-to": "^1.1.2",
|
||||
"vxe-table": "^4.7.16",
|
||||
"xe-utils": "^3.5.30"
|
||||
"xe-utils": "^3.5.31"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "3.7.1",
|
||||
"@iconify/json": "^2.2.251",
|
||||
"@antfu/eslint-config": "3.8.0",
|
||||
"@iconify/json": "^2.2.261",
|
||||
"@iconify/vue": "^4.1.2",
|
||||
"@stylistic/stylelint-config": "^2.0.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
@ -82,38 +82,38 @@
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/qs": "^6.9.16",
|
||||
"@types/splitpanes": "^2.2.6",
|
||||
"@unocss/eslint-plugin": "^0.62.4",
|
||||
"@unocss/eslint-plugin": "^0.63.4",
|
||||
"@vitejs/plugin-legacy": "^5.4.2",
|
||||
"@vitejs/plugin-vue": "^5.1.4",
|
||||
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"boxen": "^8.0.1",
|
||||
"eslint": "^9.11.0",
|
||||
"esno": "^4.7.0",
|
||||
"eslint": "^9.12.0",
|
||||
"esno": "^4.8.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"http-server": "^14.1.1",
|
||||
"inquirer": "^11.0.2",
|
||||
"inquirer": "^12.0.0",
|
||||
"lint-staged": "^15.2.10",
|
||||
"npm-run-all2": "^6.2.3",
|
||||
"picocolors": "^1.1.0",
|
||||
"npm-run-all2": "^6.2.4",
|
||||
"picocolors": "^1.1.1",
|
||||
"plop": "^4.0.1",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-nested": "^6.2.0",
|
||||
"sass-embedded": "^1.79.3",
|
||||
"sass-embedded": "^1.80.2",
|
||||
"simple-git-hooks": "^2.11.1",
|
||||
"stylelint": "^16.9.0",
|
||||
"stylelint-config-recess-order": "^5.1.0",
|
||||
"stylelint": "^16.10.0",
|
||||
"stylelint-config-recess-order": "^5.1.1",
|
||||
"stylelint-config-standard-scss": "^13.1.0",
|
||||
"stylelint-config-standard-vue": "^1.0.0",
|
||||
"stylelint-scss": "^6.7.0",
|
||||
"stylelint-scss": "^6.8.1",
|
||||
"svgo": "^3.3.2",
|
||||
"typescript": "^5.6.2",
|
||||
"unocss": "^0.62.4",
|
||||
"typescript": "^5.6.3",
|
||||
"unocss": "^0.63.4",
|
||||
"unocss-preset-scrollbar": "^0.3.1",
|
||||
"unplugin-auto-import": "^0.18.3",
|
||||
"unplugin-turbo-console": "^1.10.2",
|
||||
"unplugin-turbo-console": "^1.10.4",
|
||||
"unplugin-vue-components": "^0.27.4",
|
||||
"vite": "^5.4.6",
|
||||
"vite": "^5.4.9",
|
||||
"vite-plugin-app-loading": "^0.3.0",
|
||||
"vite-plugin-archiver": "^0.1.1",
|
||||
"vite-plugin-banner": "^0.8.0",
|
||||
@ -121,7 +121,7 @@
|
||||
"vite-plugin-fake-server": "^2.1.2",
|
||||
"vite-plugin-pages": "^0.32.3",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-devtools": "^7.4.5",
|
||||
"vite-plugin-vue-devtools": "^7.5.2",
|
||||
"vite-plugin-vue-meta-layouts": "^0.4.3",
|
||||
"vue-tsc": "^2.1.6"
|
||||
},
|
||||
|
3197
pnpm-lock.yaml
3197
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -23,7 +23,7 @@ const loading = ref(false)
|
||||
|
||||
// 登录方式,default 账号密码登录,qrcode 扫码登录
|
||||
const type = ref('default')
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRef = useTemplateRef<FormInstance>('formRef')
|
||||
const form = ref({
|
||||
account: props.account ?? localStorage.login_account ?? '',
|
||||
password: '',
|
||||
|
@ -17,7 +17,7 @@ const emits = defineEmits<{
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRef = useTemplateRef<FormInstance>('formRef')
|
||||
const form = ref({
|
||||
account: props.account ?? '',
|
||||
captcha: '',
|
||||
|
@ -17,7 +17,7 @@ const emits = defineEmits<{
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRef = useTemplateRef<FormInstance>('formRef')
|
||||
const form = ref({
|
||||
account: props.account ?? localStorage.login_account ?? '',
|
||||
captcha: '',
|
||||
|
@ -13,12 +13,12 @@ const menuStore = useMenuStore()
|
||||
|
||||
const { switchTo } = useMenu()
|
||||
|
||||
const menuRef = ref()
|
||||
const menuRef = useTemplateRef('menuRef')
|
||||
|
||||
// 顶部模式鼠标滚动
|
||||
function handlerMouserScroll(event: WheelEvent) {
|
||||
if (event.deltaY || event.detail !== 0) {
|
||||
menuRef.value.scrollBy({
|
||||
menuRef.value?.scrollBy({
|
||||
left: (event.deltaY || event.detail) > 0 ? 50 : -50,
|
||||
})
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ const props = withDefaults(
|
||||
)
|
||||
|
||||
const index = props.menu.path ?? JSON.stringify(props.menu)
|
||||
const itemRef = shallowRef()
|
||||
const subMenuRef = shallowRef<OverlayScrollbarsComponentRef>()
|
||||
const itemRef = useTemplateRef('itemRef')
|
||||
const subMenuRef = useTemplateRef<OverlayScrollbarsComponentRef>('subMenuRef')
|
||||
const rootMenu = inject(rootMenuInjectionKey)!
|
||||
|
||||
const opened = computed(() => {
|
||||
@ -130,7 +130,10 @@ function handleMouseenter() {
|
||||
if (hasChildren.value) {
|
||||
rootMenu.openMenu(index, props.uniqueKey)
|
||||
nextTick(() => {
|
||||
const el = itemRef.value.ref
|
||||
const el = itemRef.value?.ref
|
||||
if (!el) {
|
||||
return
|
||||
}
|
||||
let top = 0
|
||||
let left = 0
|
||||
if (rootMenu.props.mode === 'vertical' || props.level !== 0) {
|
||||
|
@ -55,12 +55,9 @@ const searchInput = ref('')
|
||||
const sourceList = ref<listTypes[]>([])
|
||||
const actived = ref(-1)
|
||||
|
||||
const searchInputRef = ref()
|
||||
const searchResultRef = ref<OverlayScrollbarsComponentRef>()
|
||||
const searchResultItemRef = ref<HTMLElement[]>([])
|
||||
onBeforeUpdate(() => {
|
||||
searchResultItemRef.value = []
|
||||
})
|
||||
const searchInputRef = useTemplateRef('searchInputRef')
|
||||
const searchResultRef = useTemplateRef<OverlayScrollbarsComponentRef>('searchResultRef')
|
||||
const searchResultItemRef = useTemplateRef<HTMLElement[]>('searchResultItemRef')
|
||||
|
||||
const resultList = computed(() => {
|
||||
let result = []
|
||||
@ -204,7 +201,7 @@ function keyDown() {
|
||||
}
|
||||
function keyEnter() {
|
||||
if (actived.value !== -1) {
|
||||
searchResultItemRef.value.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.click()
|
||||
searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.click()
|
||||
}
|
||||
}
|
||||
function handleScroll() {
|
||||
@ -213,8 +210,8 @@ function handleScroll() {
|
||||
let scrollTo = 0
|
||||
if (actived.value !== -1) {
|
||||
scrollTo = contentDom.scrollTop
|
||||
const activedOffsetTop = searchResultItemRef.value.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.offsetTop ?? 0
|
||||
const activedClientHeight = searchResultItemRef.value.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.clientHeight ?? 0
|
||||
const activedOffsetTop = searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.offsetTop ?? 0
|
||||
const activedClientHeight = searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.clientHeight ?? 0
|
||||
const searchScrollTop = contentDom.scrollTop
|
||||
const searchClientHeight = contentDom.clientHeight
|
||||
if (activedOffsetTop + activedClientHeight > searchScrollTop + searchClientHeight) {
|
||||
|
@ -14,14 +14,14 @@ const route = useRoute()
|
||||
const settingsStore = useSettingsStore()
|
||||
const menuStore = useMenuStore()
|
||||
|
||||
const subSidebarRef = ref()
|
||||
const subSidebarRef = useTemplateRef('subSidebarRef')
|
||||
const showShadowTop = ref(false)
|
||||
const showShadowBottom = ref(false)
|
||||
function onSidebarScroll() {
|
||||
const scrollTop = subSidebarRef.value.scrollTop
|
||||
const scrollTop = subSidebarRef.value?.scrollTop ?? 0
|
||||
showShadowTop.value = scrollTop > 0
|
||||
const clientHeight = subSidebarRef.value.clientHeight
|
||||
const scrollHeight = subSidebarRef.value.scrollHeight
|
||||
const clientHeight = subSidebarRef.value?.clientHeight ?? 0
|
||||
const scrollHeight = subSidebarRef.value?.scrollHeight ?? 0
|
||||
showShadowBottom.value = Math.ceil(scrollTop + clientHeight) < scrollHeight
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ watch(enableSidebar, (val) => {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
const menuRef = ref()
|
||||
const menuRef = useTemplateRef('menuRef')
|
||||
|
||||
onMounted(() => {
|
||||
if (enableSidebar.value) {
|
||||
|
@ -25,19 +25,16 @@ const keys = useMagicKeys({ reactive: true })
|
||||
|
||||
const activedTabId = computed(() => tabbar.getId())
|
||||
|
||||
const tabsRef = ref()
|
||||
const tabContainerRef = ref()
|
||||
const tabRef = shallowRef<HTMLElement[]>([])
|
||||
onBeforeUpdate(() => {
|
||||
tabRef.value = []
|
||||
})
|
||||
const tabsRef = useTemplateRef('tabsRef')
|
||||
const tabContainerRef = useTemplateRef('tabContainerRef')
|
||||
const tabRef = useTemplateRef<HTMLElement[]>('tabRef')
|
||||
|
||||
watch(() => route, (val) => {
|
||||
if (settingsStore.settings.tabbar.enable) {
|
||||
tabbarStore.add(val).then(() => {
|
||||
const index = tabbarStore.list.findIndex(item => item.tabId === activedTabId.value)
|
||||
if (index !== -1) {
|
||||
scrollTo(tabRef.value[index].offsetLeft)
|
||||
tabRef.value && scrollTo(tabRef.value[index].offsetLeft)
|
||||
tabbarScrollTip()
|
||||
}
|
||||
})
|
||||
@ -47,7 +44,7 @@ watch(() => route, (val) => {
|
||||
deep: true,
|
||||
})
|
||||
function tabbarScrollTip() {
|
||||
if (tabContainerRef.value.$el.clientWidth > tabsRef.value.clientWidth && localStorage.getItem('tabbarScrollTip') === undefined) {
|
||||
if (tabContainerRef.value?.$el.clientWidth > (tabsRef.value?.clientWidth ?? 0) && localStorage.getItem('tabbarScrollTip') === undefined) {
|
||||
localStorage.setItem('tabbarScrollTip', '')
|
||||
Message.info('标签栏数量超过展示区域范围,可以将鼠标移到标签栏上,通过鼠标滚轮滑动浏览', {
|
||||
title: '温馨提示',
|
||||
@ -58,12 +55,12 @@ function tabbarScrollTip() {
|
||||
}
|
||||
}
|
||||
function handlerMouserScroll(event: WheelEvent) {
|
||||
tabsRef.value.scrollBy({
|
||||
tabsRef.value?.scrollBy({
|
||||
left: event.deltaY || event.detail,
|
||||
})
|
||||
}
|
||||
function scrollTo(offsetLeft: number) {
|
||||
tabsRef.value.scrollTo({
|
||||
tabsRef.value?.scrollTo({
|
||||
left: offsetLeft - 50,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
|
@ -11,7 +11,7 @@ withDefaults(
|
||||
|
||||
const value = defineModel<T>()
|
||||
|
||||
const inputRef = ref()
|
||||
const inputRef = useTemplateRef('inputRef')
|
||||
|
||||
defineExpose({
|
||||
ref: inputRef,
|
||||
@ -20,6 +20,6 @@ defineExpose({
|
||||
|
||||
<template>
|
||||
<div class="relative w-full lg-w-48">
|
||||
<input v-model="value" type="text" :placeholder="placeholder" :disabled="disabled" class="relative block w-full border-0 rounded-md bg-white px-2.5 py-1.5 text-sm shadow-sm ring-1 ring-stone-2 ring-inset disabled-cursor-not-allowed dark-bg-dark disabled-opacity-50 focus-outline-none focus-ring-2 dark-ring-stone-8 focus-ring-ui-primary placeholder-stone-4 dark-placeholder-stone-5">
|
||||
<input ref="inputRef" v-model="value" type="text" :placeholder="placeholder" :disabled="disabled" class="relative block w-full border-0 rounded-md bg-white px-2.5 py-1.5 text-sm shadow-sm ring-1 ring-stone-2 ring-inset disabled-cursor-not-allowed dark-bg-dark disabled-opacity-50 focus-outline-none focus-ring-2 dark-ring-stone-8 focus-ring-ui-primary placeholder-stone-4 dark-placeholder-stone-5">
|
||||
</div>
|
||||
</template>
|
||||
|
@ -22,7 +22,7 @@ const pcasBoth = ref([
|
||||
{ code: '330106', name: '西湖区' },
|
||||
])
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRef = useTemplateRef<FormInstance>('formRef')
|
||||
const ruleForm = ref({
|
||||
pca: [],
|
||||
})
|
||||
|
@ -21,7 +21,6 @@ const redirect = ref(route.query.redirect?.toString() ?? settingsStore.settings.
|
||||
const account = ref<string>()
|
||||
// 表单类型
|
||||
const formType = ref<'login' | 'register' | 'resetPassword'>('login')
|
||||
const formRef = ref()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -35,7 +34,6 @@ const formRef = ref()
|
||||
<Transition name="fade" mode="out-in">
|
||||
<LoginForm
|
||||
v-if="formType === 'login'"
|
||||
ref="formRef"
|
||||
:account
|
||||
@on-login="router.push(redirect)"
|
||||
@on-register="(account) => { formType = 'register'; account = account }"
|
||||
@ -43,14 +41,12 @@ const formRef = ref()
|
||||
/>
|
||||
<RegisterForm
|
||||
v-else-if="formType === 'register'"
|
||||
ref="formRef"
|
||||
:account
|
||||
@on-register="(account) => { formType = 'login'; account = account }"
|
||||
@on-login="formType = 'login'"
|
||||
/>
|
||||
<ResetPasswordForm
|
||||
v-else-if="formType === 'resetPassword'"
|
||||
ref="formRef"
|
||||
:account
|
||||
@on-reset-password="(account) => { formType = 'login'; account = account }"
|
||||
@on-login="formType = 'login'"
|
||||
|
@ -15,7 +15,7 @@ defineOptions({
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const formRef = useTemplateRef<FormInstance>('formRef')
|
||||
const form = ref({
|
||||
password: '',
|
||||
newpassword: '',
|
||||
|
@ -7,7 +7,7 @@ meta:
|
||||
import { CountTo } from 'vue3-count-to'
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
const countToRef = ref()
|
||||
const countToRef = useTemplateRef<any>('countToRef')
|
||||
const form = ref({
|
||||
startVal: 0,
|
||||
endVal: 2017,
|
||||
|
@ -9,10 +9,13 @@ import Alert from './components/alert.vue'
|
||||
import 'cropperjs/dist/cropper.css'
|
||||
|
||||
let cropper: any = null
|
||||
const imageRef = ref()
|
||||
const imageRef = useTemplateRef('imageRef')
|
||||
const cropImage = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
if (!imageRef.value) {
|
||||
return
|
||||
}
|
||||
cropper = new Cropper(imageRef.value, {
|
||||
aspectRatio: 1,
|
||||
viewMode: 1,
|
||||
|
@ -7,10 +7,10 @@ meta:
|
||||
import * as Echarts from 'echarts'
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
const chart1Ref = ref()
|
||||
const chart2Ref = ref()
|
||||
const chart3Ref = ref()
|
||||
const chart4Ref = ref()
|
||||
const chart1Ref = useTemplateRef('chart1Ref')
|
||||
const chart2Ref = useTemplateRef('chart2Ref')
|
||||
const chart3Ref = useTemplateRef('chart3Ref')
|
||||
const chart4Ref = useTemplateRef('chart4Ref')
|
||||
let chart1: any
|
||||
let chart2: any
|
||||
let chart3: any
|
||||
|
@ -12,7 +12,7 @@ defineOptions({
|
||||
name: 'ComponentExampleSignCanvas',
|
||||
})
|
||||
|
||||
const esignRef = ref()
|
||||
const esignRef = useTemplateRef<any>('esignRef')
|
||||
const options = ref({
|
||||
lineWidth: 6,
|
||||
lineColor: '#000000',
|
||||
|
@ -11,7 +11,7 @@ const url1 = ref('')
|
||||
const url2 = ref('')
|
||||
const url3 = ref('')
|
||||
|
||||
const canvasRef = ref()
|
||||
const canvasRef = useTemplateRef('canvasRef')
|
||||
|
||||
onMounted(() => {
|
||||
// 渲染成 img 标签
|
||||
|
@ -7,10 +7,10 @@ meta:
|
||||
import VChart from '@visactor/vchart'
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
const chart1Ref = ref()
|
||||
const chart2Ref = ref()
|
||||
const chart3Ref = ref()
|
||||
const chart4Ref = ref()
|
||||
const chart1Ref = useTemplateRef('chart1Ref')
|
||||
const chart2Ref = useTemplateRef('chart2Ref')
|
||||
const chart3Ref = useTemplateRef('chart3Ref')
|
||||
const chart4Ref = useTemplateRef('chart4Ref')
|
||||
let chart1: any
|
||||
let chart2: any
|
||||
let chart3: any
|
||||
@ -24,6 +24,9 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
function initChart1() {
|
||||
if (!chart1Ref.value) {
|
||||
return
|
||||
}
|
||||
const spec: any = {
|
||||
type: 'bar',
|
||||
data: {
|
||||
@ -73,6 +76,9 @@ function initChart1() {
|
||||
chart1.renderSync()
|
||||
}
|
||||
function initChart2() {
|
||||
if (!chart2Ref.value) {
|
||||
return
|
||||
}
|
||||
const spec: any = {
|
||||
type: 'line',
|
||||
data: {
|
||||
@ -139,6 +145,9 @@ function initChart2() {
|
||||
chart2.renderSync()
|
||||
}
|
||||
function initChart3() {
|
||||
if (!chart3Ref.value) {
|
||||
return
|
||||
}
|
||||
const spec: any = {
|
||||
type: 'common',
|
||||
padding: {
|
||||
@ -300,6 +309,9 @@ function initChart3() {
|
||||
chart3.renderSync()
|
||||
}
|
||||
function initChart4() {
|
||||
if (!chart4Ref.value) {
|
||||
return
|
||||
}
|
||||
const spec: any = {
|
||||
type: 'radar',
|
||||
data: [
|
||||
@ -397,7 +409,6 @@ function initChart4() {
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
chart4 = new VChart(spec, { dom: chart4Ref.value })
|
||||
chart4.renderSync()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user