Merge branch 'zyl' of http://xny.yj-3d.com:3000/zhouyulong/electron-4 into zyl
This commit is contained in:
Binary file not shown.
@ -3,6 +3,7 @@
|
||||
v-show="visible"
|
||||
class="context-menu"
|
||||
:style="{ left: x + 'px', top: y + 'px' }"
|
||||
@mousedown.stop
|
||||
@click.stop
|
||||
@contextmenu.stop
|
||||
>
|
||||
@ -24,8 +25,6 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
@ -52,7 +51,7 @@ defineProps({
|
||||
|
||||
const emit = defineEmits(['update:visible', 'command'])
|
||||
|
||||
const handleClick = (command: string) => {
|
||||
const handleClick = (command: string): void => {
|
||||
emit('update:visible', false)
|
||||
emit('command', command)
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import type { TableColumnCtx } from 'element-plus'
|
||||
import contextMenuCom from './contentMenu.vue'
|
||||
@ -249,6 +249,24 @@ const handleHeaderDragend = (newWidth, oldWidth, column, event) => {
|
||||
item.width = item.width - (item.width / sum) * widthDiff
|
||||
})
|
||||
}
|
||||
const getColKey = (col: any) => {
|
||||
return col.property || col.type || col.label
|
||||
}
|
||||
const applySavedOrder = () => {
|
||||
const saved = localStorage.getItem('graphLabelManageTableOrder')
|
||||
if (!saved) return
|
||||
const order: string[] = JSON.parse(saved)
|
||||
const headerCols = tableRef.value.$refs.tableHeaderRef.columnRows[0]
|
||||
const allCols = tableRef.value.store.states.columns
|
||||
const sortByOrder = (arr: any[]) => {
|
||||
return arr.slice().sort((a, b) => order.indexOf(getColKey(a)) - order.indexOf(getColKey(b)))
|
||||
}
|
||||
const newHeader = sortByOrder(headerCols)
|
||||
const newAll = sortByOrder(allCols)
|
||||
tableRef.value.$refs.tableHeaderRef.columnRows[0] = newHeader
|
||||
tableRef.value.store.states.columns = newAll
|
||||
tableRef.value.doLayout()
|
||||
}
|
||||
|
||||
//搜索
|
||||
var photoName = ref('')
|
||||
@ -343,9 +361,36 @@ let openfunc = () => {
|
||||
|
||||
onMounted(() => {
|
||||
eventBus.on('openGraphLabel', openfunc)
|
||||
nextTick(() => {
|
||||
const headerEl = tableRef.value?.$refs?.tableHeaderRef?.$el?.querySelector('tr')
|
||||
if (!headerEl) return
|
||||
applySavedOrder()
|
||||
sortableInstance = Sortable.create(headerEl, {
|
||||
animation: 150,
|
||||
filter: '.el-table__column--gutter',
|
||||
onEnd: (evt: any) => {
|
||||
const headerCols = tableRef.value.$refs.tableHeaderRef.columnRows[0]
|
||||
const allCols = tableRef.value.store.states.columns
|
||||
const move = (arr: any[], from: number, to: number) => {
|
||||
const item = arr.splice(from, 1)[0]
|
||||
arr.splice(to, 0, item)
|
||||
}
|
||||
move(headerCols, evt.oldIndex, evt.newIndex)
|
||||
move(allCols, evt.oldIndex, evt.newIndex)
|
||||
tableRef.value.store.states.columns = allCols
|
||||
tableRef.value.doLayout()
|
||||
const keys = headerCols.map((c: any) => getColKey(c))
|
||||
localStorage.setItem('graphLabelManageTableOrder', JSON.stringify(keys))
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
onUnmounted(() => {
|
||||
eventBus.off('openGraphLabel', openfunc)
|
||||
if (sortableInstance && typeof sortableInstance.destroy === 'function') {
|
||||
sortableInstance.destroy()
|
||||
sortableInstance = null
|
||||
}
|
||||
})
|
||||
let haventModel = false
|
||||
const getModelList = async () => {
|
||||
@ -586,11 +631,15 @@ const handleContextMenu = (event: MouseEvent, row: TypeNode) => {
|
||||
clickTreeNode = row
|
||||
|
||||
contextMenu.visible = true
|
||||
document.getElementsByClassName('settingPop')[0].removeEventListener('mousedown', func)
|
||||
document.getElementsByClassName('settingPop')[0].addEventListener('mousedown', func)
|
||||
const dialogEl = document.getElementsByClassName('settingPop')[0] as HTMLElement
|
||||
dialogEl.removeEventListener('mousedown', func as EventListener)
|
||||
dialogEl.addEventListener('mousedown', func as EventListener)
|
||||
}
|
||||
let func = () => {
|
||||
document.getElementsByClassName('settingPop')[0].removeEventListener('mousedown', func)
|
||||
let func = (ev: Event) => {
|
||||
const me = ev as MouseEvent
|
||||
const dialogEl = document.getElementsByClassName('settingPop')[0] as HTMLElement
|
||||
dialogEl.removeEventListener('mousedown', func as EventListener)
|
||||
if (me.button !== 0) return
|
||||
setTimeout(() => {
|
||||
contextMenu.visible = false
|
||||
}, 200)
|
||||
@ -645,8 +694,9 @@ const divContextMenu = (event: MouseEvent) => {
|
||||
contextMenu.items = [{ command: 'addType', label: '添加军标类型', icon: 'add' }]
|
||||
|
||||
contextMenu.visible = true
|
||||
document.getElementsByClassName('settingPop')[0].removeEventListener('mousedown', func)
|
||||
document.getElementsByClassName('settingPop')[0].addEventListener('mousedown', func)
|
||||
const dialogEl = document.getElementsByClassName('settingPop')[0] as HTMLElement
|
||||
dialogEl.removeEventListener('mousedown', func as EventListener)
|
||||
dialogEl.addEventListener('mousedown', func as EventListener)
|
||||
}
|
||||
|
||||
const handleMenuCommand = (command: string) => {
|
||||
@ -683,7 +733,8 @@ const handleTypeClick = (row: any) => {
|
||||
// loadModelsByType(row.id)
|
||||
// contextMenu.visible && (contextMenu.visible = false)
|
||||
}
|
||||
const handleClick = () => {
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
if (event.button !== 0) return
|
||||
contextMenu.visible && (contextMenu.visible = false)
|
||||
}
|
||||
|
||||
|
||||
@ -173,7 +173,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue'
|
||||
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import type { TableColumnCtx } from 'element-plus'
|
||||
import contextMenuCom from './contentMenu.vue'
|
||||
@ -242,6 +242,49 @@ const handleHeaderDragend = (newWidth, oldWidth, column, event) => {
|
||||
item.width = item.width - (item.width / sum) * widthDiff
|
||||
})
|
||||
}
|
||||
const getColKey = (col: any) => {
|
||||
return col.property || col.type || col.label
|
||||
}
|
||||
const applySavedOrder = () => {
|
||||
const saved = localStorage.getItem('modelManageTableOrder')
|
||||
if (!saved) return
|
||||
const order: string[] = JSON.parse(saved)
|
||||
const headerCols = tableRef.value.$refs.tableHeaderRef.columnRows[0]
|
||||
const allCols = tableRef.value.store.states.columns
|
||||
const sortByOrder = (arr: any[]) => {
|
||||
return arr.slice().sort((a, b) => order.indexOf(getColKey(a)) - order.indexOf(getColKey(b)))
|
||||
}
|
||||
const newHeader = sortByOrder(headerCols)
|
||||
const newAll = sortByOrder(allCols)
|
||||
tableRef.value.$refs.tableHeaderRef.columnRows[0] = newHeader
|
||||
tableRef.value.store.states.columns = newAll
|
||||
tableRef.value.doLayout()
|
||||
}
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
const headerEl = tableRef.value?.$refs?.tableHeaderRef?.$el?.querySelector('tr')
|
||||
if (!headerEl) return
|
||||
applySavedOrder()
|
||||
sortableInstance = Sortable.create(headerEl, {
|
||||
animation: 150,
|
||||
filter: '.el-table__column--gutter',
|
||||
onEnd: (evt: any) => {
|
||||
const headerCols = tableRef.value.$refs.tableHeaderRef.columnRows[0]
|
||||
const allCols = tableRef.value.store.states.columns
|
||||
const move = (arr: any[], from: number, to: number) => {
|
||||
const item = arr.splice(from, 1)[0]
|
||||
arr.splice(to, 0, item)
|
||||
}
|
||||
move(headerCols, evt.oldIndex, evt.newIndex)
|
||||
move(allCols, evt.oldIndex, evt.newIndex)
|
||||
tableRef.value.store.states.columns = allCols
|
||||
tableRef.value.doLayout()
|
||||
const keys = headerCols.map((c: any) => getColKey(c))
|
||||
localStorage.setItem('modelManageTableOrder', JSON.stringify(keys))
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
//滚动加载
|
||||
const disabledScroll = ref(false)
|
||||
@ -349,6 +392,10 @@ onMounted(() => {
|
||||
onUnmounted(() => {
|
||||
eventBus.off('settingPop', setFunc)
|
||||
eventBus.off('openModelManage', openfunc)
|
||||
if (sortableInstance && typeof sortableInstance.destroy === 'function') {
|
||||
sortableInstance.destroy()
|
||||
sortableInstance = null
|
||||
}
|
||||
})
|
||||
//获取模型列表
|
||||
// eventBus.on('settingPop', (data) => {
|
||||
@ -651,11 +698,15 @@ const handleContextMenu = (event: MouseEvent, row: TypeNode) => {
|
||||
clickTreeNode = row
|
||||
|
||||
contextMenu.visible = true
|
||||
document.getElementsByClassName('settingPop')[0].removeEventListener('mousedown', func)
|
||||
document.getElementsByClassName('settingPop')[0].addEventListener('mousedown', func)
|
||||
const dialogEl = document.getElementsByClassName('settingPop')[0] as HTMLElement
|
||||
dialogEl.removeEventListener('mousedown', func as EventListener)
|
||||
dialogEl.addEventListener('mousedown', func as EventListener)
|
||||
}
|
||||
let func = () => {
|
||||
document.getElementsByClassName('settingPop')[0].removeEventListener('mousedown', func)
|
||||
let func = (ev: Event) => {
|
||||
const me = ev as MouseEvent
|
||||
const dialogEl = document.getElementsByClassName('settingPop')[0] as HTMLElement
|
||||
dialogEl.removeEventListener('mousedown', func as EventListener)
|
||||
if (me.button !== 0) return
|
||||
setTimeout(() => {
|
||||
contextMenu.visible = false
|
||||
}, 200)
|
||||
@ -713,8 +764,9 @@ const divContextMenu = (event: MouseEvent) => {
|
||||
contextMenu.items = [{ command: 'addType', label: '添加模型类型', icon: 'add' }]
|
||||
|
||||
contextMenu.visible = true
|
||||
document.getElementsByClassName('settingPop')[0].removeEventListener('mousedown', func)
|
||||
document.getElementsByClassName('settingPop')[0].addEventListener('mousedown', func)
|
||||
const dialogEl = document.getElementsByClassName('settingPop')[0] as HTMLElement
|
||||
dialogEl.removeEventListener('mousedown', func as EventListener)
|
||||
dialogEl.addEventListener('mousedown', func as EventListener)
|
||||
}
|
||||
|
||||
const handleMenuCommand = (command: string) => {
|
||||
@ -751,7 +803,8 @@ const handleTypeClick = (row: any) => {
|
||||
// loadModelsByType(row.id)
|
||||
// contextMenu.visible && (contextMenu.visible = false)
|
||||
}
|
||||
const handleClick = () => {
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
if (event.button !== 0) return
|
||||
contextMenu.visible && (contextMenu.visible = false)
|
||||
}
|
||||
|
||||
|
||||
@ -199,7 +199,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue'
|
||||
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import type { TableColumnCtx } from 'element-plus'
|
||||
import contextMenuCom from './contentMenu.vue'
|
||||
import Sortable from 'sortablejs'
|
||||
@ -275,6 +275,24 @@ const handleHeaderDragend = (newWidth, oldWidth, column, event) => {
|
||||
item.width = item.width - (item.width / sum) * widthDiff
|
||||
})
|
||||
}
|
||||
const getColKey = (col: any) => {
|
||||
return col.property || col.type || col.label
|
||||
}
|
||||
const applySavedOrder = () => {
|
||||
const saved = localStorage.getItem('photoManageTableOrder')
|
||||
if (!saved) return
|
||||
const order: string[] = JSON.parse(saved)
|
||||
const headerCols = tableRef.value.$refs.tableHeaderRef.columnRows[0]
|
||||
const allCols = tableRef.value.store.states.columns
|
||||
const sortByOrder = (arr: any[]) => {
|
||||
return arr.slice().sort((a, b) => order.indexOf(getColKey(a)) - order.indexOf(getColKey(b)))
|
||||
}
|
||||
const newHeader = sortByOrder(headerCols)
|
||||
const newAll = sortByOrder(allCols)
|
||||
tableRef.value.$refs.tableHeaderRef.columnRows[0] = newHeader
|
||||
tableRef.value.store.states.columns = newAll
|
||||
tableRef.value.doLayout()
|
||||
}
|
||||
|
||||
var expandedKeys: any = ref([])
|
||||
// 获取当前所有展开节点的key
|
||||
@ -414,9 +432,36 @@ let openfunc = () => {
|
||||
|
||||
onMounted(() => {
|
||||
eventBus.on('openPhotoManage', openfunc)
|
||||
nextTick(() => {
|
||||
const headerEl = tableRef.value?.$refs?.tableHeaderRef?.$el?.querySelector('tr')
|
||||
if (!headerEl) return
|
||||
applySavedOrder()
|
||||
sortableInstance = Sortable.create(headerEl, {
|
||||
animation: 150,
|
||||
filter: '.el-table__column--gutter',
|
||||
onEnd: (evt: any) => {
|
||||
const headerCols = tableRef.value.$refs.tableHeaderRef.columnRows[0]
|
||||
const allCols = tableRef.value.store.states.columns
|
||||
const move = (arr: any[], from: number, to: number) => {
|
||||
const item = arr.splice(from, 1)[0]
|
||||
arr.splice(to, 0, item)
|
||||
}
|
||||
move(headerCols, evt.oldIndex, evt.newIndex)
|
||||
move(allCols, evt.oldIndex, evt.newIndex)
|
||||
tableRef.value.store.states.columns = allCols
|
||||
tableRef.value.doLayout()
|
||||
const keys = headerCols.map((c: any) => getColKey(c))
|
||||
localStorage.setItem('photoManageTableOrder', JSON.stringify(keys))
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
onUnmounted(() => {
|
||||
eventBus.off('openPhotoManage', openfunc)
|
||||
if (sortableInstance && typeof sortableInstance.destroy === 'function') {
|
||||
sortableInstance.destroy()
|
||||
sortableInstance = null
|
||||
}
|
||||
})
|
||||
let haventModel = false
|
||||
const getModelList = async () => {
|
||||
@ -669,11 +714,15 @@ const handleContextMenu = (event: MouseEvent, row: TypeNode) => {
|
||||
clickTreeNode = row
|
||||
|
||||
contextMenu.visible = true
|
||||
document.getElementsByClassName('settingPop')[0].removeEventListener('mousedown', func)
|
||||
document.getElementsByClassName('settingPop')[0].addEventListener('mousedown', func)
|
||||
const dialogEl = document.getElementsByClassName('settingPop')[0] as HTMLElement
|
||||
dialogEl.removeEventListener('mousedown', func as EventListener)
|
||||
dialogEl.addEventListener('mousedown', func as EventListener)
|
||||
}
|
||||
let func = () => {
|
||||
document.getElementsByClassName('settingPop')[0].removeEventListener('mousedown', func)
|
||||
let func = (ev: Event) => {
|
||||
const me = ev as MouseEvent
|
||||
const dialogEl = document.getElementsByClassName('settingPop')[0] as HTMLElement
|
||||
dialogEl.removeEventListener('mousedown', func as EventListener)
|
||||
if (me.button !== 0) return
|
||||
setTimeout(() => {
|
||||
contextMenu.visible = false
|
||||
}, 200)
|
||||
@ -737,8 +786,9 @@ const divContextMenu = (event: MouseEvent) => {
|
||||
contextMenu.items = [{ command: 'addType', label: '添加图标类型', icon: 'add' }]
|
||||
|
||||
contextMenu.visible = true
|
||||
document.getElementsByClassName('settingPop')[0].removeEventListener('mousedown', func)
|
||||
document.getElementsByClassName('settingPop')[0].addEventListener('mousedown', func)
|
||||
const dialogEl = document.getElementsByClassName('settingPop')[0] as HTMLElement
|
||||
dialogEl.removeEventListener('mousedown', func as EventListener)
|
||||
dialogEl.addEventListener('mousedown', func as EventListener)
|
||||
}
|
||||
|
||||
const handleMenuCommand = (command: string) => {
|
||||
@ -776,7 +826,8 @@ const handleTypeClick = (row: any) => {
|
||||
// loadModelsByType(row.id)
|
||||
// contextMenu.visible && (contextMenu.visible = false)
|
||||
}
|
||||
const handleClick = () => {
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
if (event.button !== 0) return
|
||||
contextMenu.visible && (contextMenu.visible = false)
|
||||
}
|
||||
|
||||
|
||||
@ -599,7 +599,7 @@ const sysChange = async () => {
|
||||
let data = name_map1.value.filter((item) => item.epsg === systemSetting.value.coordinate)
|
||||
showPosiType.value = data.length
|
||||
}
|
||||
utilsSysChange(eventBus)
|
||||
utilsSysChange(eventBus, false)
|
||||
}
|
||||
const searchWayChange = (val) => {
|
||||
localStorage.setItem('searchWay', val)
|
||||
|
||||
@ -124,6 +124,8 @@
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
min="-180"
|
||||
max="180"
|
||||
placeholder="请输入坐标点经度数值"
|
||||
v-model="longitude"
|
||||
/>
|
||||
@ -136,6 +138,8 @@
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
min="-90"
|
||||
max="90"
|
||||
placeholder="请输入坐标点纬度数值"
|
||||
v-model="latitude"
|
||||
/>
|
||||
@ -390,14 +394,25 @@ const turnToPosition = async () => {
|
||||
let position
|
||||
switch (activeName.value) {
|
||||
case 'first':
|
||||
var point = await new YJ.Tools().sampleHeightMostDetailed(
|
||||
[{ lng: longitude.value, lat: latitude.value, alt: 0 }],
|
||||
window.earth
|
||||
)
|
||||
let lng1 = Number(longitude.value)
|
||||
let lat1 = Number(latitude.value)
|
||||
if (!Number.isFinite(lng1) || !Number.isFinite(lat1)) return { lng: null, lat: null, alt: 0 }
|
||||
if (lng1 > 180) lng1 = ((lng1 + 180) % 360) - 180
|
||||
if (lng1 < -180) lng1 = ((lng1 - 180) % 360) + 180
|
||||
if (lat1 > 90) lat1 = 90
|
||||
if (lat1 < -90) lat1 = -90
|
||||
let alt1 = 0
|
||||
if (Math.abs(lat1) < 89.999) {
|
||||
const p = await new YJ.Tools().sampleHeightMostDetailed(
|
||||
[{ lng: lng1, lat: lat1, alt: 0 }],
|
||||
window.earth
|
||||
)
|
||||
alt1 = p[0].height < 0 ? 0 : p[0].height
|
||||
}
|
||||
position = {
|
||||
lng: longitude.value,
|
||||
lat: latitude.value,
|
||||
alt: point[0].height < 0 ? 0 : point[0].height
|
||||
lng: lng1,
|
||||
lat: lat1,
|
||||
alt: alt1
|
||||
}
|
||||
break
|
||||
case 'second':
|
||||
@ -410,6 +425,10 @@ const turnToPosition = async () => {
|
||||
lng = longitude.value < 0 ? -lng : lng
|
||||
// @ts-ignore (define in dts)
|
||||
lat = latitude.value < 0 ? -lat : lat
|
||||
if (lng > 180) lng = ((lng + 180) % 360) - 180
|
||||
if (lng < -180) lng = ((lng - 180) % 360) + 180
|
||||
if (lat > 90) lat = 90
|
||||
if (lat < -90) lat = -90
|
||||
|
||||
var point2 = await new YJ.Tools().sampleHeightMostDetailed(
|
||||
[{ lng, lat, alt: 0 }],
|
||||
@ -434,6 +453,10 @@ const turnToPosition = async () => {
|
||||
lng = longitude.value < 0 ? -lng : lng
|
||||
// @ts-ignore (define in dts)
|
||||
lat = latitude.value < 0 ? -lat : lat
|
||||
if (lng > 180) lng = ((lng + 180) % 360) - 180
|
||||
if (lng < -180) lng = ((lng - 180) % 360) + 180
|
||||
if (lat > 90) lat = 90
|
||||
if (lat < -90) lat = -90
|
||||
|
||||
var point3 = await new YJ.Tools().sampleHeightMostDetailed(
|
||||
[{ lng, lat, alt: 0 }],
|
||||
@ -482,7 +505,7 @@ const getPosition = async () => {
|
||||
const flyto = async (e) => {
|
||||
await getPosition()
|
||||
console.log(pointPosi.lng, pointPosi.lng !== 0, pointPosi.lat, 'ooppp')
|
||||
if ((!pointPosi.lng && pointPosi.lng !== 0) || (!pointPosi.lat && pointPosi.lng !== 0)) {
|
||||
if ((!pointPosi.lng && pointPosi.lng !== 0) || (!pointPosi.lat && pointPosi.lat !== 0)) {
|
||||
ElMessage({
|
||||
message: '请正确填写坐标信息!',
|
||||
type: 'warning'
|
||||
@ -490,13 +513,14 @@ const flyto = async (e) => {
|
||||
return
|
||||
}
|
||||
if ((window as any).earthPlaceMap === undefined) {
|
||||
(window as any).earthPlaceMap = new Map()
|
||||
;(window as any).earthPlaceMap = new Map()
|
||||
}
|
||||
if ((window as any).earthPlaceMap.size) {
|
||||
(window as any).earthPlaceMap.forEach((item) => {
|
||||
item.remove()
|
||||
})
|
||||
(window as any).earthPlaceMap.clear()
|
||||
;(window as any).earthPlaceMap
|
||||
.forEach((item) => {
|
||||
item.remove()
|
||||
})(window as any)
|
||||
.earthPlaceMap.clear()
|
||||
}
|
||||
new YJ.Global.flyTo(window.earth, {
|
||||
position: { lng: pointPosi.lng, lat: pointPosi.lat, alt: pointPosi.alt + 300 }
|
||||
|
||||
@ -508,7 +508,7 @@ const createEarth = async () => {
|
||||
})
|
||||
|
||||
tree.value.initTreeCallBack()
|
||||
utilsSysChange(eventBus)
|
||||
utilsSysChange(eventBus, true)
|
||||
// @ts-ignore
|
||||
let options = JSON.parse(localStorage.getItem('defaultView'))
|
||||
YJ.Global.setDefaultView(window.earth, options)
|
||||
|
||||
Reference in New Issue
Block a user