Merge branch 'master' of git.code.tencent.com:xian-ncc-da/xian-ncc-da-client
This commit is contained in:
commit
357a52ce2d
@ -7,7 +7,8 @@ export interface createParams {
|
||||
alertType: string;
|
||||
timeType: string;
|
||||
locationType: string;
|
||||
infoJson: string;
|
||||
drivingInfo: string;
|
||||
submissionInfo: string;
|
||||
}
|
||||
|
||||
interface Item {
|
||||
@ -15,7 +16,8 @@ interface Item {
|
||||
alertType: string;
|
||||
timeType: string;
|
||||
locationType: string;
|
||||
infoJson: string;
|
||||
drivingInfo: string;
|
||||
submissionInfo: string;
|
||||
}
|
||||
|
||||
export class PagingQueryParams extends PageQueryDto {
|
||||
|
@ -7,8 +7,8 @@
|
||||
@update:model-value="onUpdate"
|
||||
/>
|
||||
<q-checkbox
|
||||
v-model="pathLineModel.aToB"
|
||||
label="是否A->B"
|
||||
v-model="pathLineModel.isKmIncrease"
|
||||
label="运行方向公里标递增"
|
||||
@update:model-value="onUpdate"
|
||||
/>
|
||||
<q-select
|
||||
|
@ -58,11 +58,11 @@ export class PathLineData extends GraphicDataBase implements IPathLineData {
|
||||
})
|
||||
);
|
||||
}
|
||||
get aToB(): boolean {
|
||||
return this.data.aToB;
|
||||
get isKmIncrease(): boolean {
|
||||
return this.data.isKmIncrease;
|
||||
}
|
||||
set aToB(v: boolean) {
|
||||
this.data.aToB = v;
|
||||
set isKmIncrease(v: boolean) {
|
||||
this.data.isKmIncrease = v;
|
||||
}
|
||||
clone(): PathLineData {
|
||||
return new PathLineData(this.data.cloneMessage());
|
||||
|
@ -255,12 +255,15 @@ export function initDrawApp(dom: HTMLElement): JlDrawApp {
|
||||
};
|
||||
jumpStaitonItems.push(item);
|
||||
});
|
||||
JumpStaitonOptions.subMenu = [
|
||||
JumpStaitonOptions.subMenu = {
|
||||
name: '车站列表',
|
||||
groups: [
|
||||
{
|
||||
name: '车站列表',
|
||||
items: jumpStaitonItems,
|
||||
},
|
||||
];
|
||||
],
|
||||
};
|
||||
DefaultCanvasMenu.update();
|
||||
DefaultCanvasMenu.open(e.global);
|
||||
});
|
||||
|
@ -210,10 +210,10 @@ export async function loadLineDatas(app: GraphicApp) {
|
||||
token: getJwtToken() as string,
|
||||
});
|
||||
msgBroker = new AppWsMsgBroker(app);
|
||||
const states: GraphicState[] = [];
|
||||
msgBroker.subscribe({
|
||||
destination: `/queue/line/${lineId}/device`,
|
||||
messageConverter: (message: Uint8Array) => {
|
||||
const states: GraphicState[] = [];
|
||||
const storage = state.WsLineMessage.deserialize(message);
|
||||
storage.signal.forEach((item) => {
|
||||
states.push(new SignalState(item));
|
||||
@ -230,6 +230,7 @@ export async function loadLineDatas(app: GraphicApp) {
|
||||
msgBroker.subscribe({
|
||||
destination: `/queue/line/${lineId}/train`,
|
||||
messageConverter: (message: Uint8Array) => {
|
||||
const states: GraphicState[] = [];
|
||||
const trainStorage = state.WsLineTrainMessage.deserialize(message);
|
||||
trainStorage.trainInfo.forEach((item) => {
|
||||
states.push(new TrainState(item));
|
||||
|
@ -120,12 +120,11 @@ export async function loadLineNetDatas(app: GraphicApp) {
|
||||
token: getJwtToken() as string,
|
||||
});
|
||||
msgBroker = new AppWsMsgBroker(app);
|
||||
const states: GraphicState[] = [];
|
||||
msgBroker.subscribe({
|
||||
destination: '/queue/lineNet',
|
||||
messageConverter: (message: Uint8Array) => {
|
||||
const storage = state.WsLineNetMessage.deserialize(message);
|
||||
// console.log(storage, 'storage');
|
||||
const states: GraphicState[] = [];
|
||||
storage.offset.forEach((item) => {
|
||||
states.push(new TrainLineState(item));
|
||||
});
|
||||
|
@ -28,8 +28,8 @@ export interface IPathLineData extends GraphicData {
|
||||
set isUp(v: boolean);
|
||||
get kilometerPoints(): KilometerPoint[];
|
||||
set kilometerPoints(kilometerPoints: KilometerPoint[]);
|
||||
get aToB(): boolean;
|
||||
set aToB(v: boolean);
|
||||
get isKmIncrease(): boolean;
|
||||
set isKmIncrease(v: boolean);
|
||||
clone(): IPathLineData;
|
||||
copyFrom(data: IPathLineData): void;
|
||||
eq(other: IPathLineData): boolean;
|
||||
@ -43,15 +43,11 @@ export const pathLineConsts = {
|
||||
export class PathLine extends JlGraphic {
|
||||
static Type = 'PathLine';
|
||||
pathLine: Graphics = new Graphics();
|
||||
aLabel: VectorText = new VectorText('A');
|
||||
bLabel: VectorText = new VectorText('B');
|
||||
_stationPointMap: Map<string, IPointData[]> | null = null; // 两个车站对应坐标点
|
||||
_sortKp: KilometerPoint[] = [];
|
||||
_stationPointMap: Map<string, IPointData[]> | null = null; // val :两个车站点的连线段的端点坐标;key: stationName+stationName 前一车站为公里标大
|
||||
_sortKp: KilometerPoint[] = []; //根据公里标排序的车站点(由小到大)
|
||||
constructor() {
|
||||
super(PathLine.Type);
|
||||
this.addChild(this.pathLine);
|
||||
this.addChild(this.aLabel);
|
||||
this.addChild(this.bLabel);
|
||||
}
|
||||
|
||||
get datas(): IPathLineData {
|
||||
@ -72,12 +68,6 @@ export class PathLine extends JlGraphic {
|
||||
const p = this.datas.points[i];
|
||||
this.pathLine.lineTo(p.x, p.y);
|
||||
}
|
||||
this.aLabel.style.fill = 0xff0000;
|
||||
this.aLabel.setVectorFontSize(14);
|
||||
this.bLabel.style.fill = 0xff0000;
|
||||
this.bLabel.setVectorFontSize(14);
|
||||
this.aLabel.position.set(this.getStartPoint().x, this.getStartPoint().y);
|
||||
this.bLabel.position.set(this.getEndPoint().x, this.getEndPoint().y);
|
||||
}
|
||||
|
||||
get linePoints(): IPointData[] {
|
||||
@ -101,21 +91,37 @@ export class PathLine extends JlGraphic {
|
||||
this._stationPointMap = new Map();
|
||||
for (let i = 0; i < this.sortKp.length - 1; i++) {
|
||||
const kp1 = this.sortKp[i];
|
||||
const kp2 = this.sortKp[i + 1];
|
||||
const kp2 = this.sortKp[i + 1]; // 选取相邻两站
|
||||
const containRefP: IPointData[] = [];
|
||||
this.linePoints.forEach((p) => {
|
||||
const maxX = Math.max(kp1.point.x, kp2.point.x);
|
||||
const minX = Math.min(kp1.point.x, kp2.point.x);
|
||||
const maxY = Math.max(kp1.point.y, kp2.point.y);
|
||||
const minY = Math.min(kp1.point.y, kp2.point.y);
|
||||
if (this.checkSampP(p, kp1.point) || this.checkSampP(p, kp2.point)) {
|
||||
// console.log('相同点')
|
||||
} else if (minX <= p.x && p.x <= maxX && minY <= p.y && p.y <= maxY) {
|
||||
containRefP.push(p);
|
||||
let kp1Flag = false;
|
||||
let kp2Flag = false;
|
||||
// 选取轨迹线相邻两点判断 计算两站线段端点
|
||||
for (let j = 0; j < this.linePoints.length - 1; j++) {
|
||||
const containKp1 = isPointOnLine(
|
||||
this.linePoints[j],
|
||||
this.linePoints[j + 1],
|
||||
kp1.point
|
||||
);
|
||||
const containKp2 = isPointOnLine(
|
||||
this.linePoints[j],
|
||||
this.linePoints[j + 1],
|
||||
kp2.point
|
||||
);
|
||||
if (containKp1 && !kp1Flag) {
|
||||
containRefP.push(kp1.point);
|
||||
kp1Flag = true;
|
||||
}
|
||||
});
|
||||
if (containKp2 && !kp2Flag) {
|
||||
containRefP.push(kp2.point);
|
||||
containRefP.unshift(kp1.point);
|
||||
kp2Flag = true;
|
||||
}
|
||||
if (kp1Flag && kp2Flag) {
|
||||
break;
|
||||
}
|
||||
if (kp1Flag || kp2Flag) {
|
||||
containRefP.push(this.linePoints[j + 1]);
|
||||
}
|
||||
}
|
||||
this._stationPointMap.set(kp2.stName + kp1.stName, containRefP);
|
||||
}
|
||||
}
|
||||
|
@ -162,8 +162,8 @@ export class PathLinePointsEditPlugin extends GraphicInteractionPlugin<PathLine>
|
||||
}
|
||||
onUnselected(g: DisplayObject): void {
|
||||
const pathLine = g as PathLine;
|
||||
if (pathLine.children.length > 3) {
|
||||
pathLine.removeChildren(3);
|
||||
if (pathLine.children.length > 1) {
|
||||
pathLine.removeChildren(1);
|
||||
}
|
||||
}
|
||||
kpTransforming(e: GraphicTransformEvent) {
|
||||
|
@ -61,6 +61,7 @@ export class TrainLine extends JlGraphic {
|
||||
}
|
||||
this.train.texture = this.trainTextures;
|
||||
const pathLines = this.queryStore.queryByType<PathLine>(PathLine.Type);
|
||||
// 筛选目标轨迹线 同线路同方向
|
||||
const goalPath = pathLines.find((pathLine) => {
|
||||
return (
|
||||
pathLine.datas.code === this.states.lineId + '' &&
|
||||
@ -72,31 +73,35 @@ export class TrainLine extends JlGraphic {
|
||||
if (!goalPath) {
|
||||
return;
|
||||
}
|
||||
const kc = this.states.kilometerCode;
|
||||
const kps = goalPath.sortKp;
|
||||
const kc = this.states.kilometerCode; // 当前位置公里标
|
||||
const kps = goalPath.sortKp; // 车站公里标排序
|
||||
const index = kps.findIndex((kp, i) => {
|
||||
if (kps[i + 1]) {
|
||||
return kp.kilometer <= kc && kps[i + 1].kilometer >= kc;
|
||||
}
|
||||
});
|
||||
const k1 = kps[index];
|
||||
const k2 = kps[index + 1];
|
||||
const k1 = kps[index]; // 当前位置相邻车站
|
||||
const k2 = kps[index + 1]; // 当前位置相邻车站
|
||||
if (!k1 || !k2) {
|
||||
return;
|
||||
}
|
||||
const ps = goalPath.stationPointMap.get(k2.stName + k1.stName);
|
||||
const ps = goalPath.stationPointMap.get(k2.stName + k1.stName); // 两站线段端点
|
||||
if (!ps) {
|
||||
return;
|
||||
}
|
||||
const offset = (kc - k1.kilometer) / (k2.kilometer - k1.kilometer);
|
||||
const offset = (kc - k1.kilometer) / (k2.kilometer - k1.kilometer); //车辆在两站偏移量
|
||||
let length = 0;
|
||||
// 如果两站选段存在2个以上的点需要进行端点排序
|
||||
if (ps.length > 2 && ps[0].x === k2.point.x && ps[0].y === k2.point.y) {
|
||||
ps.reverse();
|
||||
}
|
||||
const lengthList = [];
|
||||
for (let i = 0; i < ps.length - 1; i++) {
|
||||
const d = distance(ps[i].x, ps[i].y, ps[i + 1].x, ps[i + 1].y);
|
||||
length += d;
|
||||
lengthList.push(d);
|
||||
}
|
||||
let offsetLength = length * offset;
|
||||
let offsetLength = length * offset; // 偏移长度
|
||||
const indexP = lengthList.findIndex((l) => {
|
||||
offsetLength -= l;
|
||||
return offsetLength <= 0;
|
||||
@ -107,7 +112,7 @@ export class TrainLine extends JlGraphic {
|
||||
const px = startP.x + Math.round(offset * (endP.x - startP.x));
|
||||
const py = startP.y + Math.round(offset * (endP.y - startP.y));
|
||||
const angle = Math.atan2(endP.y - startP.y, endP.x - startP.x);
|
||||
if (!goalPath.datas.aToB) {
|
||||
if (goalPath.datas.isKmIncrease) {
|
||||
this.train.scale.set(-0.02, 0.02);
|
||||
} else {
|
||||
this.train.scale.set(0.02, 0.02);
|
||||
|
@ -212,16 +212,11 @@ export class ContextMenu extends Container {
|
||||
// this.initTitle();
|
||||
this.groups = [];
|
||||
const options = this.menuOptions;
|
||||
let maxItemWidth = 0;
|
||||
let borderHeight = 0;
|
||||
options.groups.forEach((group) => {
|
||||
const menuGroup = new MenuGroup(this, group);
|
||||
this.groups.push(menuGroup);
|
||||
borderHeight += menuGroup.totalHeight;
|
||||
if (menuGroup.maxWidth > maxItemWidth) {
|
||||
maxItemWidth = menuGroup.maxWidth;
|
||||
}
|
||||
});
|
||||
const { borderHeight, maxItemWidth } = this.calculateBorderInfo();
|
||||
|
||||
const splitLineWidth = 1;
|
||||
|
||||
@ -293,20 +288,40 @@ export class ContextMenu extends Container {
|
||||
}
|
||||
}
|
||||
|
||||
private calculateBorderInfo(): {
|
||||
borderHeight: number;
|
||||
maxItemWidth: number;
|
||||
} {
|
||||
let maxItemNameWidth = 0;
|
||||
let maxShortcutWidth = 0;
|
||||
let maxGutter = 0;
|
||||
let borderHeight = 0;
|
||||
this.groups.forEach((menuGroup) => {
|
||||
borderHeight += menuGroup.totalHeight;
|
||||
const maxInw = menuGroup.maxItemNameWidth;
|
||||
if (maxInw > maxItemNameWidth) {
|
||||
maxItemNameWidth = maxInw;
|
||||
}
|
||||
const maxSw = menuGroup.maxShortcutWidth;
|
||||
if (maxSw > maxShortcutWidth) {
|
||||
maxShortcutWidth = maxSw;
|
||||
}
|
||||
const gutter = menuGroup.totalGutter;
|
||||
if (gutter > maxGutter) {
|
||||
maxGutter = gutter;
|
||||
}
|
||||
});
|
||||
|
||||
const maxItemWidth = maxItemNameWidth + maxShortcutWidth + maxGutter;
|
||||
return { borderHeight, maxItemWidth };
|
||||
}
|
||||
|
||||
updateBg() {
|
||||
this.bg.clear();
|
||||
const options = this.menuOptions;
|
||||
let maxItemWidth = 0;
|
||||
let borderHeight = 0;
|
||||
const { borderHeight, maxItemWidth } = this.calculateBorderInfo();
|
||||
const splitLineWidth = 1;
|
||||
|
||||
this.groups.forEach((menuGroup) => {
|
||||
borderHeight += menuGroup.totalHeight;
|
||||
if (menuGroup.maxWidth > maxItemWidth) {
|
||||
maxItemWidth = menuGroup.maxWidth;
|
||||
}
|
||||
});
|
||||
|
||||
const bgWidth = maxItemWidth + this.padding * 2;
|
||||
const bgHeight =
|
||||
borderHeight +
|
||||
@ -350,9 +365,13 @@ export class ContextMenu extends Container {
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this.menuOptions.groups.length !== this.groups.length) {
|
||||
this.init();
|
||||
} else {
|
||||
this.groups.forEach((group) => group.update());
|
||||
this.updateBg();
|
||||
}
|
||||
}
|
||||
|
||||
public get menuName(): string {
|
||||
return this.menuOptions.name;
|
||||
@ -426,10 +445,14 @@ class MenuGroup extends Container {
|
||||
super();
|
||||
this.config = config;
|
||||
this.menu = menu;
|
||||
config.items.forEach((item) => {
|
||||
this.items.push(new ContextMenuItem(menu, item));
|
||||
});
|
||||
this.init();
|
||||
}
|
||||
|
||||
private init() {
|
||||
this.items = []; // 清空
|
||||
this.config.items.forEach((item) => {
|
||||
this.items.push(new ContextMenuItem(this.menu, item));
|
||||
});
|
||||
this.addChild(...this.items);
|
||||
}
|
||||
|
||||
@ -443,22 +466,24 @@ class MenuGroup extends Container {
|
||||
return false;
|
||||
}
|
||||
|
||||
public get maxWidth(): number {
|
||||
public get maxItemNameWidth(): number {
|
||||
const maxNameWidth = this.items
|
||||
.map((item) => item.nameBounds.width)
|
||||
.sort((a, b) => a - b)
|
||||
.reverse()[0];
|
||||
return maxNameWidth;
|
||||
}
|
||||
|
||||
public get maxShortcutWidth(): number {
|
||||
const maxShortcutWidth = this.items
|
||||
.map((item) => item.shortcutKeyBounds.width)
|
||||
.sort((a, b) => a - b)
|
||||
.reverse()[0];
|
||||
const maxWidth =
|
||||
maxNameWidth +
|
||||
this.gutter +
|
||||
maxShortcutWidth +
|
||||
this.items[0].paddingLeft +
|
||||
this.items[0].paddingRight;
|
||||
return maxWidth;
|
||||
return maxShortcutWidth;
|
||||
}
|
||||
|
||||
public get totalGutter(): number {
|
||||
return this.gutter + this.items[0].paddingLeft + this.items[0].paddingRight;
|
||||
}
|
||||
|
||||
public get totalHeight(): number {
|
||||
@ -468,6 +493,9 @@ class MenuGroup extends Container {
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this.items.length !== this.config.items.length) {
|
||||
this.init();
|
||||
} else {
|
||||
let i = 0;
|
||||
this.items.forEach((item) => {
|
||||
item.update();
|
||||
@ -476,13 +504,7 @@ class MenuGroup extends Container {
|
||||
i++;
|
||||
}
|
||||
});
|
||||
// this.items.forEach()
|
||||
// for (let i = 0; i < this.items.length; i++) {
|
||||
// const item = this.items[i];
|
||||
// if (item.visible) {
|
||||
// item.position.y = i * item.totalHeight;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
updateItemBox(maxItemWidth: number) {
|
||||
@ -557,7 +579,7 @@ class ContextMenuItem extends Container {
|
||||
this.config.handler();
|
||||
this.menu.plugin.app.emit('post-menu-handle', this.config);
|
||||
}
|
||||
if (!this.config.subMenu || this.config.subMenu.length === 0) {
|
||||
if (!this.config.subMenu || this.config.subMenu.groups.length === 0) {
|
||||
this.active = false;
|
||||
this.menu.active = false;
|
||||
this.menu.rootMenu.close();
|
||||
@ -684,20 +706,13 @@ class ContextMenuItem extends Container {
|
||||
}
|
||||
|
||||
initSubMenu() {
|
||||
if (this.config.subMenu && this.config.subMenu.length > 0) {
|
||||
if (this.config.subMenu && this.config.subMenu.groups.length > 0) {
|
||||
this.arrowText = new Text('>', {
|
||||
fontSize: this.fontSize,
|
||||
fill: this.fontColor,
|
||||
});
|
||||
this.addChild(this.arrowText);
|
||||
this.subMenu = new ContextMenu(
|
||||
{
|
||||
name: `${this.config.name}子菜单`,
|
||||
groups: this.config.subMenu,
|
||||
style: this.menu.style,
|
||||
},
|
||||
this
|
||||
);
|
||||
this.subMenu = new ContextMenu(this.config.subMenu, this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -767,6 +782,12 @@ class ContextMenuItem extends Container {
|
||||
this.initShortcutKeyText();
|
||||
}
|
||||
|
||||
if (this.config.subMenu == undefined && this.subMenu) {
|
||||
this.subMenu = undefined;
|
||||
} else if (this.config.subMenu && this.subMenu == undefined) {
|
||||
this.initSubMenu();
|
||||
}
|
||||
|
||||
if (this.subMenu) {
|
||||
this.subMenu.update();
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ export interface MenuItemOptions {
|
||||
/**
|
||||
* 子菜单
|
||||
*/
|
||||
subMenu?: MenuGroupOptions[];
|
||||
subMenu?: MenuOptions;
|
||||
}
|
||||
|
||||
export interface MenuItemStyle {
|
||||
|
@ -83,8 +83,14 @@
|
||||
/>
|
||||
<q-input
|
||||
outlined
|
||||
label="提示信息"
|
||||
v-model="creatForm.infoJson"
|
||||
label="行车方面"
|
||||
v-model="creatForm.drivingInfo"
|
||||
lazy-rules
|
||||
/>
|
||||
<q-input
|
||||
outlined
|
||||
label="信息报送方面"
|
||||
v-model="creatForm.submissionInfo"
|
||||
lazy-rules
|
||||
/>
|
||||
</q-card-section>
|
||||
@ -154,11 +160,21 @@ const columnDefs: QTableColumn[] = [
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'infoJson',
|
||||
label: '提示信息',
|
||||
name: 'drivingInfo',
|
||||
label: '行车方面',
|
||||
field: (row) => {
|
||||
if (row.infoJson) {
|
||||
return JSON.parse(row.infoJson);
|
||||
if (row.drivingInfo) {
|
||||
return JSON.parse(row.drivingInfo);
|
||||
}
|
||||
},
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'submissionInfo',
|
||||
label: '信息报送方面',
|
||||
field: (row) => {
|
||||
if (row.submissionInfo) {
|
||||
return JSON.parse(row.submissionInfo);
|
||||
}
|
||||
},
|
||||
align: 'center',
|
||||
@ -216,7 +232,8 @@ const creatForm = reactive({
|
||||
alertType: '',
|
||||
timeType: '',
|
||||
locationType: '',
|
||||
infoJson: '',
|
||||
drivingInfo: '',
|
||||
submissionInfo: '',
|
||||
});
|
||||
|
||||
function onReset() {
|
||||
@ -224,7 +241,8 @@ function onReset() {
|
||||
creatForm.alertType = '';
|
||||
creatForm.timeType = '';
|
||||
creatForm.locationType = '';
|
||||
creatForm.infoJson = '';
|
||||
creatForm.drivingInfo = '';
|
||||
creatForm.submissionInfo = '';
|
||||
myForm.value?.resetValidation();
|
||||
}
|
||||
|
||||
@ -238,10 +256,14 @@ function onCreate() {
|
||||
alertType: creatForm.alertType,
|
||||
timeType: creatForm.timeType,
|
||||
locationType: creatForm.locationType,
|
||||
infoJson: creatForm.infoJson,
|
||||
drivingInfo: creatForm.drivingInfo,
|
||||
submissionInfo: creatForm.submissionInfo,
|
||||
};
|
||||
if (creatForm.infoJson) {
|
||||
params.infoJson = JSON.stringify(creatForm.infoJson);
|
||||
if (creatForm.drivingInfo) {
|
||||
params.drivingInfo = JSON.stringify(creatForm.drivingInfo);
|
||||
}
|
||||
if (creatForm.submissionInfo) {
|
||||
params.submissionInfo = JSON.stringify(creatForm.submissionInfo);
|
||||
}
|
||||
if (creatForm.id) {
|
||||
await updataAlarmInfo(+creatForm.id, params);
|
||||
@ -269,7 +291,8 @@ function editData(row: any) {
|
||||
creatForm.alertType = row.alertType;
|
||||
creatForm.timeType = row.timeType || '';
|
||||
creatForm.locationType = row.locationType || '';
|
||||
creatForm.infoJson = row.infoJson || '';
|
||||
creatForm.drivingInfo = row.drivingInfo || '';
|
||||
creatForm.submissionInfo = row.submissionInfo || '';
|
||||
createFormShow.value = true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user