修改 云南测试问题
This commit is contained in:
@@ -97,9 +97,9 @@ export function getXbLineRank(data:any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 数据补招
|
// 数据补招
|
||||||
export function recall(data:any) {
|
export function FullRecall(data:any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/data-processing-boot/data/recall',
|
url: '/data-processing-boot/data/FullRecall',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
|
|||||||
11221
src/assets/map/云南.json
11221
src/assets/map/云南.json
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="position: relative">
|
<div style="position: relative">
|
||||||
<div class="bars_w" ref="chartMap" id="chartMap"></div>
|
<div class="bars_w" ref="chartMap" id="chartMap"></div>
|
||||||
<span @click="circle" v-show="showCircle" class="iconfont icon-back"></span>
|
<!-- <span @click="circle" v-show="showCircle" class="iconfont icon-back"></span> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ const myCharts = ref()
|
|||||||
const showCircle = ref(false)
|
const showCircle = ref(false)
|
||||||
|
|
||||||
const fetchConfig = async (name: string) => {
|
const fetchConfig = async (name: string) => {
|
||||||
const res = await import(`../../assets/map/${name}.json`)
|
const res = await import(`../../assets/map/${name.replace(/市$/, "")}.json`)
|
||||||
return res.default
|
return res.default
|
||||||
// GetEchar(res.default)
|
// GetEchar(res.default)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,9 @@
|
|||||||
{{ Math.floor(row.eventValue * 10000) / 100 }}
|
{{ Math.floor(row.eventValue * 10000) / 100 }}
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
<vxe-column field="eventReason" width="110px" title="暂降类型">
|
<vxe-column field="eventType" width="110px" title="暂降类型">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
{{ eventType.filter(item => item.id == row.eventReason)[0]?.name || '/' }}
|
{{ event.filter(item => item.id == row.eventType)[0]?.name || '/' }}
|
||||||
</template>
|
</template>
|
||||||
</vxe-column>
|
</vxe-column>
|
||||||
</vxe-table>
|
</vxe-table>
|
||||||
@@ -38,7 +38,7 @@ import { mainHeight } from '@/utils/layout'
|
|||||||
import { useDictData } from '@/stores/dictData'
|
import { useDictData } from '@/stores/dictData'
|
||||||
import MQTT from '@/utils/mqtt'
|
import MQTT from '@/utils/mqtt'
|
||||||
const dictData = useDictData()
|
const dictData = useDictData()
|
||||||
const eventType = dictData.getBasicData('Event_Type')
|
const event = dictData.getBasicData('Event_Statis')
|
||||||
import { useAdminInfo } from '@/stores/adminInfo'
|
import { useAdminInfo } from '@/stores/adminInfo'
|
||||||
const adminInfo = useAdminInfo()
|
const adminInfo = useAdminInfo()
|
||||||
const height = mainHeight(-20)
|
const height = mainHeight(-20)
|
||||||
@@ -63,6 +63,7 @@ const init = async () => {
|
|||||||
// 设置消息接收回调
|
// 设置消息接收回调
|
||||||
mqttClient.onMessage((topic, message) => {
|
mqttClient.onMessage((topic, message) => {
|
||||||
const msg = JSON.parse(message.toString())
|
const msg = JSON.parse(message.toString())
|
||||||
|
console.log("🚀 ~ init ~ msg:", msg)
|
||||||
if (msg.deptList.includes(adminInfo.$state.deptId)) {
|
if (msg.deptList.includes(adminInfo.$state.deptId)) {
|
||||||
drawer.value = true
|
drawer.value = true
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
|
|||||||
@@ -52,7 +52,10 @@ class MQTT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const mqttUrl = localStorage.getItem('MqttUrl') || 'ws://192.168.1.68:8083/mqtt'
|
const mqttUrl =
|
||||||
|
localStorage.getItem('MqttUrl') == 'null'
|
||||||
|
? 'ws://192.168.1.68:8083/mqtt'
|
||||||
|
: localStorage.getItem('MqttUrl')
|
||||||
|
|
||||||
this.client = mqtt.connect(mqttUrl, this.defaultOptions as IClientOptions)
|
this.client = mqtt.connect(mqttUrl, this.defaultOptions as IClientOptions)
|
||||||
this.setupEventListeners()
|
this.setupEventListeners()
|
||||||
|
|||||||
@@ -73,8 +73,11 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:operation>
|
||||||
|
<el-button :icon='Download' type='primary' @click='download'>下载波形</el-button>
|
||||||
|
</template>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<Table ref="tableRef" />
|
<Table ref="tableRef" :checkboxConfig='checkboxConfig'/>
|
||||||
</div>
|
</div>
|
||||||
<div :style="{ height: pageHeight.height }" style="padding: 10px; overflow: hidden" v-if="!view">
|
<div :style="{ height: pageHeight.height }" style="padding: 10px; overflow: hidden" v-if="!view">
|
||||||
<waveForm ref="waveFormRef" senior :boxoList="boxoList" :wp="wp" @backbxlb="backbxlb" />
|
<waveForm ref="waveFormRef" senior :boxoList="boxoList" :wp="wp" @backbxlb="backbxlb" />
|
||||||
@@ -84,12 +87,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, provide } from 'vue'
|
import { ref, onMounted, provide } from 'vue'
|
||||||
import TableStore from '@/utils/tableStore'
|
import TableStore from '@/utils/tableStore'
|
||||||
|
import { Download } from '@element-plus/icons-vue'
|
||||||
import Table from '@/components/table/index.vue'
|
import Table from '@/components/table/index.vue'
|
||||||
import TableHeader from '@/components/table/header/index.vue'
|
import TableHeader from '@/components/table/header/index.vue'
|
||||||
import { mainHeight } from '@/utils/layout'
|
import { mainHeight } from '@/utils/layout'
|
||||||
import waveForm from '@/components/echarts/waveForm.vue'
|
import waveForm from '@/components/echarts/waveForm.vue'
|
||||||
import { getMonitorEventAnalyseWave } from '@/api/event-boot/transient'
|
import { getMonitorEventAnalyseWave } from '@/api/event-boot/transient'
|
||||||
import { useDictData } from '@/stores/dictData'
|
import { useDictData } from '@/stores/dictData'
|
||||||
|
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||||
|
import { VxeTablePropTypes } from 'vxe-table'
|
||||||
|
import { downloadWaveFile } from '@/api/event-boot/transient'
|
||||||
const dictData = useDictData()
|
const dictData = useDictData()
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'harmonic-boot/area/TransientEventList'
|
name: 'harmonic-boot/area/TransientEventList'
|
||||||
@@ -107,6 +114,7 @@ const tableStore = new TableStore({
|
|||||||
url: '/event-boot/transient/getTransientValue',
|
url: '/event-boot/transient/getTransientValue',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
column: [
|
column: [
|
||||||
|
{ width: '60', type: 'checkbox' },
|
||||||
{
|
{
|
||||||
field: 'index',
|
field: 'index',
|
||||||
title: '序号',
|
title: '序号',
|
||||||
@@ -238,9 +246,37 @@ provide('tableStore', tableStore)
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
tableStore.index()
|
tableStore.index()
|
||||||
})
|
})
|
||||||
|
const checkboxConfig = reactive<VxeTablePropTypes.CheckboxConfig<any>>({
|
||||||
|
checkMethod: ({ row }) => {
|
||||||
|
return row.fileFlag === 1
|
||||||
|
}
|
||||||
|
})
|
||||||
const backbxlb = () => {
|
const backbxlb = () => {
|
||||||
view.value = true
|
view.value = true
|
||||||
view2.value = false
|
view2.value = false
|
||||||
}
|
}
|
||||||
|
// 下载波形
|
||||||
|
const download = () => {
|
||||||
|
if (!tableStore.table.selection.length) {
|
||||||
|
ElMessage.warning('请选择数据')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
downloadWaveFile({
|
||||||
|
lineId: tableStore.table.selection.map((item: any) => item.eventId)
|
||||||
|
}).then((res: any) => {
|
||||||
|
if (res.type == 'application/json') {
|
||||||
|
ElMessage.warning('暂无可下载的波形文件!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ElMessage.success('下载中。。。!')
|
||||||
|
let blob = new Blob([res], { type: 'application/zip' }) // console.log(blob) // var href = window.URL.createObjectURL(blob); //创建下载的链接
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a') // 创建a标签
|
||||||
|
link.href = url
|
||||||
|
link.download = '波形分析下载' // 设置下载的文件名
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click() //执行下载
|
||||||
|
document.body.removeChild(link) //释放标签
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -110,13 +110,13 @@
|
|||||||
<div class="online_main">
|
<div class="online_main">
|
||||||
<el-tabs v-model="activeName" type="border-card" @tab-click="handleClick">
|
<el-tabs v-model="activeName" type="border-card" @tab-click="handleClick">
|
||||||
<el-tab-pane :name="0" :lazy="true" label="数据完整性列表">
|
<el-tab-pane :name="0" :lazy="true" label="数据完整性列表">
|
||||||
<Table
|
<Table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:tree-config="{ transform: true, parentField: 'uPid', rowField: 'uId' }"
|
:tree-config="{ transform: true, parentField: 'uPid', rowField: 'uId' }"
|
||||||
:checkbox-config="{ labelField: 'name', checkMethod: ({ row }) => true }"
|
:checkbox-config="{ labelField: 'name', checkMethod: ({ row }) => true }"
|
||||||
:scroll-y="{ enabled: true }"
|
:scroll-y="{ enabled: true }"
|
||||||
v-if="activeName == 0"
|
v-if="activeName == 0"
|
||||||
/>
|
/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :name="1" :lazy="true" label="数据完整性图表">
|
<el-tab-pane :name="1" :lazy="true" label="数据完整性图表">
|
||||||
<charts v-if="activeName == 1" ref="chartsRef" />
|
<charts v-if="activeName == 1" ref="chartsRef" />
|
||||||
@@ -127,12 +127,15 @@
|
|||||||
补招时间:
|
补招时间:
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="timeData"
|
v-model="timeData"
|
||||||
type="daterange"
|
type="datetimerange"
|
||||||
format="YYYY-MM-DD"
|
format="YYYY-MM-DD HH:mm:00"
|
||||||
value-format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD HH:mm:00"
|
||||||
range-separator="至"
|
range-separator="至"
|
||||||
|
date-format="YYYY-MM-DD"
|
||||||
|
time-format="HH:mm:00"
|
||||||
start-placeholder="开始日期"
|
start-placeholder="开始日期"
|
||||||
end-placeholder="结束日期"
|
end-placeholder="结束日期"
|
||||||
|
style="width: 400px"
|
||||||
:disabledDate="disabledDate"
|
:disabledDate="disabledDate"
|
||||||
/>
|
/>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -148,7 +151,7 @@
|
|||||||
import { ref, onMounted, watch } from 'vue'
|
import { ref, onMounted, watch } from 'vue'
|
||||||
import { useDictData } from '@/stores/dictData'
|
import { useDictData } from '@/stores/dictData'
|
||||||
import DatePicker from '@/components/form/datePicker/index.vue'
|
import DatePicker from '@/components/form/datePicker/index.vue'
|
||||||
import { getAreaDept, recall } from '@/api/harmonic-boot/area'
|
import { getAreaDept, FullRecall } from '@/api/harmonic-boot/area'
|
||||||
import TableHeader from '@/components/table/header/index.vue'
|
import TableHeader from '@/components/table/header/index.vue'
|
||||||
import TableStore from '@/utils/tableStore'
|
import TableStore from '@/utils/tableStore'
|
||||||
import Table from '@/components/table/index.vue'
|
import Table from '@/components/table/index.vue'
|
||||||
@@ -182,7 +185,6 @@ const getTreeData = async () => {
|
|||||||
idArr.value.push(element.id)
|
idArr.value.push(element.id)
|
||||||
})
|
})
|
||||||
treeData.value = JSON.parse(JSON.stringify(res.data))
|
treeData.value = JSON.parse(JSON.stringify(res.data))
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,17 +330,15 @@ const tableStore = new TableStore({
|
|||||||
chartsRef.value && chartsRef.value.getTableStoreParams(tableStore.table.params)
|
chartsRef.value && chartsRef.value.getTableStoreParams(tableStore.table.params)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
activeName.value == 0 && tableRef.value && tableRef.value.getRef().setAllTreeExpand(true)
|
activeName.value == 0 && tableRef.value && tableRef.value.getRef().setAllTreeExpand(true)
|
||||||
|
|
||||||
}, 0)
|
}, 0)
|
||||||
},
|
},
|
||||||
resetCallback:() =>{
|
resetCallback: () => {
|
||||||
// 重置表单数据到默认值
|
// 重置表单数据到默认值
|
||||||
formData.value.statisticalType = classificationData[0]
|
formData.value.statisticalType = classificationData[0]
|
||||||
formData.value.deptIndex = treeData.value[0]?.id
|
formData.value.deptIndex = treeData.value[0]?.id
|
||||||
formData.value.scale = voltageleveloption
|
formData.value.scale = voltageleveloption
|
||||||
formData.value.manufacturer = terminaloption
|
formData.value.manufacturer = terminaloption
|
||||||
formData.value.loadType = interfereoption
|
formData.value.loadType = interfereoption
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -409,7 +409,7 @@ const makeUpSubmit = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
recall({
|
FullRecall({
|
||||||
monitorId: tableRef.value
|
monitorId: tableRef.value
|
||||||
.getRef()
|
.getRef()
|
||||||
.getCheckboxRecords()
|
.getCheckboxRecords()
|
||||||
@@ -418,9 +418,8 @@ const makeUpSubmit = () => {
|
|||||||
reCallEndTime: timeData.value[1],
|
reCallEndTime: timeData.value[1],
|
||||||
reCallStartTime: timeData.value[0]
|
reCallStartTime: timeData.value[0]
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
|
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: "补招命令下发成功",
|
message: '补招命令下发成功',
|
||||||
type: 'success'
|
type: 'success'
|
||||||
})
|
})
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
@@ -441,7 +440,6 @@ watch(
|
|||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
// .online {
|
// .online {
|
||||||
// width: 100%;
|
// width: 100%;
|
||||||
// height: 100%;
|
// height: 100%;
|
||||||
|
|||||||
@@ -67,11 +67,11 @@
|
|||||||
<!-- <el-form :inline="true">
|
<!-- <el-form :inline="true">
|
||||||
|
|
||||||
</el-form> -->
|
</el-form> -->
|
||||||
<div id="canvas" class="mt10" :style="height1" style="overflow-y: auto" v-loading="loading">
|
<div id="canvas" class="mt10" :style="height1" style="overflow-y: auto;overflow-x: hidden" v-loading="loading">
|
||||||
<my-echart
|
<my-echart
|
||||||
:options="item.option"
|
:options="item.option"
|
||||||
v-for="item in list"
|
v-for="item in list"
|
||||||
style="min-height: 210px"
|
style="min-height: 190px"
|
||||||
:style="height"
|
:style="height"
|
||||||
@triggerPoint="triggerPoint"
|
@triggerPoint="triggerPoint"
|
||||||
@group="group"
|
@group="group"
|
||||||
@@ -1926,6 +1926,8 @@ const triggerPoint = (data: any) => {
|
|||||||
}
|
}
|
||||||
// 计算高度
|
// 计算高度
|
||||||
const selectChange = (flag: boolean) => {
|
const selectChange = (flag: boolean) => {
|
||||||
|
// const height1: any = ref(mainHeight(160))
|
||||||
|
height1.value = mainHeight(flag ? 220 : 160)
|
||||||
height.value = mainHeight(flag ? 220 : 160, list.value.length > 3 ? 3 : list.value.length)
|
height.value = mainHeight(flag ? 220 : 160, list.value.length > 3 ? 3 : list.value.length)
|
||||||
}
|
}
|
||||||
const backbxlb = () => {
|
const backbxlb = () => {
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ const info = async (list: any) => {
|
|||||||
left: '80px',
|
left: '80px',
|
||||||
top: '18px'
|
top: '18px'
|
||||||
},
|
},
|
||||||
color: ['yellow', 'green', 'red'],
|
color: ['#DAA520', 'green', 'red'],
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'log',
|
type: 'log',
|
||||||
min: '0.001',
|
min: '0.001',
|
||||||
@@ -438,7 +438,7 @@ const gongfunction = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (xx < 0.5) {
|
} else if (xx < 0.5) {
|
||||||
if (yy > 70) {
|
if (yy > 50) {
|
||||||
standF++
|
standF++
|
||||||
pointF.value.push({
|
pointF.value.push({
|
||||||
value: point,
|
value: point,
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ const tableStore = new TableStore({
|
|||||||
beforeSearchFun: () => {},
|
beforeSearchFun: () => {},
|
||||||
loadCallback: () => {
|
loadCallback: () => {
|
||||||
geoCoordMap.value = []
|
geoCoordMap.value = []
|
||||||
|
list.value=[]
|
||||||
header.value.areaRef.change()
|
header.value.areaRef.change()
|
||||||
// 处理地图数据
|
// 处理地图数据
|
||||||
tableStore.table.data.eventHeatMapValue.forEach(val => {
|
tableStore.table.data.eventHeatMapValue.forEach(val => {
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ const init = () => {
|
|||||||
name: '%'
|
name: '%'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
color: ['#007D7B','#07CCCA', 'green','red'],
|
color: ['#FF8C00', '#00BFFF', 'green', 'red'],
|
||||||
options: {
|
options: {
|
||||||
dataZoom: null,
|
dataZoom: null,
|
||||||
series: [
|
series: [
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ const init = () => {
|
|||||||
name: '%'
|
name: '%'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
color: ['#007D7B', 'green', 'red'],
|
color: ['#DAA520', 'green', 'red'],
|
||||||
options: {
|
options: {
|
||||||
dataZoom: null,
|
dataZoom: null,
|
||||||
series: [
|
series: [
|
||||||
@@ -135,13 +135,13 @@ const init = () => {
|
|||||||
name: '可容忍事件',
|
name: '可容忍事件',
|
||||||
type: 'scatter',
|
type: 'scatter',
|
||||||
symbol: 'circle',
|
symbol: 'circle',
|
||||||
data: gongData.pointI
|
data: gongData.pointF
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '不可容忍事件',
|
name: '不可容忍事件',
|
||||||
type: 'scatter',
|
type: 'scatter',
|
||||||
symbol: 'circle',
|
symbol: 'circle',
|
||||||
data: gongData.pointIun
|
data: gongData.pointFun
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -205,6 +205,8 @@ function gongfunction(arr: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else if (xx <= 0.5) {
|
} else if (xx <= 0.5) {
|
||||||
|
|
||||||
|
|
||||||
if (yy > 120 || yy < 70) {
|
if (yy > 120 || yy < 70) {
|
||||||
unstandI++
|
unstandI++
|
||||||
pointIun.push({
|
pointIun.push({
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="first">
|
<div class="first">
|
||||||
<div class="mb10">DISDIP表格(国际发配电联盟UNIPEDE)</div>
|
<div class="mb10">DISDIP表格(国际发配电联盟UNIPEDE)</div>
|
||||||
<div style="flex: 1; overflow: hidden">
|
<div style="flex: 1; overflow: hidden">
|
||||||
<vxe-table v-bind="defaultAttribute" height="400px" size="mini" :data="firstData">
|
<vxe-table v-bind="defaultAttribute" height="420px" size="mini" :data="firstData">
|
||||||
<vxe-colgroup title="剩余电压" field="name" width="80px"></vxe-colgroup>
|
<vxe-colgroup title="剩余电压" field="name" width="80px"></vxe-colgroup>
|
||||||
<vxe-colgroup title="持续时间">
|
<vxe-colgroup title="持续时间">
|
||||||
<vxe-column field="twentyMs" title="20ms" :formatter="formatter" />
|
<vxe-column field="twentyMs" title="20ms" :formatter="formatter" />
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="overview-right-item" style="padding-top: 0">
|
<div class="overview-right-item" style="padding-top: 0">
|
||||||
<div class="overview-right-item-title">
|
<div class="overview-right-item-title">
|
||||||
<div style="font-weight: 700">未处理暂态事件</div>
|
<div style="font-weight: 700">暂态事件</div>
|
||||||
<!-- <el-button type="primary" size="small" icon="el-icon-Promotion" @click="jump">事件关联分析</el-button> -->
|
<!-- <el-button type="primary" size="small" icon="el-icon-Promotion" @click="jump">事件关联分析</el-button> -->
|
||||||
</div>
|
</div>
|
||||||
<div style="flex: 1; overflow: hidden">
|
<div style="flex: 1; overflow: hidden">
|
||||||
|
|||||||
Reference in New Issue
Block a user