打包测试
This commit is contained in:
parent
69cd136393
commit
0619184b9b
50
components/common/CommonGraphics.js
Normal file
50
components/common/CommonGraphics.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { calculateMirrorPoint } from 'jl-graphic';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param polygon
|
||||||
|
* @param x 箭头顶点x坐标
|
||||||
|
* @param y 箭头顶点y坐标
|
||||||
|
* @param length 箭头长度
|
||||||
|
* @param radius 箭头三角半径
|
||||||
|
* @param lineWidth 箭头线宽
|
||||||
|
* @param mirror 是否镜像翻转 (基于箭头顶点)
|
||||||
|
*/
|
||||||
|
function drawArrow(polygon, x, y, length, radius, lineWidth, mirror) {
|
||||||
|
const trianglAcme = { x, y };
|
||||||
|
let triangleP1 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y + Math.cos(Math.PI / 6) * radius,
|
||||||
|
};
|
||||||
|
let triangleP2 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y - Math.cos(Math.PI / 6) * radius,
|
||||||
|
};
|
||||||
|
let lineP1 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y + lineWidth / 2,
|
||||||
|
};
|
||||||
|
let lineP2 = {
|
||||||
|
x: x - length,
|
||||||
|
y: y + lineWidth / 2,
|
||||||
|
};
|
||||||
|
let lineP3 = {
|
||||||
|
x: x - length,
|
||||||
|
y: y - lineWidth / 2,
|
||||||
|
};
|
||||||
|
let lineP4 = {
|
||||||
|
x: x - radius - Math.sin(Math.PI / 6),
|
||||||
|
y: y - lineWidth / 2,
|
||||||
|
};
|
||||||
|
if (mirror) {
|
||||||
|
triangleP1 = calculateMirrorPoint(trianglAcme, triangleP1);
|
||||||
|
triangleP2 = calculateMirrorPoint(trianglAcme, triangleP2);
|
||||||
|
lineP1 = calculateMirrorPoint(trianglAcme, lineP1);
|
||||||
|
lineP2 = calculateMirrorPoint(trianglAcme, lineP2);
|
||||||
|
lineP3 = calculateMirrorPoint(trianglAcme, lineP3);
|
||||||
|
lineP4 = calculateMirrorPoint(trianglAcme, lineP4);
|
||||||
|
}
|
||||||
|
polygon.drawPolygon(trianglAcme.x, trianglAcme.y, triangleP1.x, triangleP1.y, lineP1.x, lineP1.y, lineP2.x, lineP2.y, lineP3.x, lineP3.y, lineP4.x, lineP4.y, triangleP2.x, triangleP2.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { drawArrow };
|
@ -2,7 +2,7 @@ import { GraphicData, GraphicState, JlGraphic, JlGraphicTemplate, VectorText } f
|
|||||||
import { IPointData } from 'pixi.js';
|
import { IPointData } from 'pixi.js';
|
||||||
import { SectionGraphic } from './SectionGraphic';
|
import { SectionGraphic } from './SectionGraphic';
|
||||||
import { DevicePort, IRelatedRef } from 'common/common';
|
import { DevicePort, IRelatedRef } from 'common/common';
|
||||||
import { Turnout } from 'src/packages/Turnout/Turnout';
|
import { JlTurnout } from 'src/packages/Turnout/common/JlTurnout';
|
||||||
export interface ISectionData extends GraphicData {
|
export interface ISectionData extends GraphicData {
|
||||||
code: string;
|
code: string;
|
||||||
isCurve: boolean;
|
isCurve: boolean;
|
||||||
@ -51,7 +51,7 @@ export declare class JlSection extends JlGraphic {
|
|||||||
get linePoints(): IPointData[];
|
get linePoints(): IPointData[];
|
||||||
set linePoints(points: IPointData[]);
|
set linePoints(points: IPointData[]);
|
||||||
getConnectElement(port: DevicePort): {
|
getConnectElement(port: DevicePort): {
|
||||||
g: Turnout | JlSection;
|
g: JlTurnout | JlSection;
|
||||||
port: DevicePort;
|
port: DevicePort;
|
||||||
} | undefined;
|
} | undefined;
|
||||||
/** 获取拆分逻辑区段数据 */
|
/** 获取拆分逻辑区段数据 */
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { JlGraphic, VectorText, convertToBezierParams, Vector2, splitLineEvenly, distance2, GraphicRelationParam, JlGraphicTemplate } from 'jl-graphic';
|
import { JlGraphic, VectorText, convertToBezierParams, Vector2, splitLineEvenly, distance2, GraphicRelationParam, JlGraphicTemplate } from 'jl-graphic';
|
||||||
import { SectionGraphic } from './SectionGraphic.js';
|
import { SectionGraphic } from './SectionGraphic.js';
|
||||||
import { DevicePort, IRelatedRef } from '../../../common/common.js';
|
import { DevicePort, IRelatedRef } from '../../../common/common.js';
|
||||||
import { Turnout } from '../../Turnout/Turnout.js';
|
import { JlTurnout } from '../../Turnout/common/JlTurnout.js';
|
||||||
import { AxleCounting } from '../../AxleCounting/AxleCounting.js';
|
import { AxleCounting } from '../../AxleCounting/AxleCounting.js';
|
||||||
|
|
||||||
const tolerance = 0.01;
|
const tolerance = 0.01;
|
||||||
@ -97,7 +97,7 @@ class JlSection extends JlGraphic {
|
|||||||
.getRelationsOfGraphic(this)
|
.getRelationsOfGraphic(this)
|
||||||
.find((relation) => relation.getRelationParam(this).getParam() === port &&
|
.find((relation) => relation.getRelationParam(this).getParam() === port &&
|
||||||
(relation.getOtherGraphic(this) instanceof JlSection ||
|
(relation.getOtherGraphic(this) instanceof JlSection ||
|
||||||
relation.getOtherGraphic(this) instanceof Turnout));
|
relation.getOtherGraphic(this) instanceof JlTurnout));
|
||||||
if (!relation) {
|
if (!relation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -148,7 +148,9 @@ class JlSection extends JlGraphic {
|
|||||||
buildRelation() {
|
buildRelation() {
|
||||||
this.relationManage.deleteRelationOfGraphicAndOtherType(this, JlSection.Type);
|
this.relationManage.deleteRelationOfGraphicAndOtherType(this, JlSection.Type);
|
||||||
if (this.datas.sectionType === SectionType.Physical) {
|
if (this.datas.sectionType === SectionType.Physical) {
|
||||||
this.queryStore.queryByType(JlSection.Type).forEach((section) => {
|
this.queryStore
|
||||||
|
.queryByType(JlSection.Type)
|
||||||
|
.forEach((section) => {
|
||||||
if (section.id === this.id)
|
if (section.id === this.id)
|
||||||
return;
|
return;
|
||||||
let param = [];
|
let param = [];
|
||||||
@ -175,7 +177,7 @@ class JlSection extends JlGraphic {
|
|||||||
.getRelationsOfGraphic(this)
|
.getRelationsOfGraphic(this)
|
||||||
.find((relation) => relation.getRelationParam(this).param === DevicePort.A &&
|
.find((relation) => relation.getRelationParam(this).param === DevicePort.A &&
|
||||||
(relation.getOtherGraphic(this) instanceof JlSection ||
|
(relation.getOtherGraphic(this) instanceof JlSection ||
|
||||||
relation.getOtherGraphic(this) instanceof Turnout));
|
relation.getOtherGraphic(this) instanceof JlTurnout));
|
||||||
const paDevice = paRelation?.getOtherGraphic(this);
|
const paDevice = paRelation?.getOtherGraphic(this);
|
||||||
if (paDevice) {
|
if (paDevice) {
|
||||||
this.datas.paRef = IRelatedRef.create(paDevice.type, paDevice.id, paRelation.getOtherRelationParam(this).getParam());
|
this.datas.paRef = IRelatedRef.create(paDevice.type, paDevice.id, paRelation.getOtherRelationParam(this).getParam());
|
||||||
@ -187,7 +189,7 @@ class JlSection extends JlGraphic {
|
|||||||
.getRelationsOfGraphic(this)
|
.getRelationsOfGraphic(this)
|
||||||
.find((relation) => relation.getRelationParam(this).param === DevicePort.B &&
|
.find((relation) => relation.getRelationParam(this).param === DevicePort.B &&
|
||||||
(relation.getOtherGraphic(this) instanceof JlSection ||
|
(relation.getOtherGraphic(this) instanceof JlSection ||
|
||||||
relation.getOtherGraphic(this) instanceof Turnout));
|
relation.getOtherGraphic(this) instanceof JlTurnout));
|
||||||
const pbDevice = pbRelation?.getOtherGraphic(this);
|
const pbDevice = pbRelation?.getOtherGraphic(this);
|
||||||
if (pbDevice) {
|
if (pbDevice) {
|
||||||
this.datas.pbRef = IRelatedRef.create(pbDevice.type, pbDevice.id, pbRelation?.getOtherRelationParam(this).param);
|
this.datas.pbRef = IRelatedRef.create(pbDevice.type, pbDevice.id, pbRelation?.getOtherRelationParam(this).param);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ContextMenu, GraphicDrawAssistant, KeyListener, calculateMirrorPoint, convertToBezierParams, pointPolygon, linePoint, GraphicInteractionPlugin, getWaypointRangeIndex, addWayPoint, clearWayPoint, PolylineEditPlugin, VectorText, AppConsts, BezierCurveEditPlugin, AbsorbableLine, AbsorbablePoint } from 'jl-graphic';
|
import { ContextMenu, GraphicDrawAssistant, KeyListener, calculateMirrorPoint, convertToBezierParams, pointPolygon, linePoint, GraphicInteractionPlugin, getWaypointRangeIndex, addWayPoint, clearWayPoint, PolylineEditPlugin, VectorText, AppConsts, BezierCurveEditPlugin, AbsorbableLine, AbsorbablePoint } from 'jl-graphic';
|
||||||
import { SectionType, defaultDisplayConfig } from './Section.js';
|
import { SectionType, defaultDisplayConfig } from './Section.js';
|
||||||
import { Graphics, Point } from 'pixi.js';
|
import { Graphics, Point } from 'pixi.js';
|
||||||
import { Turnout } from '../../Turnout/Turnout.js';
|
import { JlTurnout } from '../../Turnout/common/JlTurnout.js';
|
||||||
import { Section } from '../GPSection.js';
|
import { Section } from '../GPSection.js';
|
||||||
|
|
||||||
class SectionDraw extends GraphicDrawAssistant {
|
class SectionDraw extends GraphicDrawAssistant {
|
||||||
@ -184,7 +184,7 @@ function buildAbsorbablePositions(section) {
|
|||||||
const ye = new AbsorbableLine({ x: pe.x, y: 0 }, { x: pe.x, y: height });
|
const ye = new AbsorbableLine({ x: pe.x, y: 0 }, { x: pe.x, y: height });
|
||||||
aps.push(xs, ys, xe, ye);
|
aps.push(xs, ys, xe, ye);
|
||||||
});
|
});
|
||||||
const turnouts = section.queryStore.queryByType(Turnout.Type);
|
const turnouts = section.queryStore.queryByType(JlTurnout.Type);
|
||||||
turnouts.forEach((turnout) => {
|
turnouts.forEach((turnout) => {
|
||||||
turnout.getPortPoints().forEach((points) => {
|
turnout.getPortPoints().forEach((points) => {
|
||||||
turnout.localToCanvasPoints(...points).forEach((p) => {
|
turnout.localToCanvasPoints(...points).forEach((p) => {
|
||||||
|
5
components/packages/Turnout/GPTurnout.d.ts
vendored
Normal file
5
components/packages/Turnout/GPTurnout.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { JlTurnout } from './common/JlTurnout';
|
||||||
|
export declare class Turnout extends JlTurnout {
|
||||||
|
static Type: string;
|
||||||
|
doRepaint(): void;
|
||||||
|
}
|
10
components/packages/Turnout/GPTurnout.js
Normal file
10
components/packages/Turnout/GPTurnout.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { JlTurnout } from './common/JlTurnout.js';
|
||||||
|
|
||||||
|
class Turnout extends JlTurnout {
|
||||||
|
static Type = 'Turnout';
|
||||||
|
doRepaint() {
|
||||||
|
super.draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Turnout };
|
34
components/packages/Turnout/common/JlTurnout.d.ts
vendored
Normal file
34
components/packages/Turnout/common/JlTurnout.d.ts
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { JlGraphic, VectorText } from 'jl-graphic';
|
||||||
|
import { IPointData, Graphics } from 'pixi.js';
|
||||||
|
import { DevicePort } from 'common/common';
|
||||||
|
import { ITurnoutData, TurnoutConstsConfig } from './TurnoutConfig';
|
||||||
|
export declare function getForkPoint(r: number, p: IPointData): IPointData;
|
||||||
|
export declare class TurnoutSection extends Graphics {
|
||||||
|
turnoutConsts: TurnoutConstsConfig;
|
||||||
|
turnout: JlTurnout;
|
||||||
|
port: DevicePort;
|
||||||
|
stateFillColor?: string;
|
||||||
|
constructor(turnout: JlTurnout, port: DevicePort, turnoutConsts: TurnoutConstsConfig);
|
||||||
|
paint(): void;
|
||||||
|
}
|
||||||
|
declare class ForkGraphic extends Graphics {
|
||||||
|
turnoutConsts: TurnoutConstsConfig;
|
||||||
|
turnout: JlTurnout;
|
||||||
|
stateFillColor?: string;
|
||||||
|
constructor(turnout: JlTurnout, turnoutConsts: TurnoutConstsConfig);
|
||||||
|
paint(p: IPointData): void;
|
||||||
|
}
|
||||||
|
export declare abstract class JlTurnout extends JlGraphic {
|
||||||
|
static Type: string;
|
||||||
|
graphics: {
|
||||||
|
fork: ForkGraphic;
|
||||||
|
sections: [TurnoutSection, TurnoutSection, TurnoutSection];
|
||||||
|
label: VectorText;
|
||||||
|
};
|
||||||
|
constructor(turnoutConsts: TurnoutConstsConfig);
|
||||||
|
draw(): void;
|
||||||
|
get datas(): ITurnoutData;
|
||||||
|
getPortPoints(): IPointData[][];
|
||||||
|
getGraphicOfPort(port: DevicePort): JlGraphic[];
|
||||||
|
}
|
||||||
|
export {};
|
113
components/packages/Turnout/common/JlTurnout.js
Normal file
113
components/packages/Turnout/common/JlTurnout.js
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { JlGraphic, VectorText } from 'jl-graphic';
|
||||||
|
import { Graphics } from 'pixi.js';
|
||||||
|
import { DevicePort } from '../../../common/common.js';
|
||||||
|
|
||||||
|
function getForkPoint(r, p) {
|
||||||
|
if (r === 0)
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
const len = Math.sqrt((-p.x) ** 2 + (-p.y) ** 2);
|
||||||
|
const scale = r / len;
|
||||||
|
return { x: scale * p.x, y: scale * p.y };
|
||||||
|
}
|
||||||
|
class TurnoutSection extends Graphics {
|
||||||
|
turnoutConsts;
|
||||||
|
turnout;
|
||||||
|
port;
|
||||||
|
stateFillColor;
|
||||||
|
constructor(turnout, port, turnoutConsts) {
|
||||||
|
super();
|
||||||
|
this.turnoutConsts = turnoutConsts;
|
||||||
|
this.turnout = turnout;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
paint() {
|
||||||
|
let pList = [];
|
||||||
|
switch (this.port) {
|
||||||
|
case DevicePort.A:
|
||||||
|
pList = this.turnout.datas.pointA;
|
||||||
|
break;
|
||||||
|
case DevicePort.B:
|
||||||
|
pList = this.turnout.datas.pointB;
|
||||||
|
break;
|
||||||
|
case DevicePort.C:
|
||||||
|
pList = this.turnout.datas.pointC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const gap = this.port === DevicePort.A ? 0 : this.turnoutConsts.forkLenth;
|
||||||
|
const fillColor = this.stateFillColor || this.turnoutConsts.lineColor;
|
||||||
|
const start = getForkPoint(gap, pList[0]);
|
||||||
|
this.clear()
|
||||||
|
.lineStyle(this.turnoutConsts.lineWidth, fillColor)
|
||||||
|
.moveTo(start.x, start.y);
|
||||||
|
pList.forEach((p) => {
|
||||||
|
const { x, y } = p;
|
||||||
|
this.lineTo(x, y);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class ForkGraphic extends Graphics {
|
||||||
|
turnoutConsts;
|
||||||
|
turnout;
|
||||||
|
stateFillColor;
|
||||||
|
constructor(turnout, turnoutConsts) {
|
||||||
|
super();
|
||||||
|
this.turnoutConsts = turnoutConsts;
|
||||||
|
this.turnout = turnout;
|
||||||
|
}
|
||||||
|
paint(p) {
|
||||||
|
const fillColor = this.stateFillColor || this.turnoutConsts.lineColor;
|
||||||
|
const target = getForkPoint(this.turnoutConsts.forkLenth, p);
|
||||||
|
this.clear()
|
||||||
|
.lineStyle(this.turnoutConsts.lineWidth, fillColor)
|
||||||
|
.moveTo(0, 0)
|
||||||
|
.lineTo(target.x, target.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class JlTurnout extends JlGraphic {
|
||||||
|
static Type = 'Turnout';
|
||||||
|
graphics;
|
||||||
|
constructor(turnoutConsts) {
|
||||||
|
super(JlTurnout.Type);
|
||||||
|
this.name = 'turnout';
|
||||||
|
this.graphics = {
|
||||||
|
fork: new ForkGraphic(this, turnoutConsts),
|
||||||
|
sections: [
|
||||||
|
new TurnoutSection(this, DevicePort.A, turnoutConsts),
|
||||||
|
new TurnoutSection(this, DevicePort.B, turnoutConsts),
|
||||||
|
new TurnoutSection(this, DevicePort.C, turnoutConsts),
|
||||||
|
],
|
||||||
|
label: new VectorText(),
|
||||||
|
};
|
||||||
|
this.addChild(this.graphics.fork);
|
||||||
|
this.addChild(this.graphics.sections[0]);
|
||||||
|
this.addChild(this.graphics.sections[1]);
|
||||||
|
this.addChild(this.graphics.sections[2]);
|
||||||
|
this.graphics.label.anchor.set(0.5);
|
||||||
|
this.graphics.label.style.fill = '#0f0';
|
||||||
|
this.graphics.label.setVectorFontSize(turnoutConsts.labelFontSize);
|
||||||
|
this.graphics.label.position.set(20, 20);
|
||||||
|
this.graphics.label.transformSave = true;
|
||||||
|
this.graphics.label.name = 'label';
|
||||||
|
this.addChild(this.graphics.label);
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
this.graphics.label.text = this.datas.code;
|
||||||
|
this.graphics.sections.forEach((sectionGraphic) => sectionGraphic.paint());
|
||||||
|
}
|
||||||
|
get datas() {
|
||||||
|
return this.getDatas();
|
||||||
|
}
|
||||||
|
getPortPoints() {
|
||||||
|
return [this.datas.pointA, this.datas.pointB, this.datas.pointC];
|
||||||
|
}
|
||||||
|
getGraphicOfPort(port) {
|
||||||
|
return this.relationManage
|
||||||
|
.getRelationsOfGraphic(this)
|
||||||
|
.filter((relation) => relation.getRelationParam(this).getParam() === port)
|
||||||
|
.map((relation) => {
|
||||||
|
return relation.getOtherGraphic(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { JlTurnout, TurnoutSection, getForkPoint };
|
44
components/packages/Turnout/common/TurnoutConfig.d.ts
vendored
Normal file
44
components/packages/Turnout/common/TurnoutConfig.d.ts
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { IRelatedRef, KilometerSystem } from 'common/common';
|
||||||
|
import { GraphicData } from 'jl-graphic';
|
||||||
|
import { IPointData } from 'pixi.js';
|
||||||
|
export interface TurnoutConstsConfig {
|
||||||
|
lineColor: string;
|
||||||
|
occupiedColor: string;
|
||||||
|
lineWidth: number;
|
||||||
|
forkLenth: number;
|
||||||
|
labelFontSize: number;
|
||||||
|
normalLabelColor: string;
|
||||||
|
reverseLabelColor: string;
|
||||||
|
}
|
||||||
|
export declare const GPConsts: {
|
||||||
|
lineColor: string;
|
||||||
|
occupiedColor: string;
|
||||||
|
lineWidth: number;
|
||||||
|
forkLenth: number;
|
||||||
|
labelFontSize: number;
|
||||||
|
normalLabelColor: string;
|
||||||
|
reverseLabelColor: string;
|
||||||
|
};
|
||||||
|
export declare enum SwitchMachineType {
|
||||||
|
Unknown = 0,
|
||||||
|
ZDJ9_Single = 1,
|
||||||
|
ZDJ9_Double = 2
|
||||||
|
}
|
||||||
|
export interface ITurnoutData extends GraphicData {
|
||||||
|
code: string;
|
||||||
|
pointA: IPointData[];
|
||||||
|
pointB: IPointData[];
|
||||||
|
pointC: IPointData[];
|
||||||
|
paRef?: IRelatedRef;
|
||||||
|
pbRef?: IRelatedRef;
|
||||||
|
pcRef?: IRelatedRef;
|
||||||
|
kilometerSystem: KilometerSystem;
|
||||||
|
paTrackSectionId?: number;
|
||||||
|
pbTrackSectionId?: number;
|
||||||
|
pcTrackSectionId?: number;
|
||||||
|
switchMachineType?: SwitchMachineType;
|
||||||
|
centralizedStations?: number[];
|
||||||
|
clone(): ITurnoutData;
|
||||||
|
copyFrom(data: ITurnoutData): void;
|
||||||
|
eq(other: ITurnoutData): boolean;
|
||||||
|
}
|
17
components/packages/Turnout/common/TurnoutConfig.js
Normal file
17
components/packages/Turnout/common/TurnoutConfig.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const GPConsts = {
|
||||||
|
lineColor: '#5578b6',
|
||||||
|
occupiedColor: '#f00',
|
||||||
|
lineWidth: 5,
|
||||||
|
forkLenth: 20,
|
||||||
|
labelFontSize: 12,
|
||||||
|
normalLabelColor: '#0f0',
|
||||||
|
reverseLabelColor: '#ff0',
|
||||||
|
};
|
||||||
|
var SwitchMachineType;
|
||||||
|
(function (SwitchMachineType) {
|
||||||
|
SwitchMachineType[SwitchMachineType["Unknown"] = 0] = "Unknown";
|
||||||
|
SwitchMachineType[SwitchMachineType["ZDJ9_Single"] = 1] = "ZDJ9_Single";
|
||||||
|
SwitchMachineType[SwitchMachineType["ZDJ9_Double"] = 2] = "ZDJ9_Double";
|
||||||
|
})(SwitchMachineType || (SwitchMachineType = {}));
|
||||||
|
|
||||||
|
export { GPConsts, SwitchMachineType };
|
Loading…
Reference in New Issue
Block a user